#!/usr/bin/env python
# encoding: utf-8

import svgwrite as svg
import numpy as np
import IPython


class PressKit:

    scale = 4
    bounding_corners = [[0, 0], [0, 0]]
    drawing = None
    numsides = 4 #TODO: make this more general
    
    inset = 45 * scale
    width = 10 * scale
    height = 20 * scale
    slider_length = 150 * scale
    prong_height = width * 1.2
    prong_tail = 8 * scale
    flexure_width = 3 * scale
    chamfer_angle = np.pi/3

    def __init__(self, filename='test.svg'):

        self.drawing = svg.Drawing(filename)
    
    def get_center_y(self):
        return (self.get_y_min() + self.get_y_max())/2 
        
    def get_center_x(self):
        return (self.get_x_min() + self.get_x_max())/2
        
    def get_y_min(self):
        return self.bounding_corners[0][1]
        
    def get_x_min(self):
        return self.bounding_corners[0][0]
        
    def get_x_max(self):
        return self.bounding_corners[1][0]
        
    def get_y_max(self):
        return self.bounding_corners[1][1]
    
    #TODO: let's come up with a good way to check for arbitrary geometry
    
    def __set_bounding_corners(self, point_list):
    
        for point in point_list:
            
            self.bounding_corners[0][0] = min(self.bounding_corners[0][0], point[0])

            self.bounding_corners[0][1] = min(self.bounding_corners[0][1], point[1])
            
            self.bounding_corners[1][0] = max(self.bounding_corners[1][0], point[0])
            self.bounding_corners[1][1] = max(self.bounding_corners[1][1], point[1])
               
    
    def print_points(self, point_list, color='black', polygon=True, opacity=1, fill='none', stroke_width=1.0):
        if polygon:
            polygon = svg.shapes.Polygon( point_list, stroke=color, stroke_width=stroke_width, fill=fill, opacity=1.0,  stroke_opacity=1.0)
            self.drawing.add(polygon)
        else:
            polyline = svg.shapes.Polyline( point_list, stroke=color, stroke_width=stroke_width, fill=fill, opacity=1.0,  stroke_opacity=1.0)
            self.drawing.add(polyline)
        
    def print_points_rotated(self, point_list, color='black',polygon=True,opacity=1,fill='none',stroke_width=1.0):
        center_x = self.get_center_x()
        center_y = self.get_center_y()
        
        for i in range(self.numsides):
            theta = 2*np.pi * i / self.numsides

            rotMatrix = np.array([[np.cos(theta), -np.sin(theta)], 
                                     [np.sin(theta),  np.cos(theta)]])
                                  
            new_points = []   
            for point in point_list:
                temp_point = point
                temp_point = np.array(temp_point) - np.array((center_x, center_y))

                temp_point = np.dot(rotMatrix, temp_point)
                temp_point = np.array(temp_point) + np.array((center_x, center_y))
                
                new_points.append(tuple(temp_point))

            self.print_points(new_points, color=color, polygon=polygon,opacity=opacity, fill=fill, stroke_width=stroke_width)
        

    def create_perimeter(self, no_prongs=True):
        """This function always needs to be called for each object.  It sets the object perimeter
        for cutting.  It's probably important that I make this flexible.  It will also set temporary final
        bounds"""
        
        
        #For now this is just going to be a square.  In the future, a case statement
        xu = 300 * self.scale
        yu = 300 * self.scale
        xl = 0 * self.scale
        yl = 0 * self.scale
        
        center_x = (xu - xl) / 2
        center_y = (yu - yl) / 2
        
        p1 = (0, 0)
        point_list = []
        
        if no_prongs:
        
            for i in range(self.numsides):
                theta = 2*np.pi * i / self.numsides

                rotMatrix = np.array([[np.cos(theta), -np.sin(theta)], 
                                         [np.sin(theta),  np.cos(theta)]])
                         
                temp_point = p1                
                temp_point = np.array(temp_point) - np.array((center_x, center_y))
                temp_point = np.dot(rotMatrix, temp_point)
                temp_point = np.array(temp_point) + np.array((center_x, center_y))

                    
                point_list.append(tuple(temp_point))
            
            #and now we draw this:
            self.print_points(point_list, opacity=1.0)
            
        else:
        
            point_list = [(self.inset - self.width,  0), (0, 0), (0, self.inset - self.width)]
            for i in range(self.numsides):
                theta = 2*np.pi * i / self.numsides

                rotMatrix = np.array([[np.cos(theta), -np.sin(theta)], 
                                         [np.sin(theta),  np.cos(theta)]])
                                      
                new_points = []   
                for point in point_list:
                    temp_point = point
                    temp_point = np.array(temp_point) - np.array((center_x, center_y))
                    temp_point = np.dot(rotMatrix, temp_point)
                    temp_point = np.array(temp_point) + np.array((center_x, center_y))
                    
                    new_points.append(tuple(temp_point))

                self.print_points(new_points, color='black', polygon=False,opacity=1.0, fill='none', stroke_width=1.0)
                
            point_list = [(yu - self.inset - self.width,  0), (self.inset + self.width, 0)]

            for i in range(self.numsides):
                theta = 2*np.pi * i / self.numsides

                rotMatrix = np.array([[np.cos(theta), -np.sin(theta)], 
                                         [np.sin(theta),  np.cos(theta)]])
                                      
                new_points = []   
                for point in point_list:
                    temp_point = point
                    temp_point = np.array(temp_point) - np.array((center_x, center_y))
                    temp_point = np.dot(rotMatrix, temp_point)
                    temp_point = np.array(temp_point) + np.array((center_x, center_y))
                    
                    new_points.append(tuple(temp_point))

                self.print_points(new_points, color='black', polygon=False,opacity=1.0, fill='none', stroke_width=1.0)
                
            point_list = []
            for i in range(self.numsides):
                theta = 2*np.pi * i / self.numsides


                rotMatrix = np.array([[np.cos(theta), -np.sin(theta)], 
                                         [np.sin(theta),  np.cos(theta)]])
                         
                temp_point = p1                
                temp_point = np.array(temp_point) - np.array((center_x, center_y))
                temp_point = np.dot(rotMatrix, temp_point)
                temp_point = np.array(temp_point) + np.array((center_x, center_y))
                    
                point_list.append(tuple(temp_point))
        
        
        #first set the perimeter
        self.__set_bounding_corners([(xl, yl), (xu, yu)] )
        
        
        
        
    def create_ortho_slots(self):
        #create four slots
        width = self.width
        height = self.height
        inset = self.inset
        
        center_x = self.get_center_x()
        center_y = self.get_center_y()
        
        y_min = self.get_y_min()
        
        #create four polygons and place them appropriately around the rectangle
        point_list = [(center_x - width/2, y_min - height/2 + inset), 
        (center_x - width/2, y_min + height/2 + inset), 
        (center_x + width/2, y_min + height/2 + inset), 

        (center_x + width/2, y_min - height/2 + inset)]
        
        #now rotate this the appropriate number of times
        #self.print_points(point_list)
        
        self.print_points_rotated(point_list)
        
    def create_other_ortho_slots(self):
        #create four slots
        width = self.width
        height = self.height
        inset = self.inset
        
        center_x = self.get_center_x()
        center_y = self.get_center_y()
        
        y_min = self.get_y_min()
        x_min = self.get_x_min()
        x_max = self.get_x_max()
        
        #create four polygons and place them appropriately around the rectangle
        point_list = [(x_min - width/2 + inset, y_min - height/2 + inset), 
        (x_min - width/2 + inset, y_min + height/2 + inset), 
        (x_min + width/2 + inset, y_min + height/2 + inset), 
        (x_min + width/2 + inset, y_min - height/2 + inset)]
        
        #now rotate this the appropriate number of times
        #self.print_points(point_list)
        
        self.print_points_rotated(point_list)
        
        point_list2 = [(x_max - width/2 - inset, y_min - height/2 + inset), 
        (x_max - width/2 - inset, y_min + height/2 + inset), 
        (x_max + width/2 - inset, y_min + height/2 + inset), 
        (x_max + width/2 - inset, y_min - height/2 + inset)]
        
        #now rotate this the appropriate number of times
        #self.print_points(point_list)
        
        self.print_points_rotated(point_list2)

        
    def create_radial_slots(self):
    
        height = self.height

        width = self.width
        inset = self.inset
        
        y_min = self.bounding_corners[0][1]
        x_min = self.bounding_corners[0][0]
        center_y = self.get_center_y()
        center_x = self.get_center_x()
        
        separation = 50
        degrees = 10
        radians = np.radians(degrees)
        
        outer = (center_y - y_min) - inset - height / 2.0
        inner = (center_y - y_min) - inset + height / 2.0
        
        start_1x = center_x + inner - (1 - np.cos(radians)) * inner
        start_1y = center_y - np.sin(radians) * inner
        end_1x = center_x - inner + (1 - np.cos(radians)) * inner
        end_1y = center_y - np.sin(radians) * inner
        
        s1x = start_1x
        s1y = start_1y
        e1x = end_1x
        e1y = end_1y
        

        p = self.drawing.path(d="M%f,%f A%f,%f 0 0,0 %f,%f" % (start_1x, start_1y, inner, inner, end_1x, end_1y),
             fill="none", 
             stroke="black",
            )
        
        self.drawing.add(p)
        
        
        
        
        
        start_1x = center_x + outer - (1 - np.cos(radians)) * outer
        start_1y = center_y - np.sin(radians) * outer
        end_1x = center_x - outer + (1 - np.cos(radians)) * outer
        end_1y = center_y - np.sin(radians) * outer

        p = self.drawing.path(d="M%f,%f A%f,%f 0 0,0 %f,%f" % (start_1x, start_1y, outer, outer, end_1x, end_1y),
             fill="none", 
             stroke="black",
            )
        
        self.drawing.add(p)
        
        self.print_points([(s1x, s1y), (start_1x, start_1y)], polygon=False)
        self.print_points([(e1x, e1y), (end_1x, end_1y)], polygon=False)
        
        
        start_1x = center_x + inner - (1 - np.cos(radians)) * inner
        start_1y = center_y + np.sin(radians) * inner
        end_1x = center_x - inner + (1 - np.cos(radians)) * inner
        end_1y = center_y + np.sin(radians) * inner
        
        s1x = start_1x
        s1y = start_1y
        e1x = end_1x
        e1y = end_1y

        p = self.drawing.path(d="M%f,%f A%f,%f 0 0,1 %f,%f" % (start_1x, start_1y, inner, inner, end_1x, end_1y),
             fill="none", 
             stroke="black",

            )
        
        self.drawing.add(p)
        
        start_1x = center_x + outer - (1 - np.cos(radians)) * outer
        start_1y = center_y + np.sin(radians) * outer
        end_1x = center_x - outer + (1 - np.cos(radians)) * outer
        end_1y = center_y + np.sin(radians) * outer

        p = self.drawing.path(d="M%f,%f A%f,%f 0 0,1 %f,%f" % (start_1x, start_1y, outer, outer, end_1x, end_1y),
             fill="none", 
             stroke="black",
            )

        

        self.drawing.add(p)
        
        self.print_points([(s1x, s1y), (start_1x, start_1y)], polygon=False)
        self.print_points([(e1x, e1y), (end_1x, end_1y)], polygon=False)
        
        """
        self.print_points([(center_x + inner, center_y + separation), (center_x + outer, center_y + separation)], polygon=False)
        self.print_points([(center_x - inner, center_y + separation), (center_x - outer, center_y + separation)], polygon=False)
        self.print_points([(center_x + inner, center_y - separation), (center_x + outer, center_y - separation)], polygon=False)
        self.print_points([(center_x - inner, center_y - separation), (center_x - outer, center_y - separation)], polygon=False)
        """

        
        
        
        
        
        #self.drawing.add(   svg.shapes.Circle(center=(center_x, center_y), r=outer, stroke='black', stroke_width=1, fill='none')   )
        #self.drawing.add(   svg.shapes.Circle(center=(center_x, center_y), r=inner, stroke='black', stroke_width=1, fill='none')   )
        
    def create_rectangle_slots(self):
        #create four slots
        width = self.slider_length
        height = self.height
        inset = self.inset
        
        center_x = self.get_center_x()
        center_y = self.get_center_y()
        y_min = self.get_y_min()
        
        #create four polygons and place them appropriately around the rectangle
        point_list = [(center_x - width/2, y_min - height/2 + inset), 
        (center_x - width/2, y_min + height/2 + inset), 
        (center_x + width/2, y_min + height/2 + inset), 
        (center_x + width/2, y_min - height/2 + inset)]
        
        #now rotate this the appropriate number of times
        #self.print_points(point_list)
        
        self.print_points_rotated(point_list)
            
    def create_prongs(self):
        #create four slots
        width = self.width
        height = self.height
        inset = self.inset
        prong_height = self.prong_height

        prong_tail = self.prong_tail
        chamfer_angle = self.chamfer_angle
        flexure_width = self.flexure_width
        
        center_x = self.get_center_x()
        center_y = self.get_center_y()
        y_min = self.get_y_min()
        y_max = self.get_y_max()
        x_min = self.get_x_min()
        
        #create two filanges
        
        #This is the first one
        point_list = [
        ((y_min + height/2 + inset), x_min),
        ((y_min + height/2 + inset), x_min - prong_height),
        ((y_min + height/2 + inset + prong_tail), x_min - prong_height),
        ((y_min + height/2 + inset - np.cos(chamfer_angle)*prong_tail), x_min - prong_height - np.sin(chamfer_angle)*prong_tail),

        
        ((y_min - height/2 + inset + np.cos(chamfer_angle)*prong_tail), x_min - prong_height - np.sin(chamfer_angle)*prong_tail),
        ((y_min - height/2 + inset - prong_tail), x_min - prong_height),
        ((y_min - height/2 + inset), x_min - prong_height),
        ((y_min - height/2 + inset), x_min), 
        ]
        

        self.print_points_rotated(point_list, color='black', polygon=False)
        
        point_list2 = [
        (y_max - (y_min + height/2 + inset), x_min),
        (y_max - (y_min + height/2 + inset), x_min - prong_height),
        (y_max - (y_min + height/2 + inset + prong_tail), x_min - prong_height),
        (y_max - (y_min + height/2 + inset - np.cos(chamfer_angle)*prong_tail), x_min - prong_height - np.sin(chamfer_angle)*prong_tail),
        

        
        (y_max - (y_min - height/2 + inset + np.cos(chamfer_angle)*prong_tail), x_min - prong_height - np.sin(chamfer_angle)*prong_tail),
        (y_max - (y_min - height/2 + inset - prong_tail), x_min - prong_height),
        (y_max - (y_min - height/2 + inset), x_min - prong_height),
        (y_max - (y_min - height/2 + inset), x_min), 
        ]
        


        self.print_points_rotated(point_list2, color='black', polygon=False)
        
        #TODO: cut out part of the original perimeter
        
        
        
        
        
    def save(self):
        self.drawing.viewbox(-1000, -1000, 2000, 2000)
        self.drawing.fit(horiz='left', vert='top')
        self.drawing.save()
        
if __name__ == "__main__":
    """
    pk = PressKit('prong_square.svg')
    pk.create_perimeter(False)
    pk.create_prongs()
    pk.create_other_ortho_slots()
    pk.save()
    """
    """
    pk = PressKit('octogon.svg')
    pk.create_perimeter(True)
    pk.create_ortho_slots()
    pk.save()
    """
    
    
    pk = PressKit('revolute.svg')
    pk.create_perimeter(False)
    pk.create_prongs()
    pk.create_radial_slots()
    pk.save()
    
    """
    pk = PressKit('prismatic.svg')
    pk.create_perimeter(False)
    pk.create_prongs()
    pk.create_rectangle_slots()
    pk.save()
    """
