import numpy as np
from .boxes_utils import assert_and_normalize_shape


def flip_boxes(boxes, x_center=0, y_center=0, direction='h'):
    """
    Args:
        boxes: (N, 4+K)
        x_center: array-like whose shape is (), (1,), (N,), (1, 1) or (N, 1)
        y_center: array-like whose shape is (), (1,), (N,), (1, 1) or (N, 1)
        direction: str
    """
    assert direction in ['x', 'h', 'horizontal',
                         'y', 'v', 'vertical', 
                         'o', 'b', 'both']
    boxes = np.asarray(boxes, np.float32)
    ret_boxes = boxes.copy()
    
    x_center = np.asarray(x_center, np.float32)
    y_center = np.asarray(y_center, np.float32)
    x_center = assert_and_normalize_shape(x_center, boxes.shape[0])
    y_center = assert_and_normalize_shape(y_center, boxes.shape[0])
    
    if direction in ['o', 'b', 'both', 'x', 'h', 'horizontal']:
        ret_boxes[:, 0] = 2 * x_center - boxes[:, 2] 
        ret_boxes[:, 2] = 2 * x_center - boxes[:, 0]
    if direction in ['o', 'b', 'both', 'y', 'v', 'vertical']:
        ret_boxes[:, 1] = 2 * y_center - boxes[:, 3]
        ret_boxes[:, 3] = 2 * y_center - boxes[:, 1]
    return ret_boxes
    
    
def fliplr_boxes(boxes, x_center=0, y_center=0):
    """
    Args:
        boxes: (N, 4+K)
        x_center: array-like whose shape is (), (1,), (N,), (1, 1) or (N, 1)
        y_center: array-like whose shape is (), (1,), (N,), (1, 1) or (N, 1)
    """
    boxes = np.asarray(boxes, np.float32)
    ret_boxes = boxes.copy()
    
    x_center = np.asarray(x_center, np.float32)
    y_center = np.asarray(y_center, np.float32)
    x_center = assert_and_normalize_shape(x_center, boxes.shape[0])
    y_center = assert_and_normalize_shape(y_center, boxes.shape[0])
     
    ret_boxes[:, 0] = 2 * x_center - boxes[:, 2] 
    ret_boxes[:, 2] = 2 * x_center - boxes[:, 0]
    return ret_boxes
    
    
def flipud_boxes(boxes, x_center=0, y_center=0):
    """
    Args:
        boxes: (N, 4+K)
        x_center: array-like whose shape is (), (1,), (N,), (1, 1) or (N, 1)
        y_center: array-like whose shape is (), (1,), (N,), (1, 1) or (N, 1)
    """
    boxes = np.asarray(boxes, np.float32)
    ret_boxes = boxes.copy()
    
    x_center = np.asarray(x_center, np.float32)
    y_center = np.asarray(y_center, np.float32)
    x_center = assert_and_normalize_shape(x_center, boxes.shape[0])
    y_center = assert_and_normalize_shape(y_center, boxes.shape[0])
    
    ret_boxes[:, 1] = 2 * y_center - boxes[:, 3]
    ret_boxes[:, 3] = 2 * y_center - boxes[:, 1]
    return ret_boxes
    
    
def transpose_boxes(boxes, x_center=0, y_center=0):
    """
    Args:
        boxes: (N, 4+K)
        x_center: array-like whose shape is (), (1,), (N,), (1, 1) or (N, 1)
        y_center: array-like whose shape is (), (1,), (N,), (1, 1) or (N, 1)
    """
    boxes = np.asarray(boxes, np.float32)
    ret_boxes = boxes.copy()
    
    x_center = np.asarray(x_center, np.float32)
    y_center = np.asarray(y_center, np.float32)
    x_center = assert_and_normalize_shape(x_center, boxes.shape[0])
    y_center = assert_and_normalize_shape(y_center, boxes.shape[0])
    
    shift = x_center - y_center
    ret_boxes[:, 0] = boxes[:, 1] + shift
    ret_boxes[:, 1] = boxes[:, 0] - shift
    ret_boxes[:, 2] = boxes[:, 3] + shift
    ret_boxes[:, 3] = boxes[:, 2] - shift
    return ret_boxes


def flip_boxes_in_image(boxes, image_width, image_height, direction='h'):
    """
    Args:
        boxes: (N, 4+K)
        image_width: int
        image_width: int
        direction: str
        
    References:
        `core.bbox.bbox_flip` in mmdetection
        `datasets.pipelines.RandomFlip.bbox_flip` in mmdetection
    """
    x_center = (image_width - 1) * 0.5
    y_center = (image_height - 1) * 0.5
    ret_boxes = flip_boxes(boxes, x_center, y_center, direction=direction)
    return ret_boxes
    
    
def rot90_boxes_in_image(boxes, image_width, image_height, n=1):
    """Rotate boxes counter-clockwise by 90 degrees.
    
    References:
        np.rot90
        cv2.rotate
        tf.image.rot90
    """
    n = n % 4
    if n == 0:
        ret_boxes = boxes.copy()
    elif n == 1:
        ret_boxes = transpose_boxes(boxes)
        ret_boxes = flip_boxes_in_image(ret_boxes, image_width, image_height, 'v')
    elif n == 2:
        ret_boxes = flip_boxes_in_image(boxes, image_width, image_height, 'o')
    else:
        ret_boxes = transpose_boxes(boxes)
        ret_boxes = flip_boxes_in_image(ret_boxes, image_width, image_height, 'h');
    return ret_boxes
    
    