from gk_coord import *
from math import *
from gk_global import *
from gk_vector import *
import gk_transformable

class straight_line(gk_transformable.gds_transformable):
    """ creates a line ax + by + c = 0 """
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c
    
    def get_slope(self):
        if self.b == 0:
            return None
        return -self.a / self.b
    slope = property(get_slope, doc = "gives slope (tangent) of the line""")
    
    def is_on_line(self, coordinate):
        """ returns True if point is on line """
        return abs(self.a * coordinate[0] + self.b * coordinate[1] + self.c) < 1E-10
    
    def get_angle_rad(self):
        return atan2(-self.a, self.b)
    angle_rad = property(get_angle_rad, doc = "gives angle of line (in radians)")
    def get_angle_deg(self):
        return RAD2DEG * self.angle_rad
    angle_deg = property(get_angle_deg, doc = "gives angle of line (in radians)")
    
    def get_y_intercept(self):
        if self.b == 0.0:
            return None
        return -self.c / -self.b
    y_intercept = property(get_y_intercept, doc = "gives y-intercept of line (None if parallel to Y)")
    
    def get_x_intercept(self):
        if self.a == 0.0:
            return None
        return -self.c / -self.a
    x_intercept = property(get_x_intercept, doc = "gives x-intercept of line (None if parallel to X)")
    
    def distance(self, coordinate):
        """ gives distance of point to line """
        return abs(self.a * coordinate[0] + self.b * coordinate[1] + self.c) / sqrt(self.a**2 + self.b**2)

    def intersection(self, line):
        """ gives intersection of line with other line """
        if (self.b * line.a - self.a * line.b) == 0.0:
            return None
        return coord2(-(self.b * line.c - line.b * self.c)/ (self.b * line.a - self.a * line.b) ,
                      (self.a * line.c - line.a * self.c)/ (self.b * line.a - self.a * line.b))

    def closest_point(self, point):
        """ gives closest point on line """
        line2 = straight_line_from_point_angle(point, self.angle_deg + 90.0)
        return self.intersection(line2)
    
    def is_on_same_side(self, point1, point2):
        """ returns True is both points are on the same side of the line """
        return sign(self.a * point1[0] + self.b * point1[1] + self.c) == sign(self.a * point2[0] + self.b * point2[1] + self.c)


    def is_parallel(self, other):
        """ returns True is lines are parallel """
        return abs(self.a * other.b - self.b * other.a) < 1E-10

    def __eq__(self, other):
        return abs(self.a * other.b - self.b * other.a)< 1E-10 and abs(self.c * other.b -self.b * other.c)< 1E-10 and abs(self.a * other.c -self.c * other.a)< 1E-10
    
    def __get_2_points__(self):
        """ returns 2 points on the line. If a horizontal or vertical, it returns one point on the axis, and another 1.0 further.
            If the line is oblique, it returns the intersects with the axes """
        if b == 0:
            return gds_shape([coord2(-self.c / self.a, 0.0), coord2(-self.c / self.a, 1.0)])
        elif a == 0: 
            return gds_shape([coord2(0.0, -self.c / self.b), coord2(1.0, -self.c / self.b)])
        else:
            return gds_shape([coord2(-self.c / self.a, 0.0), coord2(0.0, -self.c / self.b)])
    
    def transform(self, transformation):
        """ transforms the straight line with a given transformation """
        p = self.__get_2_points__().transform(transformation)
        self.a = y2-y1
        self.b = x1 - x2
        self.c = (x2-x1)*y1 - (y2-y1) * x1
        
    def transform_copy(self, transformation):
        """ transforms a copy of the straight line with a given transformation """
        p = self.__get_2_points__().transform(transformation)
        return straight_line_from_two_points(p[0], p[1])


    

def straight_line_from_slope_intercept(slope, y_intercept):
    """ creates straight_line object from slope and y_intercept """
    return straight_line(slope, -1.0, intercept)

def straight_line_from_two_points(point1, point2):
    """ creates straight_line object from two points """
    x1, y1 = point1[0], point1[1]
    x2, y2 = point2[0], point2[1]
    return straight_line(y2-y1, x1 - x2, (x2-x1)*y1 - (y2-y1) * x1)

def straight_line_from_point_angle(point, angle):
    """ creates straight_line object from point and angle """
    slope = tan(DEG2RAD *angle)
    return straight_line(slope, -1, point[1] - slope * point[0])

def straight_line_from_vector(vector):
    """ creates straight_line object from a vector """
    return straight_line_from_point_angle(vector.position, vector.angle_deg)
    
