""" a custom layer for 'crop', maybe we should implement this in standard way.
    more info can be found here: http://caffe.berkeleyvision.org/tutorial/layers/reduction.html
"""
from .register import register


def reduction_shape(input_shape, axis=0):
    """ calculate the output shape of this layer using input shape

    Args:
        @input_shape (list of num): a list of number which represents the input shape
        @axis (int): parameter from caffe's reduction layer

    Returns:
        @output_shape (list of num): a list of numbers represent the output shape
    """
    if axis < 0:
        axis += len(input_shape) + 1

    assert axis <= len(input_shape), 'invalid axis[%d] error' % (axis)
    
    return input_shape[0:axis]


def reduction_layer(input, name, axis=0, operation=1, coeff=1.0):
    """ build a layer of type 'Crop' using fluid

    Args:
        @input (variable): input fluid variable for this layer
        @name (str): name for this layer
        @axis (int): parameter from caffe's reduction layer
        @operation (int): parameter from caffe's reduction layer
        @coeff (float): parameter from caffe's reduction layer

    Returns:
        output (variable): output variable for this layer
    """
    assert operation >= 1 and operation <= 4, "reduction reduction [%s] error" % (operation)
    
    input_len = len(input.shape)
    if axis < 0:
        axis += input_len + 1
    
    dim = range(input_len)

    import paddle.fluid as fluid
    if operation == 1:   ## operation = SUM
        output = fluid.layers.reduce_sum(input, dim=dim[axis:], keep_dim=False, name=name)
    elif operation == 2: ## operation = ASUM
        absout = fluid.layers.abs(input)
        output = fluid.layers.reduce_sum(absout, dim=dim[axis:], keep_dim=False, name=name)
    elif operation == 3: ## operation = SUMSQ
        powout = fluid.layers.pow(x=input, factor=2.0)
        output = fluid.layers.reduce_sum(powout, dim=dim[axis:], keep_dim=False, name=name)
    else:                ## operation = MEAN
        output = fluid.layers.reduce_mean(input, dim=dim[axis:], keep_dim=False, name=name)

    mulout = fluid.layers.scale(x=output, scale=coeff)
    return mulout

register(kind='Reduction', shape=reduction_shape, layer=reduction_layer)
