from .. import constants
from . import coord
from . import shape
import numpy
from math import atan2, pi, sqrt

__all__ = ["shape_bottom",
           "shape_bottom_left",
           "shape_bottom_right",
           "shape_bounding_box",
           "shape_box",
           "shape_box_center",
           "shape_left",
           "shape_length",
           "shape_orientation",
           "shape_right",
           "shape_size",
           "shape_top",
           "shape_top_left",
           "shape_top_right",
           "shape_xcoords",
           "shape_ycoords",
           "angle_deg",
           "angle_rad",
           "distance",
           "lines_cross",
           "intersection",
           "turn_deg",
           "turn_rad",
           "is_left",
           "midpoint",
           "point_in_triangle"]
           

#----------------------------------------------------------------------------
#shape information
#----------------------------------------------------------------------------

def shape_xcoords(shape):
        """ returns the x coordinates of a shape """
        return shape.__points__[:, 0].tolist()
                
def shape_ycoords(shape):
        """ returns the y coordinates of a shape """
        return shape.__points__[:, 1].tolist()

def shape_top(shape):
        """ returns the top Y of the shape """
        return numpy.max(shape.__points__[:, 1])

def shape_bottom(shape):
        """ returns the bottom Y of the shape """
        return numpy.min(shape.__points__[:, 1])

def shape_left(shape):
        """ returns the left X of the shape """
        return numpy.min(shape.__points__[:, 0])

def shape_right(shape):
        """ returns the right X of the shape """
        return numpy.max(shape.__points__[:, 0])

def shape_size(shape):
        """ returns the size of the shape """
        LB = numpy.min(shape.__points__, 0)
        TR = numpy.max(shape.__points__, 0)
        return (TR[0] - LB[0], TR[1] - LB[1])

def shape_bottom_left(shape):
        """ returns the bottom left coordinate of the shape """
        LB = numpy.min(shape.__points__, 0)
        TR = numpy.max(shape.__points__, 0)
        return coord.coord2(LB[0], LB[1])

def shape_top_right(shape):
        """ returns the top right coordinate of the shape """
        TR = numpy.max(shape.__points__, 0)
        return coord.coord2(TR[0], TR[1])

def shape_bottom_right(shape):
        """ returns the bottom right coordinate of the shape """
        LB = numpy.min(shape.__points__, 0)
        TR = numpy.max(shape.__points__, 0)
        return coord.coord2(TR[0], LB[1])

def shape_top_left(shape):
        """ returns the top left coordinate of the shape """
        LB = numpy.min(shape.__points__, 0)
        TR = numpy.max(shape.__points__, 0)
        return coord.coord2(LB[0], TR[1])

def shape_box_center(shape):
        """ returns the center coordinate of the shape """
        LB = numpy.min(shape.__points__, 0)
        TR = numpy.max(shape.__points__, 0)
        return coord.coord2(0.5*(LB[0] + TR[0]), 0.5*(LB[0] + TR[1]))

def shape_box(shape):
        """ returns the (bottom_left , top_right) coordinates of the shape """
        if len(shape) == 0:
                return shape.gds_shape([(0.0,0.0), (0.0,0.0)])
        else:
                LB = numpy.min(shape.__points__, 0)
                TR = numpy.max(shape.__points__, 0)
                return shape.gds_shape([(LB[0], LB[1]), (TR[0], TR[1])], True)

def shape_bounding_box(coordinates):
        """ returns a rectangle shape enclosing the shape"""
        if len(shape) == 0:
                return shape.gds_shape([(0.0,0.0), (0.0,0.0), (0.0, 0.0), (0.0, 0.0)], True)
        else:
                LB = numpy.min(shape.__points__, 0)
                TR = numpy.max(shape.__points__, 0)
                return shape.gds_shape([(LB[0], LB[1]), (LB[0], TR[1]), (TR[0], TR[1]), (TR[0], LB[1])], True)

def shape_orientation(coordinates):
        """ returns true for clockwise orientation """
        #returns True if the coordinates are clockwise or False if not
        turn = 0.0
        L = len(coordinates)
        if coordinates[0] == coordinates[-1]:
                L -= 1
        for i in range(L):
                angle1 = atan2(coordinates[i][1] - coordinates[(i-1)%L][1], coordinates[i][0] - coordinates[(i-1)%L][0])
                angle2 = atan2(coordinates[(i+1)%L][1] - coordinates[i][1], coordinates[(i+1)%L][0] - coordinates[i][0])
                turn += ((angle2 - angle1 + pi)%(2*pi))-pi
        return turn < 0
        

def distance(coord, origin = (0.0,0.0)):
        """ distance of coordinate to origin """
        return sqrt((coord[0]-origin[0])**2 + (coord[1]-origin[1])**2)

def angle_rad(coord, origin = (0.0,0.0)):
        """ absolute angle (radians) of coordinate with respect to origin"""
        return atan2(coord[1]-origin[1], coord[0]-origin[0])

def angle_deg(coord, origin = (0.0,0.0)):
        """ absolute angle (radians) of coordinate with respect to origin"""
        return angle_rad(coord, origin) * constants.RAD2DEG

def turn_rad(coord1, coord2, coord3):
        """ turn angle in coord 2 """
        angle1 = angle_rad(coord2, coord1)
        angle2 = angle_rad(coord3, coord2)
        return (angle2 - angle1+pi)%(2*pi) - pi

def turn_deg(coord1, coord2, coord3):
        """ turn angle in coord 2 """
        return turn_rad(coord1, coord2, coord3) * constants.RAD2DEG


def is_left(point, line_point1, line_point2):
        """ checks if point lies to the left (>0), the right (<0) or on (=0) of the line defined by line_point1 and 2 
        point can be a single point or a numpy array"""
        S = shape.gds_shape(point)
        P1 = numpy.array([line_point1[0], line_point1[1]])
        P2 = numpy.array([line_point2[0], line_point2[1]])
        R = numpy.sign(numpy.diff(  (S.__points__ - P1) * numpy.flipud(P2 - P1) , 1, 1))
        if len(R) == 1: 
                return R[0]
        else:
                return R
        
                #return ( (line_point2[0] - line_point1[0]) * (point[1]- line_point1[1])
                         #- (point[0] - line_point1[0]) * (line_point2[1] - line_point1[1]) )


def lines_cross(begin1, end1, begin2, end2):
        """ returns true if the line segments intersect """
        # check if line ends between points cross
        # rechte = Ax + By - C = 0
        A1 = end1[1] - begin1[1]
        B1 = -end1[0] + begin1[0]
        C1 = - (begin1[1] * B1 + begin1[0] * A1)

        A2 = end2[1] - begin2[1]
        B2 = -end2[0] + begin2[0]
        C2 = - (begin2[1] * B2 + begin2[0] * A2)

        if A1*B2 == A2*B1:
                return False

        return ((A1 * begin2[0] + B1 * begin2[1] + C1) * (A1 * end2[0] + B1 * end2[1] + C1) < 0 and
                        (A2 * begin1[0] + B2 * begin1[1] + C2) * (A2 * end1[0] + B2 * end1[1] + C2) < 0)

        
def intersection(begin1, end1, begin2, end2):
        """ gives the intersection between two lines (not sections) """
        # calculate the intersection of 2 lines through points
        # rechte = Ax + By  = C
        A1 = end1[1] - begin1[1]
        B1 = -end1[0] + begin1[0]
        C1 = begin1[1] * B1 + begin1[0] * A1

        A2 = end2[1] - begin2[1]
        B2 = -end2[0] + begin2[0]
        C2 = begin2[1] * B2 + begin2[0] * A2

        # check if lines aren't parallel
        if A1*B2 == A2*B1:
                print >> sys.stderr, "Error: Can't intersect parallel lines"
                raise SystemExit

        x = (C1*B2 - C2*B1)/(A1*B2 - A2*B1)
        y = (C1*A2 - C2*A1)/(B1*A2 - B2*A1)
        return (x, y)

def midpoint(P1, P2, ratio =0.5):
        return ratio * coord.coord2(P1[0], P1[1]) + (1-ratio) * coord.coord2(P2[0], P2[1])

def point_in_triangle(P, P1, P2, P3):
        """ returns true if point P is in the triangle (P1,P2,P3) """
        # for speed
        x, y = P[0], P[1]
        x1, y1 = P1[0], P1[1] 
        x2, y2 = P2[0], P3[1] 
        x3, y3 = P3[0], P3[1] 
        f12 = (y-y1)*(x2-x1) - (x-x1)*(y2-y1)
        f31 =  (y-y3)*(x1-x3) - (x-x3)*(y1-y3)
        f23 =  (y-y2)*(x3-x2) - (x-x2)*(y3-y2)
        return (f12*f23)>0 and (f23 * f31) > 0


def shape_length(coordinates):
        """ length of a shape """
        #returns the length of a shape
        L = 0
        last_c = coordinates[0]
        for c in coordinates[1:]:
                L+= distance(c, last_c)
                last_c = c
        return L


