from tkinter import *
from random import *
import math

nbrenv=0 #nombre de renversement de la pile de crêpes
nbcrp=10 # nombre de crêpes
pmax=nbcrp #position de la crêpe de taille max (utilisée dans la fonction déplacement)
lf=800 #largeur de la fenêtre tkinter
hf=500 #hauteur de la fenêtre tkinter
lcp=[] #liste des crêpes non correctement placée (en rouge)
lcpep=[] #liste des crêpes bien placées (en vert)
lr=[] #liste de tous les rectangles représentant les crêpes
presAuto = False #utile si je veux que la présentation soit automatique ou réponde à un clic

ddpl=800 #temps mis entre deux actions dans le cas où on lance le programme en mode automatique
pauseColoration = 1000 #temps de latence pour montrer la crêpe qui sera retournée (cas de figure N°2)

#################Lancement de la fenêtre tkinter
fen = Tk()
cadre = Frame(fen, width =lf, height =hf, bg="light yellow")
cadre.pack()
c=Canvas(cadre, height=hf, width=lf, bg="white")
c.place(x=0, y=0)



###########
###########   Fonctions utilisées pour le premier algo (on met la plus grande crêpe au sommet)
###########
def deplacement1():
    """cette fonction renverse tout le paquet de crêpes car la crêpe située au-dessus est la plus grande"""
    global nbrenv,ddpl,longEnHaut,presAuto
    deleterect ()
    for i in range (len(lcpep)):
        lr.append(c.create_rectangle(((lf-lcpep[i])//2,hf-20-20*i,(lf+lcpep[i])//2,hf-20-20*(i+1)), outline="blue", fill="green", width=2))
    for i in range (len(lcp)):
        lr.append(c.create_rectangle(((lf-lcp[i])//2,hf-20-20*(i+len(lcpep)),(lf+lcp[i])//2,hf-20-20*(i+len(lcpep)+1)), outline="blue", fill="red", width=2))
    nbrenv=nbrenv+1
    lcp.reverse()
    lcpep.append(lcp[0])
    del lcp[0]
    longEnHaut = False
    if presAuto:
        fen.after(ddpl,deplacement)

def deplacement():
    """cette fonction identifie la crêpe la plus grande parmi celles qui restent et la renverse avec celles qui sont au-dessus
    de sorte que la plus grande se retrouve au sommet de la pile"""
    global nbrenv,ddpl,longEnHaut,presAuto,btnEtape ##longEnHaut sert à savoir si la plus grande crêpes (parmi celles qui restent à trier) est en haut
    deleterect()
    for i in range (len(lcpep)):
        lr.append(c.create_rectangle(((lf-lcpep[i])//2,hf-20-20*i,(lf+lcpep[i])//2,hf-20-20*(i+1)), outline="blue", fill="green", width=2,tag="leRectangle"))
    for i in range (len(lcp)):
        lr.append(c.create_rectangle(((lf-lcp[i])//2,hf-20-20*(i+len(lcpep)),(lf+lcp[i])//2,hf-20-20*(i+len(lcpep)+1)), outline="blue", fill="red", width=2))
    
    if len(lcp)!=0:
        pmax=lcp.index(max(lcp))
        chgtCouleurRect(c,lr[pmax+len(lcpep)],"blue")
        if pmax!=0:
            nbrenv=nbrenv+1
            a=lcp[pmax:]
            a.reverse()
            for i in range(pmax,len(lcp)):
                lcp[i]=a[i-pmax]
            longEnHaut = True
            if presAuto:
                fen.after(ddpl,deplacement1)
        else:
            lcpep.append(lcp[0])
            del lcp[0]
            if presAuto:
                fen.after(ddpl,deplacement)
            longEnHaut = False
    else:
        if not presAuto :
            btnEtape.destroy()
        textper="nombre de renversement "+str(nbrenv)+" pour "+str(nbcrp)+" crêpes."
        lbtext= Label( fen, text=textper, relief=RAISED )
        lbtext.pack()
        print ("fin")






###########
###########   Cette autre partie est pour la deuxième méthode de retournement des crêpes
###########
def indiceSupInf(l,nb):
    """l est une liste de nombres
    nb est un nombre
    cette fonction renvoie l'indice du plus petit nombre strictement supérieur ou inférieur à nb
    si nb est le max, alors elle renvoie inf pour val max et si nb est la valeur min, elle renvoie -1 pour min"""
    indMin,indMax=-1,-1
    nbMax,nbMin = float('inf'),-1
    for i in range(len(l)):
        if(nb<l[i]<nbMax):
            nbMax,indMax=l[i],i
        elif(nbMin<l[i]<nb):
            nbMin,indMin=l[i],i
    return indMax,indMin


def deplacementBis():
    """cette fonction identifie la crêpe à retourner suivant 3 critères consécutifs (corrigés pour perdre l'avantage de ce qui aurait été fait)"""
    global nbrenv,ddpl,longEnHaut,btnEtape,pas,jeuLance ##longEnHaut sert à savoir si la plus grande crêpes (parmi celles qui restent à trier) est en haut
    deleterect()
    for i in range (len(lcpep)):
        lr.append(c.create_rectangle(((lf-lcpep[i])//2,hf-20-20*i,(lf+lcpep[i])//2,hf-20-20*(i+1)), outline="blue", fill="green", width=2))
    for i in range (len(lcp)):
        lr.append(c.create_rectangle(((lf-lcp[i])//2,hf-20-20*(i+len(lcpep)),(lf+lcp[i])//2,hf-20-20*(i+len(lcpep)+1)), outline="blue", fill="red", width=2))
    #print("lcp:",lcp)
    if jeuLance:
        if len(lcp)!=0:
            #On cherche l'indice à partir duquel on va renverser la pile
            pmax,pmin = indiceSupInf(lcp,lcp[-1])
            indiceRetournement = -1
            if pmax==-1:
                #C'est que la plus grande des crêpes à replacer est en haut (donc on retrounera la pile)
                indiceRetournement = len(lcp)
            elif pmax!=len(lcp)-2:
                #la crêpe juste plus grande que celle du sommet n'est pas en position 2, alors on se place juste au-dessus de cette plus grande crêpe
                #On évite tout de même de faire ceci si cela sépare deux crêpes qui doivent être l'une sur l'autre à la fin
                if(abs(lcp[pmax+1]-lcp[pmax]-pas)<math.pow(10,-7)):
                    #On choisit autre chose dans ce cas là
                    #On regarde alors du côté du min avec la même mise en garde
                    if((abs(lcp[pmin]-lcp[pmin+1]-pas)<math.pow(10,-7)) or (pmin==len(lcp)-2)):
                        #je choisis donc de prendre la plus grande crêpe
                        indiceRetournement = lcp.index(max(lcp))
                    else :
                        indiceRetournement = pmin+1
                else :
                    indiceRetournement = pmax+1
            else :
                #la crêpe située en position 2 sur la pile est dans ce cas juste celle qui est plus grande que celle du sommet,
                #donc on cherche la position de la crêpe juste plus petite que celle du sommet et on effectue le retournement au-dessus de cette dernière
                #Je prends les même dispositions que pour le pmax
                if(abs(lcp[pmin]-lcp[pmin+1]-pas)<math.pow(10,-7)):
                    #je choisis donc de prendre la plus grande crêpe
                    indiceRetournement = lcp.index(max(lcp))
                elif pmin==-1:
                    #dans ce cas aussi je prends la plus grande crêpe que je mets dessus
                    indiceRetournement = lcp.index(max(lcp))
                else :
                    indiceRetournement = pmin+1
            #je mets en couleur la crêpe à retourner
            if(indiceRetournement < len(lcp)):
                chgtCouleurRect(c,lr[indiceRetournement+len(lcpep)],"blue")
            if not presAuto :
                btnEtape['state']=DISABLED
            if indiceRetournement!=len(lcp):#c'est que celle du dessus n'est pas la plus grande des crêpes restantes
                nbrenv=nbrenv+1
                a=lcp[indiceRetournement:]
                a.reverse()
                for i in range(indiceRetournement,len(lcp)):
                    lcp[i]=a[i-indiceRetournement]
                longEnHaut = False
                fen.after(pauseColoration,deplacementBis1)
            else:
                lcp.reverse()
                while (len(lcp)>1 and abs(lcp[0]-lcp[1]-pas)<math.pow(10,-8)):
                    lcpep.append(lcp[0])
                    del lcp[0]
                lcpep.append(lcp[0])
                del lcp[0]
                nbrenv=nbrenv+1
                fen.after(pauseColoration,deplacementBis1)
        else:
            if not presAuto :
                btnEtape.destroy()
            textper="nombre de renversement "+str(nbrenv)+" pour "+str(nbcrp)+" crêpes."
            lbtext= Label( fen, text=textper, relief=RAISED )
            lbtext.pack()
            print ("fin")
    else :
        jeuLance = True
        #Si des crêpes sont bien placées, alors on les décompte
        if(lcp[0]==max(lcp)):
            while (len(lcp)>1 and abs(lcp[0]-lcp[1]-pas)<math.pow(10,-8)):
                lcpep.append(lcp[0])
                del lcp[0]
            lcpep.append(lcp[0])
            del lcp[0]
        if presAuto:
            fen.after(ddpl,deplacementBis)

def deplacementBis1():
    """cette fonction recrée le tas de crêpes avec les bonnes couleurs"""
    global nbrenv,ddpl,longEnHaut,presAuto,valeurs,indiceRetournement
    deleterect ()
    for i in range (len(lcpep)):
        lr.append(c.create_rectangle(((lf-lcpep[i])//2,hf-20-20*i,(lf+lcpep[i])//2,hf-20-20*(i+1)), outline="blue", fill="green", width=2))
    for i in range (len(lcp)):
        lr.append(c.create_rectangle(((lf-lcp[i])//2,hf-20-20*(i+len(lcpep)),(lf+lcp[i])//2,hf-20-20*(i+len(lcpep)+1)), outline="blue", fill="red", width=2))
    longEnHaut = False
    if presAuto:
        fen.after(ddpl,deplacementBis)
    else :
        btnEtape['state']='normal'





###########
###########   Fonctions utilisées dans les deux cas de figure
###########
def deleterect():
    global lr
    for rect in lr:
        c.delete(rect)
    lr = []
    
def choixDeplacement():
    """cette fonction est appelée à chaque clic sur le bouton, dans le cas d'une présentation manuelle"""
    global longEnHaut
    if(typePresentation == valeurs[0]):
        if(longEnHaut):
            deplacement1()
        else :
            deplacement()
    else :
        deplacementBis()


def chgtCouleurRect(can,rect,couleur):
    """le rectangle du canvas est changé de couleur"""
    can.itemconfig(rect,fill=couleur)
    can.update()


def automatique():
    """cette fonction lance le classement automatique des crêpes"""
    global presAuto,varGr,valeurs,typePresentation
    presAuto = True
    typePresentation = varGr.get()
    if(typePresentation == valeurs[0]):
        deplacement()
    else :
        deplacementBis()
    frame.destroy()

def manuelle():
    """cette fonction lance le classement manuel des crêpes, c'est-à-dire à l'aide du bouton"""
    global varGr,valeurs,btnEtape,typePresentation
    btnEtape = Button(fen,text="Etape suivante",command=choixDeplacement)
    btnEtape.pack()
    typePresentation = varGr.get()
    if(typePresentation == valeurs[0]):
        deplacement()
    else :
        deplacementBis()
    frame.destroy()



###################################Variables pour le lancement et la construction de la zone
jeuLance = False
pas = 50
if(nbcrp>11):
    pas = (lf-200-50)//(nbcrp-1)
for i in range(nbcrp):
    lcp.append(50+pas*i)
shuffle(lcp)

longEnHaut = False

######################Début de la construction de la frame tkinter
frame = Frame(fen)
frame.pack()
valeurs =[1,2]
etiquettes = ["sélection de la plus grande crêpe","sélection en lien avec la crêpe du dessus"]
varGr = IntVar()
varGr.set(valeurs[0])
typePresentation = -1#cette variable me servira à mettre le contenu de varGr (qui disparaitra quand les bouons ne seront plus présents)
radiob = []
for i in range(2):
    radiob.append(Radiobutton(frame, variable=varGr, text=etiquettes[i], value=valeurs[i]))
    radiob[i].grid(row=i,column=0)

btnPresentation = Button(frame,text="Choix d'une présentation automatique",command=automatique)
btnPresentation.grid(row=0,column=1)

btnManuel = Button(frame,text="Choix d'une présentation manuelle",command=manuelle)
btnManuel.grid(row=1,column=1)

fen.mainloop()
