from tkinter import *
import math

class Window(Frame):
    def __init__(self, width=500, height=500, background='white', title='Animation', master=None):
        self.XMin = 0
        self.YMin = 0
        self.XMax = width
        self.YMax = height
        self._im = {}
        self._root = master
        if not self._root: self._root = Tk()
        Frame.__init__(self, master=self._root)
        self.pack()
        self._root.wm_title(title)
        self._can = Canvas(self, width=width, height=height, background=background)
        self._can.pack()

    def run(self):
        self._root.mainloop()
    def finish(self):
        self.quit()
        self._root.destroy()

    def _mkCB(self, f):
        def g(event):
            self.lastEvent = event
            f()
        return g
    def _mkAnimate(self, f):
        def g():
            f()
            self._root.after(50, g)
        return g

    def on(self, key, f):
        self._root.bind(key, self._mkCB(f))
    def animate(self, f):
        self._root.after(50, self._mkAnimate(f))

    def line(self, x1, y1, x2, y2, color='black'):
        return self._can.create_line(x1, self.YMax-y1, x2, self.YMax-y2, fill=color)
    def circle(self, x, y, radius, border='black', color='red'):
        return self._can.create_oval(x-radius, self.YMax-(y-radius), x+radius, self.YMax-(y+radius), outline=border, fill=color)
    def rectangle(self, x1, y1, x2, y2, border='black', color='red'):
        return self._can.create_rectangle(x1, self.YMax-y1, x2, self.YMax-y2, outline=border, fill=color)
    def gif(self, x, y, file):
        im = PhotoImage(file=file)
        id = self._can.create_image(x, self.YMax-y, image=im)
        self._im[id] = im
        return id

    def _center(self, id):
        try:
            x1, y1, x2, y2 = self._can.coords(id)
            x = (x1+x2)/2.0
            y = (y1+y2)/2.0
            return (x, y)
        except:
            try:
                x, y = self._can.coords(id)
                return (x, y)
            except:
                x1, y1, x2, y2, x3, y3 = self._can.coords(id)
                x = (x1+x2+x3)/3.0
                y = (y1+y2+y3)/3.0
                return (x, y)
    def center(self, id):
        x, y = self._center(id)
        return (x, self.YMax-y)
    def radius(self, id):
        try:
            x1, y1, x2, y2 = self._can.coords(id)
            return (x2-x1)/2.0
        except:
            try:
                im = self._im[id]
                return im.width()/2.0
            except:
                x1, y1, x2, y2, x3, y3 = self._can.coords(id)
                x = (x1+x2+x3)/3.0
                y = (y1+y2+y3)/3.0
                return math.sqrt((x1-x)**2 + (y1-y)**2)
                
    def upperLeft(self, id):
        try:
            x1, y1, x2, y2 = self._can.coords(id)
            return (x1, self.YMax-y1)
        except:
            try:
                x, y = self._can.coords(id)
                im = self._im[id]
                return (x-im.width()/2.0, self.YMax-(y-im.height()/2.0))
            except:
                x1, y1, x2, y2, x3, y3 = self._can.coords(id)
                return (min([x1, x2, x3]), self.YMax-min([y1, y2, y3]))
    def lowerRight(self, id):
        try:
            x1, y1, x2, y2 = self._can.coords(id)
            return (x2, self.YMax-y2)
        except:
            try:
                x, y = self._can.coords(id)
                im = self._im[id]
                return (x+im.width()/2.0, self.YMax-(y+im.height()/2.0))
            except:
                x1, y1, x2, y2, x3, y3 = self._can.coords(id)
                return (max([x1, x2, x3]), self.YMax-max([y1, y2, y3]))
    def width(self, id):
        try:
            x1, y1, x2, y2 = self._can.coords(id)
            return x2-x1
        except:
            try:
                im = self._im[id]
                return im.width()
            except:
                x1, y1, x2, y2, x3, y3 = self._can.coords(id)
                l = min([x1, x2, x3])
                r = max([x1, x2, x3])
                return r-l
    def height(self, id):
        try:
            x1, y1, x2, y2 = self._can.coords(id)
            return y2-y1
        except:
            try:
                im = self._im[id]
                return im.height()
            except:
                x1, y1, x2, y2, x3, y3 = self._can.coords(id)
                b = min([y1, y2, y3])
                t = max([y1, y2, y3])
                return t-b

    def move(self, id, deltaX, deltaY):
        try:
            x1, y1, x2, y2 = self._can.coords(id)
            self._can.coords(id, x1+deltaX, y1-deltaY, x2+deltaX, y2-deltaY)
        except:
            try:
                x, y = self._can.coords(id)
                self._can.coords(id, x+deltaX, y-deltaY)
            except:
                x1, y1, x2, y2, x3, y3 = self._can.coords(id)
                self._can.coords(id, x1+deltaX, y1-deltaY, x2+deltaX, y2-deltaY, x3+deltaX, y3-deltaY)
    def moveTo(self, id, x, y):
        ox, oy = self.center(id)
        self.move(id, x-ox, y-oy)
    def moveBoxTo(self, id, x1, y1, x2, y2):
        try:
            self._can.coords(id, x1, self.YMax-y1, x2, self.YMax-y2)
        except:
            print("Does not apply to GIF or triangle.")
    def setRadius(self, id, r):
        try:
            x, y = self._center(id)
            self._can.coords(id, x-r, y-r, x+r, y+r)
        except:
            print("Does not apply to GIF or triangle.")
    def changeRadius(self, id, deltaR):
        self.setRadius(id, self.radius(id)+deltaR)

    def triangle(self, x, y, r, border='black', color='yellow'):
        r = round(r)
        return self._can.create_polygon(x+r, self.YMax-y,
                                        x+r*math.cos(2*math.pi/3), self.YMax-(y+r*math.sin(2*math.pi/3)),
                                        x+r*math.cos(2*math.pi/3), self.YMax-(y+r*math.sin(4*math.pi/3)),
                                        outline=border, fill=color)
    def setAngle(self, id, angle):
        try:
            x, y = self._center(id)
            r = round(self.radius(id))
            angle = -angle
            self._can.coords(id,
                             x+r*math.cos(angle), y+r*math.sin(angle),
                             x+r*math.cos(angle+2*math.pi/3), y+r*math.sin(angle+2*math.pi/3),
                             x+r*math.cos(angle-2*math.pi/3), y+r*math.sin(angle-2*math.pi/3))
        except:
            print("Only applies to triangle.")
    def angle(self, id):
        try:
            x, y = self._center(id)
            x1, y1, x2, y2, x3, y3 = self._can.coords(id)
            return -math.atan2(y1-y, x1-x)
        except:
            print("Only applies to triangle.")

    def hide(self, id):
        self._can.itemconfig(id, state=HIDDEN)
    def show(self, id):
        self._can.itemconfig(id, state=NORMAL)
    def setColor(self, id, color):
        self._can.itemconfig(id, fill=color)
    def delete(self, id):
        self._can.delete(id)
