class CustomLayout(BoxLayout):
    def __init__(self, **kwargs):

        super(CustomLayout, self).__init__(**kwargs)
        
        Window.bind(on_key_down=self.key_action) #Binds Keyboard for key detection
        Window.bind(on_resize=self.on_window_resize)
        self.saved_states= []
        self.back_button = Button(text = 'Back',
                                          background_color = (1,1,1,1),
                                          font_size = '12dp',
                                          size_hint = (.3,.07),
                                          pos_hint = {'bottom': 0.3})
        #self.back_button.tooltip.text ="Go back to main menu"
        # Add change color button
        self.color_picker_button = ToolBtn(text = 'Choose Color',
                                          font_size = '12dp',
                                          background_color = (1,1,1,1),
                                          size_hint = (.35,.07),
                                          pos_hint = {'bottom': 0.3})
        self.color_picker_button.tooltip.text ="Find a color that suits your tessellation"

        #Add background color button
        self.background_color_picker_button = ToolBtn(text = 'Choose Background',
                                                     font_size = '12dp',
                                                     background_color = (1,1,1,1),
                                                     size_hint = (.45,.07),
                                                     pos_hint = {'bottom': 0.3})
        self.background_color_picker_button.tooltip.text = "Choose color for tessellation background"
        self.add_widget(self.back_button)
        self.back_button.bind(on_press=self.go_back)
        self.add_widget(self.color_picker_button)
        self.color_picker_button.bind(on_press=self.change_color)
        self.add_widget(self.background_color_picker_button)
        self.color_picker_button.bind(on_press=self.change_color_background)

        #add help button for keyboard commands
        def open_help(self, *args):
            self.help.open()

        self.help_label = Label(text = " Click on edge/vertex to select\n To delete edge/vertex: Press 'delete'/'backspace\n To add a vertex: Press 'a'\n To reset: Press 'r'",
                                font_size = '20sp')
        self.help = Popup(title = 'Help',
                           content = self.help_label,
                           pos_hint={'center_x': .5, 'center_y': .5},
                           size_hint = (.4, .4),
                           auto_dismiss = True)
        self.help_button = ToolBtn(text = '?',
                                   size_hint = (.1, .04),
                                   background_color = (1,1,1,.3),
                                   pos_hint = {'top':1})
        self.help_button.tooltip.text = "Click for controls help and descriptions"
        self.help_button.bind(on_press = open_help)
        self.add_widget(self.help_button)

        self.shape = InstructionGroup()
        self.col = [1,1,1,1]
        self.col_background = [0,0,0,1]
        self.fill = [0,0,1,1]

        self.c_coords = f_coords
        #print(self.c_coords)
        #print("num points:")
        #print(len(self.c_coords))
        self.pressed = False
        self.index = -1
        self.edge = -1

        self.canvas_edge = {}
        self.canvas_nodes = {}
        self.nodesize = [20, 20]

        self.grabbed = {}

        self.configCoords()

        #declare a canvas
        with self.canvas.after:
            pass

        self.define_nodes()

        i = 0
        for points in self.c_coords:
            self.canvas.add(self.canvas_nodes[i])
            i = i + 1

        self.define_edge()

        i = 0
        for i in range(len(self.c_coords)):
            self.canvas.add(self.canvas_edge[i])
            i = i + 1

    def configCoords(self):
        poly = Polygon(self.c_coords)

        sizeX = Window.size[0]
        sizeY = Window.size[1]
        xdistnace = (poly.bounds[2] - poly.bounds[0])
        ydistance = (poly.bounds[3] - poly.bounds[1])
        self.xscale = sizeX * .25 / xdistnace
        self.yscale = sizeY * .25 / ydistance
        center = (poly.centroid.coords[0])
        xoff = ((sizeX/5) + (Window.size[0]*.14)) - center[0]
        yoff = (sizeY/4) - center[1]

        poly = affinity.translate(poly, xoff= xoff, yoff= yoff)
        if self.xscale > self.yscale:
            poly = affinity.scale(poly, xfact= self.yscale, yfact= self.yscale)
        else:
            poly = affinity.scale(poly, xfact= self.xscale, yfact= self.xscale)
        self.c_coords = list(poly.exterior.coords)
        self.c_coords.pop(-1)

        temp2 = []
        for x in self.c_coords:
            temp = []
            for y in x:
                temp.append(int(y))
            temp2.append(tuple(temp))
        self.c_coords = temp2
        self.orgi_coords = self.c_coords

    def config_from_shapely_poly(self, shapely_poly_saved):
        poly = shapely_poly_saved
        self.c_coords = poly.exterior.coords
        sizeX = Window.size[0]
        sizeY = Window.size[1]
        xdistnace = (poly.bounds[2] - poly.bounds[0])
        ydistance = (poly.bounds[3] - poly.bounds[1])
        self.xscale = sizeX * .25 / xdistnace
        self.yscale = sizeY * .25 / ydistance
        center = (poly.centroid.coords[0])
        xoff = ((sizeX/5) + (Window.size[0]*.14)) - center[0]
        yoff = (sizeY/4) - center[1]

        poly = affinity.translate(poly, xoff= xoff, yoff= yoff)
        if self.xscale > self.yscale:
            poly = affinity.scale(poly, xfact= self.yscale, yfact= self.yscale)
        else:
            poly = affinity.scale(poly, xfact= self.xscale, yfact= self.xscale)
        self.c_coords = list(poly.exterior.coords)
        self.c_coords.pop(-1)

        temp2 = []
        for x in self.c_coords:
            temp = []
            for y in x:
                temp.append(int(y))
            temp2.append(tuple(temp))
        self.c_coords = temp2
        self.orgi_coords = self.c_coords

    def key_action(self, *args):

        key_pressed = list(args)


        #Delete when pressing delete
        if key_pressed[2] == 42 and self.pressed and len(self.canvas_edge) > 3:
            self.pressed = False
            self.canvas.children.remove(self.highlight)

            if self.edge:
                self.canvas.children.remove(self.canvas_edge[self.index])
                self.canvas.children.remove(self.canvas_nodes[self.index])
                self.canvas.children.remove(self.canvas_nodes[(self.index+1)%len(self.canvas_nodes)])

                mid = midpoint(self.canvas_edge[self.index].points)

                self.c_coords.insert((self.index+1)%len(self.canvas_nodes), tuple(mid))
                self.c_coords.remove(self.canvas_nodes[self.index].pos)
                self.c_coords.remove(self.canvas_nodes[(self.index+1)%len(self.canvas_nodes)].pos)
            elif not self.edge:
                self.canvas.children.remove(self.canvas_nodes[self.index])
                self.c_coords.remove(self.canvas_nodes[self.index].pos)

            self.draw()
            poly = []
            i = 0
            for xy in self.canvas_nodes:
                poly.append(self.canvas_nodes[i].pos)
                i = i + 1

            newply = Polygon(poly)
            newply = affinity.translate(newply, xoff= -size[0]/2.95, yoff= -size[1]/4)
            if self.xscale > self.yscale:
                newply = affinity.scale(newply, xfact= 1/self.yscale, yfact= 1/self.yscale)
            else:
                newply = affinity.scale(newply, xfact= 1/self.xscale, yfact= 1/self.xscale)
            self.parent.children[1].original_base_unit = newply
            self.parent.children[1].polygon = newply
            self.parent.children[1].reset(0)
            #self.parent.children[1].tile_regular_polygon()

            if (self.parent.children[0] != None):
                    new_shape_info = tr.identify_shape(newply)
                    self.parent.main_shape_info = new_shape_info
                    self.parent.children[1].shape_info = new_shape_info
                    for state in self.saved_states:
                        new_shape_info.append(state)
                    self.parent.remove_widget(self.parent.children[0])
                    btns = ReccomendationButtons()
                    self.parent.add_widget(btns)
                    if (new_shape_info != None and len(new_shape_info) >= 1):
                        btns.setup_btns(False)
        #Add when pressing a
        elif key_pressed[2] == 4 and self.pressed:
            try:
                self.canvas.children.remove(self.highlight)
                mid = midpoint(self.canvas_edge[self.index].points)
                self.c_coords.insert((self.index+1)%len(self.canvas_nodes), tuple(mid))
                self.draw()

            except:
                print(' Point selected')
            self.pressed = False

            poly = []
            i = 0
            for xy in self.canvas_nodes:
                poly.append(self.canvas_nodes[i].pos)
                i = i + 1

            newply = Polygon(poly)
            newply = affinity.translate(newply, xoff= -size[0]/2.95, yoff= -size[1]/4)
            if self.xscale > self.yscale:
                newply = affinity.scale(newply, xfact= 1/self.yscale, yfact= 1/self.yscale)
            else:
                newply = affinity.scale(newply, xfact= 1/self.xscale, yfact= 1/self.xscale)
            self.parent.children[1].original_base_unit = newply
            self.parent.children[1].polygon = newply
            self.parent.children[1].reset(0)
            if (self.parent.children[0] != None):
                    new_shape_info = tr.identify_shape(newply)
                    self.parent.main_shape_info = new_shape_info
                    self.parent.children[1].shape_info = new_shape_info
                    for state in self.saved_states:
                        new_shape_info.append(state)
                    self.parent.remove_widget(self.parent.children[0])
                    btns = ReccomendationButtons()
                    self.parent.add_widget(btns)
                    if (new_shape_info != None and len(new_shape_info) >= 1):
                        btns.setup_btns(False)

        #Reset when pressing r
        elif key_pressed[2] == 21:
            self.c_coords = self.orgi_coords
            self.draw()

            poly = []
            i = 0
            for xy in self.canvas_nodes:
                poly.append(self.canvas_nodes[i].pos)
                i = i + 1

            newply = Polygon(poly)
            newply = affinity.translate(newply, xoff= -size[0]/2.95, yoff= -size[1]/4)
            if self.xscale > self.yscale:
                newply = affinity.scale(newply, xfact= 1/self.yscale, yfact= 1/self.yscale)
            else:
                newply = affinity.scale(newply, xfact= 1/self.xscale, yfact= 1/self.xscale)
            self.parent.children[1].original_base_unit = newply
            self.parent.children[1].polygon = newply
            self.parent.children[1].reset(0)
            #self.parent.children[1].tile_regular_polygon()

            if (self.parent.children[0] != None):
                    new_shape_info = tr.identify_shape(newply)
                    self.parent.main_shape_info = new_shape_info
                    self.parent.children[1].shape_info = new_shape_info
                    for state in self.saved_states:
                        new_shape_info.append(state)
                    self.parent.remove_widget(self.parent.children[0])
                    btns = ReccomendationButtons()
                    self.parent.add_widget(btns)
                    if (new_shape_info != None and len(new_shape_info) >= 1):
                        btns.setup_btns(False)
    #resize event for formatting
    def on_window_resize(self, window, width, height):
        self.canvas.remove_group('shape')
        self.canvas.remove(self.shape)
        self.configCoords()

        self.define_nodes()

        i = 0
        for points in self.c_coords:
            self.canvas.add(self.canvas_nodes[i])
            i = i + 1

        self.define_edge()

        i = 0
        for i in range(len(self.c_coords)):
            self.canvas.add(self.canvas_edge[i])
            i = i + 1
        self.parent.remove_widget(self.parent.children[0])
        btns = ReccomendationButtons()
        self.parent.add_widget(btns)
        if (self.parent.main_shape_info != None and len(self.parent.main_shape_info) >= 1):
            btns.setup_btns(False)

    def draw(self):
        r,g,b,a = self.col[0], self.col[1], self.col[2], self.col[3]
        self.canvas.add(Color(r,g,b,a))
        self.canvas.remove_group('shape')
        self.canvas.remove(self.shape)
        self.define_nodes()
        i = 0
        for i in range(len(self.c_coords)):
            self.canvas.add(self.canvas_nodes[i])
            i = i + 1
        self.define_edge()

        i = 0
        for i in range(len(self.c_coords)):
            self.canvas.add(self.canvas_edge[i])
            i = i + 1

    def define_nodes(self):

        self.canvas_nodes.clear()

        """define all the node canvas elements as a list"""

        i = 0
        for points in self.c_coords:
            x,y = points
            self.canvas_nodes[i] = Ellipse(
                size = self.nodesize,
                pos =  [x,y],
                group = 'shape',
                )
            i = i + 1

    def define_edge(self):
        """define an edge canvas elements"""
        self.canvas_edge.clear()
        i = 0
        xy = []
        for points in self.c_coords:
            xy.append(self.canvas_nodes[i].pos[0] + self.nodesize[0] / 2)
            xy.append(self.canvas_nodes[i].pos[1] + self.nodesize[1] / 2)
            i = i + 1

        test = []
        i = 0
        while i < (len(xy)):
            if i + 2 == len(xy):
                test.append((xy[-2], xy[-1],xy[0], xy[1]))
            else:
                test.append(tuple(xy[i:i+4]))
            i = i + 2

        i = 0
        for point in test:
            self.canvas_edge[i] =  Line(
                points =  test[i],
                joint = 'round',
                cap = 'round',
                width = 3,
                close = False,
                group = 'shape'
                )
            i = i + 1

    def on_touch_down(self, touch):
        i = 0
        if self.color_picker_button.collide_point(*touch.pos):
            self.change_color()
        if self.back_button.collide_point(*touch.pos):
            self.go_back()
        if self.background_color_picker_button.collide_point(*touch.pos):
            self.change_color_background()
        if self.help_button.collide_point(*touch.pos):
            self.help.open()
        else:
            for lines in self.canvas_edge:
                x,y = self.canvas_edge[i].points[0], self.canvas_edge[i].points[1]
                a = [x,y]
                x,y = self.canvas_edge[i].points[2], self.canvas_edge[i].points[3]
                b = [x,y]
                c = list(touch.pos)
                if angle(a,b,c) and not self.pressed:
                    points = [a,b]

                    self.shape.add(Color(1,0,0, .5))
                    self.canvas.add(self.shape)
                    self.highlight = Line(
                        points = points,
                        width = 5,
                        group = 'shape'
                        )
                    self.canvas.add(self.highlight)
                    self.pressed = True
                    self.index = i
                    self.edge = 1
                    break
                else:
                    if self.pressed:
                        self.pressed = False
                        self.canvas.children.remove(self.highlight)
                i = i + 1

            for key, value in self.canvas_nodes.items():
                if (value.pos[0] - self.nodesize[0]) <= touch.pos[0] <= (value.pos[0] + self.nodesize[0]):
                    if (value.pos[1] - self.nodesize[1]) <= touch.pos[1] <= (value.pos[1] + self.nodesize[1]):
                        touch.grab(self)
                        self.grabbed = self.canvas_nodes[key]
                        self.shape.add(Color(1,0,0, .5))
                        self.canvas.add(self.shape)
                        self.highlight = Ellipse(
                            size = self.nodesize,
                            pos =  self.canvas_nodes[key].pos,
                            group = 'shape'
                            )
                        if not self.pressed:
                            self.canvas.add(self.highlight)
                            self.pressed = True
                            self.index = key
                            self.edge = 0
                        elif self.pressed:
                            self.pressed = False
                            try:
                                self.canvas.children.remove(self.highlight)
                            except:
                                pass
                        return True

    def on_touch_move(self, touch):
        self.pressed = False
        if touch.grab_current is self:
            self.grabbed.pos = [touch.pos[0] - self.nodesize[0] / 2, touch.pos[1] - self.nodesize[1] / 2]
            self.canvas.remove_group('shape')
            self.canvas.remove(self.shape)
            r,g,b,a = self.col[0], self.col[1], self.col[2], self.col[3]
            self.canvas.add(Color(r,g,b,a))
            i = 0
            for i in range(len(self.c_coords)):
                self.canvas.add(self.canvas_nodes[i])
                i = i + 1
            self.define_edge()

            i = 0
            for i in range(len( self.c_coords)):
                self.canvas.add(self.canvas_edge[i])
                i = i + 1

            poly = []
            i = 0
            for xy in self.canvas_nodes:
                poly.append(self.canvas_nodes[i].pos)
                i = i + 1

            newply = Polygon(poly)
            newply = affinity.translate(newply, xoff= -size[0]/2.95, yoff= -size[1]/4)
            if self.xscale > self.yscale:
                newply = affinity.scale(newply, xfact= 1/self.yscale, yfact= 1/self.yscale)
            else:
                newply = affinity.scale(newply, xfact= 1/self.xscale, yfact= 1/self.xscale)
            #123 references to tessellation engine objects
            #self.parent.children[1].draw_polygons()
            self.parent.children[1].original_base_unit = newply
            self.parent.children[1].polygon = newply
            self.parent.children[1].reset(0)

        else:
            pass

    def on_touch_up(self, touch):
        temp = []
        i = 0
        for i in range(len(self.c_coords)):
            temp.append(self.canvas_nodes[i].pos)
            i = i + 1
        self.c_coords = temp
        if touch.grab_current is self:
            touch.ungrab(self)
            poly = []
            i = 0
            for xy in self.canvas_nodes:
                poly.append(self.canvas_nodes[i].pos)
                i = i + 1
            newply = Polygon(poly)
            newply = affinity.translate(newply, xoff= -size[0]/2.95, yoff= -size[1]/4)
            if self.xscale > self.yscale:
                newply = affinity.scale(newply, xfact= 1/self.yscale, yfact= 1/self.yscale)
            else:
                newply = affinity.scale(newply, xfact= 1/self.xscale, yfact= 1/self.xscale)
            self.parent.children[1].reset(0)
            self.parent.children[1].polygon = newply
            self.parent.children[1].base_unit = newply
            self.parent.children[1].tile_regular_polygon()
            if (self.parent.children[0] != None):
                new_shape_info = tr.identify_shape(newply)
                self.parent.main_shape_info = new_shape_info
                for state in self.saved_states:
                    new_shape_info.append(state)
                self.parent.children[1].shape_info = new_shape_info
                self.parent.remove_widget(self.parent.children[0])
                btns = ReccomendationButtons()
                self.parent.add_widget(btns)
                if (new_shape_info != None and len(new_shape_info) >= 1):
                    btns.setup_btns(False)
        else:
            pass

    def add_saved_state(self, polygon, typet, tf, polygons):
        self.saved_states.append((polygon,typet, tf, polygons,"s"))
        state = (polygon,typet, tf, polygons, "s")
        self.parent.main_shape_info.append(state)
        self.parent.remove_widget(self.parent.children[0])
        btns = ReccomendationButtons()
        self.parent.add_widget(btns)
        if (self.parent.main_shape_info != None and len(self.parent.main_shape_info) >= 1):
            btns.setup_btns(False)

    #handler for back button
    def go_back(self, *args):
        Window.clearcolor = (0,0,0,0)
        self.parent.parent.back_to_start()
        
    def change_color(self,*args):

        r,g,b,a = self.col[0], self.col[1], self.col[2], self.col[3]
        self.picker = ColorPicker(pos_hint={'center_x': .5, 'center_y': .5},
                                color = [r,g,b,a],
                                size_hint = (1,1))

        self.picker.add_widget(Button(text = 'Select',
                                  pos_hint = {'center_x': .76, 'y': -.02},
                                  size_hint = (.08, .08),
                                #   size = (100, 35),
                                  on_press = self.selected))

        self.edge_toggle = ToggleButton(text = 'Edge',
                                       pos_hint = {'center_x': .55, 'y': -.02},
                                       size_hint = (.08, .08),
                                    #    size = (100, 35),
                                       group = 'color',
                                       state = 'down')
        self.edge_toggle.bind(on_press = self.pressed_toggle_edge)
        self.picker.add_widget(self.edge_toggle)

        self.fill_toggle = ToggleButton(text = 'Fill',
                                            pos_hint = {'center_x': .63, 'y': -.02},
                                            size_hint = (.08, .08),
                                            # size = (100, 35),
                                            group = 'color')
        self.fill_toggle.bind(on_press = self.pressed_toggle_fill)
        self.picker.add_widget(self.fill_toggle)

        self.match_toggle = ToggleButton(text = 'Match',
                                            pos_hint = {'x': .8, 'y': -.02},
                                            size_hint = (.08, .08),
                                            # size = (100, 35),
                                            group = 'match')
        self.picker.add_widget(self.match_toggle)


        self.ColPop = Popup(title = "Choose Color",
                        size_hint = (.50, .50),
                        content = self.picker,
                        # size = (1500, 750),
                        auto_dismiss = True)

        self.ColPop.open()

    def change_color_background(self,*args):

        r,g,b,a = self.col_background[0], self.col_background[1], self.col_background[2], self.col_background[3]
        self.picker_background = ColorPicker(pos_hint={'center_x': .5, 'center_y': .5},
                                color = [r,g,b,a],
                                size_hint = (1, 1))

        self.picker_background.add_widget(Button(text = 'Select',
                                  pos_hint = {'center_x': .76, 'y': -.02},
                                  size_hint = (.08, .08),
                                #   size = (100, 35),
                                  on_press = self.background_selected))
        self.ColPop_background = Popup(title = "Choose Background",
                        size_hint = (.5, .5),
                        content = self.picker_background,
                        # size = (1500, 750),
                        auto_dismiss = True)

        self.ColPop_background.open()

    def selected(self, *args):
        self.ColPop.dismiss()

        if self.match_toggle.state == 'down':
            self.col = self.picker.color
            self.fill = self.col
            r,g,b,a = self.col[0], self.col[1], self.col[2], self.col[3]
            self.parent.children[1].stroke_color = [r,g,b,a]
            self.parent.children[1].fill_color = [r,g,b,a]
        elif self.edge_toggle.state == 'down':
            self.col = self.picker.color
            r,g,b,a = self.col[0], self.col[1], self.col[2], self.col[3]
            self.parent.children[1].stroke_color = [r,g,b,a]
        elif self.fill_toggle.state == 'down':
            self.fill = self.picker.color
            r,g,b,a = self.fill[0], self.fill[1], self.fill[2], self.fill[3]
            self.parent.children[1].fill_color = [r,g,b,a]
        elif self.fill_toggle != 'down' and self.edge_toggle != 'down':
            self.col = self.picker.color
            r,g,b,a = self.col[0], self.col[1], self.col[2], self.col[3]
            self.parent.children[1].stroke_color = [r,g,b,a]


        self.parent.children[1].draw_polygons()
        self.draw()

    def background_selected(self, *args):
        self.ColPop_background.dismiss()
        self.col_background = self.picker_background.color
        r,g,b,a = self.col_background[0], self.col_background[1], self.col_background[2], self.col_background[3]
        #print(self.parent.children[1])
        #self.parent.children[1].change_tessellation_bg_color(r,g,b,a)
        Window.clearcolor = (r, g, b, a)

    def pressed_toggle_edge(self, *args):
        self.picker.color = self.col

    def pressed_toggle_fill(self, *args):
        self.picker.color = self.fill