import tkinter as tk

import script as m

"""---------------------------------------------------------------------------------------------------------------------------------------------------"""

win = tk.Tk()
win.title('Sokoban')

"""---------------------------------------------------------------------------------------------------------------------------------------------------"""

class r:
    """Ensemble des constantes initiales"""
    
    rap = 50
    des = 20

    # couleurs

    fill_can    = 'lightgrey'
    fill_player = 'blue'
    ol_player   = 'cyan'
    fill_glass  = 'white'
    ol_glass    = 'black'
    fill_pack   = 'orange'
    ol_pack     = 'brown'
    fill_cross  = 'red'
    fill_wall   = 'grey'
    fill_brick  = 'lightgrey'

"""---------------------------------------------------------------------------------------------------------------------------------------------------"""

class new_can(tk.Canvas):
    """Nouveau Canvas"""

    def __init__(self,width=None,height=None):

        if width == None or height == None:
            self.width,self.height = len(m.gate())*r.rap,len(m.gate()[0])*r.rap
        else:
            self.width,self.height = width,height
        self.bg = r.fill_can
        
        tk.Canvas.__init__(self,win,width=self.width,height=self.height,bg=self.bg)

        self.grid()

        return

    def update_size(self,width=None,height=None):
        """Actualise la taille du Canvas"""

        if width == None or height == None:
            self.width,self.height = len(m.gate())*r.rap,len(m.gate()[0])*r.rap
        else:
            self.width,self.height = width,height
        self.config(width=self.width,height=self.height)

        return

    def move(self,obj,x,y):
        """Permet de bouger un objet OBJ_DESIGN dans le Canvas"""
        
        for k in obj:
            super(new_can,self).move(k,x,y)

        return

    def delete(self,obj):
        """Permet de détruire un objet OBJ_DESIGN dans le Canvas"""

        for k in obj:
            super(new_can,self).delete(k)

can = new_can()

"""---------------------------------------------------------------------------------------------------------------------------------------------------"""

class obj_design(list):
    """Permet la partie design du projet ; éléments d'un objet rentrés dans self, une liste"""

    def __init__(self,nb,x1,y1,x2,y2,c_ul=False,c_ur=False,c_dr=False,c_dl=False):

        list.__init__(self)

        if nb == -1: # personnage
            self.extend([
                can.create_oval(x1-r.des/4,y1-r.des/4,x2+r.des/4,y2+r.des/4,fill=r.fill_player,outline=r.ol_player,width=3),
                can.create_oval(x1+r.des*0.8-r.des/4,y1+r.des/1.5,x1+r.des*0.8+r.des/4,y1+r.des/4,outline=r.ol_glass,width=5),
                can.create_oval(x2-r.des*0.8-r.des/4,y1+r.des/1.5,x2-r.des*0.8+r.des/4,y1+r.des/4,outline=r.ol_glass,width=5),
                can.create_rectangle(x1+r.des*0.8,y1+r.des/1.5,x2-r.des*0.8,y1+r.des/4,outline=r.ol_glass,width=5),
                can.create_oval(x1+r.des*0.8-r.des/4,y1+r.des/1.5,x1+r.des*0.8+r.des/4,y1+r.des/4,fill=r.fill_glass,width=0),
                can.create_oval(x2-r.des*0.8-r.des/4,y1+r.des/1.5,x2-r.des*0.8+r.des/4,y1+r.des/4,fill=r.fill_glass,width=0),
                can.create_rectangle(x1+r.des*0.8,y1+r.des/1.5,x2-r.des*0.8,y1+r.des/4,fill=r.fill_glass,width=0)
                ])

        elif nb == 1: # caisse
            self.extend([
                #can.create_rectangle(x1+r.des,y1,x2-r.des,y2,outline=r.ol_pack,width=5),
                #can.create_rectangle(x1,y1+r.des,x2,y2-r.des,outline=r.ol_pack,width=5),
                #can.create_oval(x1,y1,x1+r.des*2,y1+r.des*2,outline=r.ol_pack,width=5),
                #can.create_oval(x2,y1,x2-r.des*2,y1+r.des*2,outline=r.ol_pack,width=5),
                #can.create_oval(x1,y2,x1+r.des*2,y2-r.des*2,outline=r.ol_pack,width=5),
                #can.create_oval(x2,y2,x2-r.des*2,y2-r.des*2,outline=r.ol_pack,width=5),
                can.create_rectangle(x1+r.des,y1,x2-r.des,y2,fill=r.fill_pack,width=0),
                can.create_rectangle(x1,y1+r.des,x2,y2-r.des,fill=r.fill_pack,width=0),
                can.create_oval(x1,y1,x1+r.des*2,y1+r.des*2,fill=r.fill_pack,width=0),
                can.create_oval(x2,y1,x2-r.des*2,y1+r.des*2,fill=r.fill_pack,width=0),
                can.create_oval(x1,y2,x1+r.des*2,y2-r.des*2,fill=r.fill_pack,width=0),
                can.create_oval(x2,y2,x2-r.des*2,y2-r.des*2,fill=r.fill_pack,width=0)
                ])

        elif nb == 2: # croix
            self.extend([
                can.create_line(x1+r.rap/4,y1+r.rap/4,x2-r.rap/4,y2-r.rap/4,width=5,fill=r.fill_cross),
                can.create_line(x2-r.rap/4,y1+r.rap/4,x1+r.rap/4,y2-r.rap/4,width=5,fill=r.fill_cross)
                ])

        elif nb == 3: # mur
            if c_ul:
                self.extend([
                    can.create_rectangle(x1,y1,x1+r.des,y1+r.des,fill=r.fill_wall,width=0)
                    ])
            if c_ur:
                self.extend([
                    can.create_rectangle(x2,y1,x2-r.des,y1+r.des,fill=r.fill_wall,width=0)
                    ])
            if c_dl:
                self.extend([
                    can.create_rectangle(x1,y2,x1+r.des,y2-r.des,fill=r.fill_wall,width=0)
                    ])
            if c_dr:
                self.extend([
                    can.create_rectangle(x2,y2,x2-r.des,y2-r.des,fill=r.fill_wall,width=0)
                    ])
            
            self.extend([
                
                can.create_rectangle(x1+r.des,y1,x2-r.des,y2,fill=r.fill_wall,width=0),
                can.create_rectangle(x1,y1+r.des,x2,y2-r.des,fill=r.fill_wall,width=0),
                can.create_oval(x1,y1,x1+r.des*2,y1+r.des*2,fill=r.fill_wall,width=0),
                can.create_oval(x2,y1,x2-r.des*2,y1+r.des*2,fill=r.fill_wall,width=0),
                can.create_oval(x1,y2,x1+r.des*2,y2-r.des*2,fill=r.fill_wall,width=0),
                can.create_oval(x2,y2,x2-r.des*2,y2-r.des*2,fill=r.fill_wall,width=0),
                
                can.create_line(x1,y1+r.rap/4,x2,y1+r.rap/4,fill=r.fill_brick,width=5),
                can.create_line(x1,y1+r.rap/2,x2,y2-r.rap/2,fill=r.fill_brick,width=5),
                can.create_line(x1,y2-r.rap/4,x2,y2-r.rap/4,fill=r.fill_brick,width=5),
                
                can.create_line(x1+r.rap/3,y1,x1+r.rap/3,y1+r.rap/4,fill=r.fill_brick,width=5),
                can.create_line(x1+r.rap/1.5,y1,x1+r.rap/1.5,y1+r.rap/4,fill=r.fill_brick,width=5),
                
                can.create_line(x1+r.rap/6,y1+r.rap/4,x1+r.rap/6,y1+r.rap/2,fill=r.fill_brick,width=5),
                can.create_line(x2-r.rap/6,y1+r.rap/4,x2-r.rap/6,y1+r.rap/2,fill=r.fill_brick,width=5),
                
                can.create_line(x1+r.rap/3,y2-r.rap/2,x1+r.rap/3,y2-r.rap/4,fill=r.fill_brick,width=5),
                can.create_line(x1+r.rap/1.5,y2-r.rap/2,x1+r.rap/1.5,y2-r.rap/4,fill=r.fill_brick,width=5),

                can.create_line(x1+r.rap/6,y2-r.rap/4,x1+r.rap/6,y2,fill=r.fill_brick,width=5),
                can.create_line(x2-r.rap/6,y2-r.rap/4,x2-r.rap/6,y2,fill=r.fill_brick,width=5)

                ])
            
            if c_ul or c_ur:
                self.extend([
                    can.create_line(x1,y1,x2,y1,fill=r.fill_brick,width=5)
                    ])
            if c_dl or c_dr:
                self.extend([
                    can.create_line(x1,y2,x2,y2,fill=r.fill_brick,width=5)
                    ])

        return

"""---------------------------------------------------------------------------------------------------------------------------------------------------"""

class player(m.player):

    def __init__(self,x=None,y=None):

        if x == None or y == None:
            m.player.__init__(self)
        else:
            m.player.__init__(self,x,y)

        t = self.get_coords()

        self.view = obj_design(-1,t[0],t[1],t[2],t[3])

        return

    def get_coords(self):
        """Renvoie les coordonnées pour CAN d'un joueur"""
        
        return (self.x*r.rap+r.rap/4,self.y*r.rap+r.rap/4,(self.x+1)*r.rap-r.rap/4,(self.y+1)*r.rap-r.rap/4)

    def move(self,event):
        """Actualise la position d'un joueur - extension de la fonction initiale"""
        
        t = self.get_coords()
        can.move(self.view,-t[0],-t[1])
        
        f = super(player,self).move(event)
        
        t = self.get_coords()
        can.move(self.view,t[0],t[1])

        if not -1 in f[1]:
            e = gateV[self.x][self.y]
            can.move(e,-self.x*r.rap,-self.y*r.rap)
            can.move(e,f[1][0]*r.rap,f[1][1]*r.rap)
            gateV[f[1][0]][f[1][1]] = e
            gateV[self.x][self.y] = self.view

        return

    def move_all(cls,event):
        """Actualise la position de tous les joueurs et, parfois, des caisses"""

        if next_level:
            for k in cls.ALL:
                k.move(event)

        return

    move_all = classmethod(move_all)
            
    def new_view(self):
        """Génère la nouvelle valeur de self.view après, souvent, sa destruction durant actualize()"""
        
        can.delete(self.view)
        t = self.get_coords()
        self.view = obj_design(-1,t[0],t[1],t[2],t[3])

        return

player()

"""---------------------------------------------------------------------------------------------------------------------------------------------------"""

gate = m.gate()
gateV = [[]]
gateVC = [[]]

def actualize():
    """Regénère les grilles"""
    
    global gate,gateV,gateVC

    can.update()
    can.update_size()

    for k1 in gateV:
        for k2 in k1:
            try:
                can.delete(k2)
            except Exception:
                pass
    for k1 in gateVC:
        for k2 in k1:
            try:
                can.delete(k2)
            except Exception:
                pass

    gate = m.gate()
    gateC = m.gateC()
    gateV = [[None for k2 in k1] for k1 in gate]
    gateVC = [[None for k2 in k1] for k1 in gate]
    p_t = [k.view for k in player.ALL]

    for k1 in range(len(gate)):
        for k2 in range(len(gate[k1])):
            e = gate[k1][k2]
            if e == -1:
                gateV[k1][k2] = p_t.pop(0)
            elif e == 1:
                gateV[k1][k2] = obj_design(1,k1*r.rap,k2*r.rap,(k1+1)*r.rap,(k2+1)*r.rap)
            elif e == 3:
                up = m.gate()[k1][k2-1] == 3 and k2-1 >= 0
                try:
                    do = m.gate()[k1][k2+1] == 3
                except Exception:
                    do = False
                try:
                    ri = m.gate()[k1+1][k2] == 3
                except Exception:
                    ri = False
                le = m.gate()[k1-1][k2] == 3 and k1-1 >= 0
                c_ul = up or le
                c_ur = up or ri
                c_dl = do or le
                c_dr = do or ri
                gateV[k1][k2] = obj_design(3,k1*r.rap,k2*r.rap,(k1+1)*r.rap,(k2+1)*r.rap,c_ul=c_ul,c_ur=c_ur,c_dl=c_dl,c_dr=c_dr)
    for k1 in range(len(gateC)):
        for k2 in range(len(gateC[k1])):
            if gateC[k1][k2][1]:
                gateVC[k1][k2] = obj_design(2,k1*r.rap,k2*r.rap,(k1+1)*r.rap,(k2+1)*r.rap)

    for k in player.ALL:
        k.new_view()

    return

actualize()

"""---------------------------------------------------------------------------------------------------------------------------------------------------"""

def restart(event=None):
    """Fait le niveau actuel recommencer en complément du module"""

    for k in player.ALL:
        can.delete(k.view)
    
    m.restart()

    for k in player.ALL:
        can.delete(k.view)
    
    cur = m.get_cur()
    levels = m.levels_save
    for k1 in levels[cur]:
        for k2 in k1:
            if k2 == -1:
                player()

    tx1 = [[k]*levels[cur][k].count(-1) for k in range(len(levels[cur])) if -1 in levels[cur][k]]
    tx2 = []
    for k1 in levels[cur]:
        t = [k2 for k2 in range(len(k1)) if k1[k2] == -1]
        if len(t) > 0:
            tx2.append(t)
    x1,x2 = [],[]
    for k in tx1:
        x1 += k
    for k in tx2:
        x2 += k

    for k in range(len(player.ALL)):
        player.ALL[k].repos(x1[k],x2[k])
        player.ALL[k].new_view()
                
    actualize()

    return

"""---------------------------------------------------------------------------------------------------------------------------------------------------"""

def keep(event=None):
    """Permet de passer d'un niveau à un autre"""

    global next_level

    if not next_level:
        next_level = True

    return

"""---------------------------------------------------------------------------------------------------------------------------------------------------"""

win.bind('<Up>',player.move_all)
win.bind('<Down>',player.move_all)
win.bind('<Right>',player.move_all)
win.bind('<Left>',player.move_all)

win.bind('<r>',restart)

win.bind('<Button-1>',keep)

end = False
next_level = True

while not end:

    try:
        win.update()
    except Exception:
        end = True
        try:
            win.destroy()
        except Exception:
            pass
    
    if m.check_end(want_ins=False):

        next_level = False

        while not next_level:
            try:
                win.update()
            except Exception:
                end = True
                try:
                    win.destroy()
                except Exception:
                    pass

        m.new_level()
        cur = m.get_cur()
        levels = m.get_levels()
        for k1 in levels[cur]:
            for k2 in k1:
                if k2 == -1:
                    player()

        tx1 = [[k]*levels[cur][k].count(-1) for k in range(len(levels[cur])) if -1 in levels[cur][k]]
        tx2 = []
        for k1 in levels[cur]:
            t = [k2 for k2 in range(len(k1)) if k1[k2] == -1]
            if len(t) > 0:
                tx2.append(t)
        x1,x2 = [],[]
        for k in tx1:
            x1 += k
        for k in tx2:
            x2 += k

        for k in range(len(player.ALL)):
            player.ALL[k].repos(x1[k],x2[k])
            player.ALL[k].new_view()
        
        actualize()

try:
    win.mainloop()
except Exception:
    pass
