from .no_distort import gds_no_distort_transform
from .special import gds_special_no_distort_transform
from .. import transformable
from ..coord import coord2, coord2_property
from ...properties.descriptor import restricted_set_function_property
from ...properties.initializer import SUPPRESSED
from ...properties.restrictions import restrict_type
from ...properties.processors import processor_typecast
from ...properties.predefined import RESTRICT_NUMBER

from ...mixin import mixin
from numpy import array

__all__ = ["gds_v_mirror",
           "gds_h_mirror",
           "gds_c_mirror"]

class gds_v_mirror(gds_special_no_distort_transform):
    """ mirror transformation around y-plane """
    def __init__(self, mirror_plane_y = 0.0, **kwargs):
        kwargs['translation'] = SUPPRESSED
        
        super(gds_v_mirror, self).__init__(
            mirror_plane_y = mirror_plane_y,
            v_mirror = True,
            **kwargs)
        
    def set_mirror_plane_y(self, value):
        self.__mirror_plane_y__ = value
        self.translation = coord2(0.0, 2.0 * value)
    mirror_plane_y = restricted_set_function_property("__mirror_plane_y__", set_mirror_plane_y, restriction = RESTRICT_NUMBER, default = 0.0)

    # overloading for efficiency
    def apply_to_coord(self, coord):      
        """ apply transformation to coordinate """
        coord = self.__v_flip__(coord)
        coord = self.__translate__(coord)
        return coord

    def reverse_on_coord(self, coord):      
        """ apply reverse transformation to coordinate """
        coord = self.__inv_translate__(coord)
        coord = self.__inv_v_flip__(coord)
        return coord

    def apply_to_array(self, coords):      
        """ apply transformation to numpy array"""
        coords = self.__v_flip_array__(coords)
        coords = self.__translate_array__(coords)
        return coords

    def reverse_on_array(self, coords):      
        """ internal use: applies reverse transformation to a numpy array """
        coords = self.__inv_translate_array__(coords)
        coords = self.__inv_v_flip_array__(coords)
        return coords

    def __neg__(self):
        """ returns reverse transformation """
        return gds_v_mirror(self.mirror_plane_y)

    def is_identity(self):
        """ returns True if the transformation does nothing """
        return False

    def apply_to_angle_deg(self, angle):
        """ apply transformation to absolute angle (degrees) """
        return (-angle)%360.0
    
    def reverse_on_angle_deg(self, angle):
        """ apply reverse transformation to absolute angle (degrees) """
        return (-angle)%360.0

    def apply_to_angle_rad(self, angle):
        """ apply transformation to absolute angle (radians) """
        return (-angle)%(2*pi)
    
    def reverse_on_angle_rad(self, angle):
        """ apply reverse transformation to absolute angle (radians) """
        return (-angle)%(2*pi)



class gds_h_mirror(gds_special_no_distort_transform):
    """ mirror transformation around x plane """
    def __init__(self, mirror_plane_x = 0.0, **kwargs):
        kwargs['translation'] = SUPPRESSED
        super(gds_h_mirror, self).__init__(
            mirror_plane_x = mirror_plane_x,
            v_mirror = True,
            rotation = 180.0,
            **kwargs)

    def set_mirror_plane_x(self, value):
        self.__mirror_plane_x__ = value
        self.translation = coord2(2.0 * value, 0.0)
    mirror_plane_x = restricted_set_function_property("__mirror_plane_x__", set_mirror_plane_x, restriction = RESTRICT_NUMBER, default = 0.0)

    def __h_flip__ (self, coord):
        """ internal use: mirror coordinate around y-axis """
        return (-coord[0], coord[1])

    def __h_flip_array__ (self, coords):
        """ internal use: mirror array around y axis """
        coords *= array([-1, 1])
        return coords

    # overloading for efficiency
    def apply_to_coord(self, coord):      
        """ apply transformation to coordinate """
        # faster than rotation!
        coord = self.__h_flip__(coord)
        coord = self.__translate__(coord)
        return coord

    def reverse_on_coord(self, coord):      
        """ apply reverse transformation to coordinate """
        # faster than rotation!
        coord = self.__inv_translate__(coord)
        coord = self.__h_flip__(coord)
        return coord

    def apply_to_array(self, coords):      
        """ apply transformation to numpy array"""
        # faster than rotation!
        coords = self.__h_flip_array__(coords)
        coords = self.__translate_array__(coords)
        return coords

    def reverse_on_array(self, coords):      
        """ internal use: applies reverse transformation to a numpy array """
        # faster than rotation!
        coords = self.__inv_translate_array__(coords)
        coords = self.__h_flip_array__(coords)
        return coords

    def apply_to_angle_deg(self, angle):
        """ apply transformation to absolute angle (degrees) """
        return (180.0-angle)%360.0
    
    def reverse_on_angle_deg(self, angle):
        """ apply reverse transformation to absolute angle (degrees) """
        return (180.0-angle)%360.0

    def apply_to_angle_rad(self, angle):
        """ apply transformation to absolute angle (radians) """
        return (pi-angle)%(2*pi)
    
    def reverse_on_angle_rad(self, angle):
        """ apply reverse transformation to absolute angle (radians) """
        return (pi-angle)%(2*pi)
 
    def __neg__(self):
        """ returns reverse transformation """        
        return gds_h_mirror(self.mirror_plane_x)

    def is_identity(self):
        """ returns True if the transformation does nothing """
        return False


class gds_c_mirror(gds_special_no_distort_transform):
    """ mirror around point (= 180 degree turn)"""
    def __init__(self, mirror_center = (0.0, 0.0), **kwargs):
        kwargs['translation'] = SUPPRESSED
        super(gds_c_mirror, self).__init__(
            mirror_center = mirror_center,
            rotation = 180.0,
            **kwargs)

    def set_mirror_center(self, center):
        self.__mirror_center__ = coord2(center[0], center[1])
        self.translation = 2* self.__mirror_center__
    """ mirror center """
    mirror_center = restricted_set_function_property("__mirror_center__", set_mirror_center, restriction = restrict_type(coord2), preprocess = processor_typecast(coord2), default = (0.0, 0.0))

    def __c_flip__ (self, coord):
        """ internal use: point mirror coordinate """
        return (-coord[0], -coord[1])
    

    def __c_flip_array__ (self, coords):
        """ internal use: point mirror numpy array """
        coords *= array([-1, -1])
        return coords

    # overloading for efficiency
    def apply_to_coord(self, coord):      
        """ apply transformation to coordinate """
        # faster than rotation!
        coord = self.__c_flip__(coord)
        coord = self.__translate__(coord)
        return coord

    def reverse_on_coord(self, coord):      
        """ apply reverse transformation to coordinate """
        # faster than rotation!
        coord = self.__inv_translate__(coord)
        coord = self.__c_flip__(coord)
        return coord

    def apply_to_array(self, coords):      
        """ apply transformation to numpy array"""
        # faster than rotation!
        coords = self.__c_flip_array__(coords)
        coords = self.__translate_array__(coords)
        return coords

    def reverse_on_array(self, coords):      
        """ internal use: applies reverse transformation to a numpy array """
        # faster than rotation!
        coords = self.__inv_translate_array__(coords)
        coords = self.__c_flip_array__(coords)
        return coords

    def apply_to_angle_deg(self, angle):
        """ apply transformation to absolute angle (degrees) """
        return (180.0+angle)%360.0
    
    def reverse_on_angle_deg(self, angle):
        """ apply reverse transformation to absolute angle (degrees) """
        return (180.0+angle)%360.0

    def __neg__(self):
        """ returns reverse transformation """
        return gds_c_mirror(self.mirror_center)
    
    def is_identity(self):
        """ returns True if the transformation does nothing """
        return False


def shape_hflip (shape, flip_plane_x = 0.0):
    """ legacy: apply a h_mirror to a shape """
    return gds_h_mirror(flip_plane_x)(shape)

def shape_vflip (shape, flip_plane_y = 0.0):
    """ legacy: apply a v_mirror to a shape """
    return gds_v_mirror(flip_plane_y)(shape)



class __mirror_mixin__(object):
    def h_mirror(self, mirror_plane_x = 0.0):
        """mirrors this object horizontally """
        return self.transform(gds_h_mirror(mirror_plane_x))
    def v_mirror(self, mirror_plane_y = 0.0):
        """mirrors this this object vertically"""
        return self.transform(gds_v_mirror(mirror_plane_y))
    def c_mirror(self, mirror_center = (0.0, 0.0)):
        """point-mirrors this object """
        return self.transform(gds_c_mirror(mirror_center))
        
    def h_mirror_copy(self, mirror_plane_x = 0.0):
        """mirrors a copy of this object horizontally """
        return self.transform_copy(gds_h_mirror(mirror_plane_x))
    def v_mirror_copy(self, mirror_plane_y = 0.0):
        """mirrors a copy of this object vertically """
        return self.transform_copy(gds_v_mirror(mirror_plane_y))
    def c_mirror_copy(self, mirror_center = (0.0, 0.0)):
        """point_mirrors copy of this object """
        return self.transform_copy(gds_c_mirror(mirror_center))


mixin.mixin(transformable.gds_transformable_basic, __mirror_mixin__)
