from ...constants import DEG2RAD, RAD2DEG
from math import *
from ..shape import gds_dynamic_shape, gds_shape
from .basic import shape_bend_rel
from ..shape_info import angle_rad, distance
from ...settings import get_angle_step
from ..coord import coord2_property
from ...properties.predefined import angle_property, length_property


__all__ = ["shape_arc_line_arc"]
class shape_arc_line_arc (gds_dynamic_shape):
    coord_start = coord2_property(required = True)
    angle_start = angle_property(required = True)
    radius_start = length_property(required = True)

    coord_end = coord2_property(required = True)
    angle_end= angle_property(required = True)
    radius_end = length_property(required = True)

    angle_step = angle_property(default = get_angle_step)
    
    def __init__(self, coord_start, angle_start, radius_start, coord_end, angle_end, radius_end, angle_step = None, **kwargs):
        if angle_step == None:
            angle_step = get_angle_step()
        super(shape_arc_line_arc, self).__init__(
            coord_start = coord_start,
            coord_end = coord_end,
            angle_start = angle_start,
            angle_end = angle_end,
            radius_start = radius_start,
            radius_end = radius_end,
            angle_step = angle_step,
            **kwargs)

    def __calc__(self):
        sa = self.angle_start * DEG2RAD
        ea = self.angle_end * DEG2RAD
        bae = (ea + pi)%(2*pi)

        # normalize angles between 0 and 2pi
        sa = (sa)%(2*pi)
        ea = (ea)%(2*pi)

        #angle bvetween two points
        connect_angle = angle_rad(self.coord_end, self.coord_start)
        ca_start = (connect_angle - sa)%(2*pi)
        ca_end =   (connect_angle - ea)%(2*pi)
        #print >> sys.stderr, "ca: ", connect_angle , ca_start, ca_end

        #check both positive and negative radii
        valid = False
        signs = [(1, 1), (1, -1), (-1, 1), (-1, -1)]
        for s in signs:
            radius_start = abs(self.radius_start) * s[0]
            radius_end = abs(self.radius_end) * s[1]
            
            # Centers of circles through the points.
            c_start= (self.coord_start[0] + radius_start * sin(sa),  self.coord_start[1] - radius_start * cos(sa))
            c_end= (self.coord_end[0] + radius_end * sin(ea), self.coord_end[1] - radius_end * cos(ea))

            #distance between centers
            dm = distance(c_start, c_end)
            if abs (radius_start - radius_end) > dm:
                # no valid solution possible 
                continue

            # unit vector between circle centers
            mm = ( (c_end[0] - c_start[0]) / dm, (c_end[1] - c_start[1]) / dm)
            # angle between normal to connector line and circle centers
            alpha = -acos((radius_start - radius_end) / dm);

            # unit vector from m to p.
            mp = (mm[0] * cos(alpha) + mm[1] * sin(alpha), -mm[0] * sin(alpha) + mm[1] * cos(alpha) )

            # Point at first circle. 
            p0= (c_start[0] + radius_start * mp[0], c_start[1] + radius_start * mp[1])
            # Point at second circle.
            p1 = (c_end[0] + radius_end * mp[0], c_end[1] + radius_end * mp[1])

            #print >> sys.stderr, "p0, p1:", p0, p1

            forward_angle = angle_rad(p1, p0) % (2*pi)
            backward_angle = angle_rad(p0, p1) % (2*pi)

            forward_turn =  (forward_angle - sa + pi) % (2*pi) - pi
            backward_turn = (backward_angle - bae + pi) %(2*pi) - pi

            # print >> sys.stderr, "F:", s[0], "B:", s[1], forward_turn, backward_turn
            
            if (forward_turn * s[0] <= 0) and (backward_turn * s[1] >= 0):
                valid = True
                break

        if not valid:
            print >> sys.stderr, "Error: Can't connect two points with arc_line_arc"
            raise SystemExit

        #print >> sys.stderr, "angles:", angle_start, straight_angle*180/pi, angle_end, backward_angle*180/pi
        if forward_turn == 0.0:
            self += self.coord_start
        else:
            self += shape_bend_rel(self.coord_start, abs(radius_start), sa * RAD2DEG, forward_turn * RAD2DEG, self.angle_step)

        if backward_turn == 0.0:
            self += self.coord_end
        else:
            bend2 = shape_bend_rel(self.coord_end, abs(radius_end), bae * RAD2DEG, backward_turn*RAD2DEG, self.angle_step)
            bend2.reverse()
            self += bend2

    def move(self, position):
        self.coord_start = (self.coord_start[0] + position[0], self.coord_start[1] + position[1])
        self.coord_end = (self.coord_end[0] + position[0], self.coord_end[1] + position[1])
        return self