#!/usr/bin/env python
# -*- coding: utf-8 -*-
# File: geometry.py
# Author: Yuxin Wu <ppwwyyxxc@gmail.com>

from .base import ImageAugmentor
import math
import cv2
import numpy as np

__all__ = ['Rotation', 'RotationAndCropValid']

class Rotation(ImageAugmentor):
    """ Random rotate the image w.r.t a random center"""
    def __init__(self, max_deg, center_range=(0,1),
            interp=cv2.INTER_CUBIC,
            border=cv2.BORDER_REPLICATE):
        """
        :param max_deg: max abs value of the rotation degree
        :param center_range: the location of the rotation center
        """
        super(Rotation, self).__init__()
        self._init(locals())

    def _get_augment_params(self, img):
        center = img.shape[1::-1] * self._rand_range(
                self.center_range[0], self.center_range[1], (2,))
        deg = self._rand_range(-self.max_deg, self.max_deg)
        return cv2.getRotationMatrix2D(tuple(center), deg, 1)

    def _augment(self, img, rot_m):
        ret = cv2.warpAffine(img, rot_m, img.shape[1::-1],
                flags=self.interp, borderMode=self.border)
        return ret

class RotationAndCropValid(ImageAugmentor):
    """ Random rotate and crop the largest possible rect without the border
        This will produce images of different shapes.
    """
    def __init__(self, max_deg, interp=cv2.INTER_CUBIC):
        super(RotationAndCropValid, self).__init__()
        self._init(locals())

    def _get_augment_params(self, img):
        deg = self._rand_range(-self.max_deg, self.max_deg)
        return deg

    def _augment(self, img, deg):
        center = (img.shape[1]*0.5, img.shape[0]*0.5)
        rot_m = cv2.getRotationMatrix2D(center, deg, 1)
        ret = cv2.warpAffine(img, rot_m, img.shape[1::-1],
                flags=self.interp, borderMode=cv2.BORDER_CONSTANT)
        neww, newh = RotationAndCropValid.largest_rotated_rect(ret.shape[1], ret.shape[0], deg)
        neww = min(neww, ret.shape[1])
        newh = min(newh, ret.shape[0])
        newx = center[0] - neww * 0.5
        newy = center[1] - newh * 0.5
        #print(ret.shape, deg, newx, newy, neww, newh)
        return ret[newy:newy+newh,newx:newx+neww]

    @staticmethod
    def largest_rotated_rect(w, h, angle):
        """ http://stackoverflow.com/questions/16702966/rotate-image-and-crop-out-black-borders """
        angle = angle / 180.0 * math.pi
        if w <= 0 or h <= 0:
            return 0,0

        width_is_longer = w >= h
        side_long, side_short = (w,h) if width_is_longer else (h,w)

        # since the solutions for angle, -angle and 180-angle are all the same,
        # if suffices to look at the first quadrant and the absolute values of sin,cos:
        sin_a, cos_a = abs(math.sin(angle)), abs(math.cos(angle))
        if side_short <= 2.*sin_a*cos_a*side_long:
          # half constrained case: two crop corners touch the longer side,
          #   the other two corners are on the mid-line parallel to the longer line
          x = 0.5*side_short
          wr,hr = (x/sin_a,x/cos_a) if width_is_longer else (x/cos_a,x/sin_a)
        else:
          # fully constrained case: crop touches all 4 sides
          cos_2a = cos_a*cos_a - sin_a*sin_a
          wr,hr = (w*cos_a - h*sin_a)/cos_2a, (h*cos_a - w*sin_a)/cos_2a

        return wr,hr