# -*- coding: utf-8 -*-
# MegEngine is Licensed under the Apache License, Version 2.0 (the "License")
#
# Copyright (c) 2014-2020 Megvii Inc. All rights reserved.
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import collections.abc
import functools
import random

import cv2
import numpy as np


def wrap_keepdims(func):
    """Wraper to keep the dimension of input images unchanged."""

    @functools.wraps(func)
    def wrapper(image, *args, **kwargs):
        if len(image.shape) != 3:
            raise ValueError(
                "image must have 3 dims, but got {} dims".format(len(image.shape))
            )
        ret = func(image, *args, **kwargs)
        if len(ret.shape) == 2:
            ret = ret[:, :, np.newaxis]
        return ret

    return wrapper


@wrap_keepdims
def to_gray(image):
    r"""
    Change BGR format image's color space to gray.

    :param image: input BGR format image, with `(H, W, C)` shape.
    :return: gray format image, with `(H, W, C)` shape.
    """
    return cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)


@wrap_keepdims
def to_bgr(image):
    r"""
    Change gray format image's color space to BGR.

    :param image: input Gray format image, with `(H, W, C)` shape.
    :return: BGR format image, with `(H, W, C)` shape.
    """
    return cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)


@wrap_keepdims
def pad(input, size, value):
    r"""
    Pad input data with *value* and given *size*.

    :param input: input data, with `(H, W, C)` shape.
    :param size: padding size of input data, it could be integer or sequence.
        If it is an integer, the input data will be padded in four directions.
        If it is a sequence contains two integer, the bottom and right side
        of input data will be padded.
        If it is a sequence contains four integer, the top, bottom, left, right
        side of input data will be padded with given size.
    :param value: padding value of data, could be a sequence of int or float.
        If it is float value, the dtype of image will be casted to float32 also.
    :return: padded image.
    """
    if isinstance(size, int):
        size = (size, size, size, size)
    elif isinstance(size, collections.abc.Sequence) and len(size) == 2:
        size = (0, size[0], 0, size[1])
    if np.array(value).dtype == float:
        input = input.astype(np.float32)
    return cv2.copyMakeBorder(input, *size, cv2.BORDER_CONSTANT, value=value)


@wrap_keepdims
def flip(image, flipCode):
    r"""
    Accordding to the flipCode (the type of flip), flip the input image.

    :param image: input image, with `(H, W, C)` shape.
    :param flipCode: code that indicates the type of flip.

        * 1 : Flip horizontally

        * 0 : Flip vertically

        * -1: Flip horizontally and vertically

    :return: BGR format image, with `(H, W, C)` shape.
    """
    return cv2.flip(image, flipCode=flipCode)


@wrap_keepdims
def resize(input, size, interpolation=cv2.INTER_LINEAR):
    r"""
    Resize the input data to given size.

    :param input: input data, could be image or masks, with `(H, W, C)` shape.
    :param size: target size of input data, with (height, width) shape.
    :param interpolation: interpolation method.
    :return: resized data, with `(H, W, C)` shape.
    """
    if len(size) != 2:
        raise ValueError("resize needs (h, w), but got {}".format(size))

    if isinstance(interpolation, collections.abc.Sequence):
        interpolation = random.choice(interpolation)
    return cv2.resize(input, size[::-1], interpolation=interpolation)
