/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */

#pragma OPENCL EXTENSION cl_khr_fp16 : enable

__kernel void box_decoder(__read_only image2d_t prior_box_image,
                      __read_only image2d_t prior_box_var_image,
                      __read_only image2d_t target_box_image,
                      __write_only image2d_t output_image,
                      __private const int out_C,
                      __private const int out_H
                      ){
                       const int out_c = get_global_id(0);
                       const int out_nh = get_global_id(1);
                       const int out_h = out_nh%out_H;
                       const int out_n =  1;

                       const int prior_box_n = 1;
                       const int prior_box_c = 0;
                       const int prior_box_h = out_h;


                       const int prior_box_var_n = 1;
                       const int prior_box_var_c = 0;
                       const int prior_box_var_h = out_h;

                       const int target_box_n = 1;
                       const int target_box_c = out_c;
                       const int target_box_h = out_h;

                       const sampler_t sampler = CLK_NORMALIZED_COORDS_TRUE |
                                                 CLK_ADDRESS_CLAMP      |
                                                 CLK_FILTER_NEAREST;
                       int2  prior_box_pos;
                       int2  prior_box_var_pos;
                       int2  target_box_pos;
                       int2  output_pos;

                       prior_box_pos.x = prior_box_c * 4;
                       prior_box_pos.y = prior_box_n * prior_box_h;

                       prior_box_var_pos.x = prior_box_var_c * 4;
                       prior_box_var_pos.y = prior_box_var_n * prior_box_var_h;

                       target_box_pos.x = target_box_c * 4;
                       target_box_pos.y = target_box_n * target_box_h;

                       output_pos.x = out_c * 4;
                       output_pos.y = out_n * out_h;

                       half4 prior_box_input[4];
                       half4 prior_box_var_input[4];
                       half4 target_box_input[4];

                       prior_box_input[0] = read_imageh(prior_box_image, sampler,(int2)(prior_box_pos.x + 0,prior_box_pos.y));
                       prior_box_input[1] = read_imageh(prior_box_image, sampler,(int2)(prior_box_pos.x + 1,prior_box_pos.y));
                       prior_box_input[2] = read_imageh(prior_box_image, sampler,(int2)(prior_box_pos.x + 2,prior_box_pos.y));
                       prior_box_input[3] = read_imageh(prior_box_image, sampler,(int2)(prior_box_pos.x + 3,prior_box_pos.y));

                       prior_box_var_input[0] = read_imageh(prior_box_var_image, sampler,(int2)(prior_box_var_pos.x + 0,prior_box_var_pos.y));
                       prior_box_var_input[1] = read_imageh(prior_box_var_image, sampler,(int2)(prior_box_var_pos.x + 1,prior_box_var_pos.y));
                       prior_box_var_input[2] = read_imageh(prior_box_var_image, sampler,(int2)(prior_box_var_pos.x + 2,prior_box_var_pos.y));
                       prior_box_var_input[3] = read_imageh(prior_box_var_image, sampler,(int2)(prior_box_var_pos.x + 3,prior_box_var_pos.y));



                       target_box_input[0] = read_imageh(target_box_image, sampler,(int2)(target_box_pos.x + 0,target_box_pos.y));
                       target_box_input[1] = read_imageh(target_box_image, sampler,(int2)(target_box_pos.x + 1,target_box_pos.y));
                       target_box_input[2] = read_imageh(target_box_image, sampler,(int2)(target_box_pos.x + 2,target_box_pos.y));
                       target_box_input[3] = read_imageh(target_box_image, sampler,(int2)(target_box_pos.x + 3,target_box_pos.y));

                       half prior_box_width = prior_box_input[2].x - prior_box_input[0].x;
                       half prior_box_height = prior_box_input[3].x - prior_box_input[1].x;
                       half prior_box_center_x = (prior_box_input[2].x + prior_box_input[0].x)/(half)2;
                       half prior_box_center_y = (prior_box_input[3].x + prior_box_input[1].x)/(half)2;

                       half4 target_box_center_x;
                       half4 target_box_center_y;
                       half4 target_box_width;
                       half4 target_box_height;
                       half4 output[4];

                       output[0] = 0.0f;
                       output[1] = 0.0f;
                       output[2] = 0.0f;
                       output[3] = 0.0f;

                       target_box_center_x.x = prior_box_var_input[0].x * target_box_input[0].x * prior_box_width + prior_box_center_x;
                       target_box_center_y.x = prior_box_var_input[1].x * target_box_input[1].x * prior_box_height + prior_box_center_y;
                       target_box_width.x = exp(prior_box_var_input[2].x * target_box_input[2].x) * prior_box_width;
                       target_box_height.x = exp(prior_box_var_input[3].x * target_box_input[3].x) * prior_box_height;

                       output[0].x = target_box_center_x.x - target_box_width.x/(half)2;
                       output[1].x = target_box_center_y.x - target_box_height.x/(half)2;
                       output[2].x = target_box_center_x.x + target_box_width.x/(half)2;
                       output[3].x = target_box_center_y.x + target_box_height.x/(half)2;

                       if(out_C - out_c * 4 >= 2){
                       target_box_center_x.y = prior_box_var_input[0].x * target_box_input[0].y * prior_box_width + prior_box_center_x;
                       target_box_center_y.y = prior_box_var_input[1].x * target_box_input[1].y * prior_box_height + prior_box_center_y;
                       target_box_width.y = exp(prior_box_var_input[2].x * target_box_input[2].y) * prior_box_width;
                       target_box_height.y = exp(prior_box_var_input[3].x * target_box_input[3].y) * prior_box_height;
                       output[0].y = target_box_center_x.y - target_box_width.y/(half)2;
                       output[1].y = target_box_center_y.y - target_box_height.y/(half)2;
                       output[2].y = target_box_center_x.y + target_box_width.y/(half)2;
                       output[3].y = target_box_center_y.y + target_box_height.y/(half)2;

                       }
                       if(out_C - out_c * 4 >= 3){
                       target_box_center_x.z = prior_box_var_input[0].x * target_box_input[0].z * prior_box_width + prior_box_center_x;
                       target_box_center_y.z = prior_box_var_input[1].x * target_box_input[1].z * prior_box_height + prior_box_center_y;
                       target_box_width.z = exp(prior_box_var_input[2].x * target_box_input[2].z) * prior_box_width;
                       target_box_height.z = exp(prior_box_var_input[3].x * target_box_input[3].z) * prior_box_height;
                       output[0].z = target_box_center_x.z - target_box_width.z/(half)2;
                       output[1].z = target_box_center_y.z - target_box_height.z/(half)2;
                       output[2].z = target_box_center_x.z + target_box_width.z/(half)2;
                       output[3].z = target_box_center_y.z + target_box_height.z/(half)2;
                       }
                       if(out_C - out_c * 4 >= 4){
                       target_box_center_x.w = prior_box_var_input[0].x * target_box_input[0].w * prior_box_width + prior_box_center_x;
                       target_box_center_y.w = prior_box_var_input[1].x * target_box_input[1].w * prior_box_height + prior_box_center_y;
                       target_box_width.w = exp(prior_box_var_input[2].x * target_box_input[2].w) * prior_box_width;
                       target_box_height.w = exp(prior_box_var_input[3].x * target_box_input[3].w) * prior_box_height;
                       output[0].w = target_box_center_x.w - target_box_width.w/(half)2;
                       output[1].w = target_box_center_y.w - target_box_height.w/(half)2;
                       output[2].w = target_box_center_x.w + target_box_width.w/(half)2;
                       output[3].w = target_box_center_y.w + target_box_height.w/(half)2;
                       }


                       write_imageh(output_image, (int2)(output_pos.x + 0, output_pos.y), output[0]);
                       write_imageh(output_image, (int2)(output_pos.x + 1, output_pos.y), output[1]);
                       write_imageh(output_image, (int2)(output_pos.x + 2, output_pos.y), output[2]);
                       write_imageh(output_image, (int2)(output_pos.x + 3, output_pos.y), output[3]);

}