# Copyright (c) 2020  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.

from __future__ import absolute_import

import math
import numpy as np
from paddle2onnx.constant import dtypes
from paddle2onnx.op_mapper import OpMapper as op_mapper
from paddle2onnx.utils import require_fixed_shape


@op_mapper('density_prior_box')
class DensityPriorBox():
    """
    In this function, use the attribute to get the prior box, because we do not use
    the image data and feature map, wo could the python code to create the varaible,
    and to create the onnx tensor as output.
    """
    support_opset_verison_range = (1, 12)

    @classmethod
    def opset_9(cls, graph, node, **kw):
        clip = bool(node.attr('clip'))
        densities = node.attr('densities')
        fixed_ratios = node.attr('fixed_ratios')
        fixed_sizes = node.attr('fixed_sizes')
        flatten_to_2d = bool(node.attr('flatten_to_2d'))
        offset = node.attr('offset')
        step_h = node.attr('step_h')
        step_w = node.attr('step_w')
        variances = node.attr('variances')

        input_shape = node.input_shape('Input', 0)
        image_shape = node.input_shape('Image', 0)

        img_width = image_shape[3]
        img_height = image_shape[2]
        feature_width = input_shape[3]
        feature_height = input_shape[2]

        assert img_width > 0 and img_height > 0, require_fixed_shape(
            cls.__name__)

        if step_w == 0.0 or step_h == 0.0:
            step_w = float(img_width / feature_width)
            step_h = float(img_height / feature_height)

        num_priors = 0
        if len(fixed_sizes) > 0 and len(densities) > 0:
            for density in densities:
                if len(fixed_ratios) > 0:
                    num_priors += len(fixed_ratios) * (pow(density, 2))

        out_dim = (feature_height, feature_width, num_priors, 4)
        out_boxes = np.zeros(out_dim).astype('float32')
        out_var = np.zeros(out_dim).astype('float32')
        step_average = int((step_w + step_h) * 0.5)

        for h in range(feature_height):
            for w in range(feature_width):
                c_x = (w + offset) * step_w
                c_y = (h + offset) * step_h
                idx = 0

                for density, fixed_size in zip(densities, fixed_sizes):
                    if (len(fixed_ratios) > 0):
                        for ar in fixed_ratios:
                            shift = int(step_average / density)
                            box_width_ratio = fixed_size * math.sqrt(ar)
                            box_height_ratio = fixed_size / math.sqrt(ar)
                            for di in range(density):
                                for dj in range(density):
                                    c_x_temp = c_x - step_average / 2.0 + shift / 2.0 + dj * shift
                                    c_y_temp = c_y - step_average / 2.0 + shift / 2.0 + di * shift
                                    out_boxes[h, w, idx, :] = [
                                        max((c_x_temp - box_width_ratio / 2.0) /
                                            img_width, 0),
                                        max((c_y_temp - box_height_ratio / 2.0)
                                            / img_height, 0),
                                        min((c_x_temp + box_width_ratio / 2.0) /
                                            img_width, 1),
                                        min((c_y_temp + box_height_ratio / 2.0)
                                            / img_height, 1)
                                    ]
                                    idx += 1

        if clip:
            out_boxes = np.clip(out_boxes, 0.0, 1.0)
        # set the variance.
        out_var = np.tile(variances,
                          (feature_height, feature_width, num_priors, 1))

        if flatten_to_2d:
            out_boxes = out_boxes.reshape((-1, 4))
            out_var = out_var.reshape((-1, 4))

        #make node that

        node_boxes = graph.make_node(
            'Constant',
            inputs=[],
            outputs=node.output('Boxes'),
            dtype=dtypes.ONNX.FLOAT,
            dims=out_boxes.shape,
            value=out_boxes.flatten().tolist())

        node_vars = graph.make_node(
            'Constant',
            inputs=[],
            outputs=node.output('Variances'),
            dtype=dtypes.ONNX.FLOAT,
            dims=out_var.shape,
            value=out_var.flatten().tolist())
