import random # Pour les tirages aleatoires
import pygame # Le module Pygame
import pygame.freetype # Pour afficher du texte
import math
pygame.init() # initialisation de Pygame

# Taille de la fenetre
width = 800
height = 600
screen = pygame.display.set_mode((width,height))
pygame.display.set_caption("Trie les valeurs")

# Definition des couleurs
black = (0, 0, 0)
white = (255, 255, 255)
blue = (180, 180, 255)
yellow = (255, 255, 0)
redish = (255, 200, 200)
grey = (120, 120, 120)

# Pour le texte.
pygame.freetype.init()
taille_texte = 50
myfont=pygame.freetype.SysFont(None, taille_texte)

# taille d'un bloc
taille_rect = 40

# Pour controler le nombre d'images par seconde
clock=pygame.time.Clock()


# Pour afficher le texte sur les blocs
# Il y a plus simple, mais bon...
# 0 -> A, 1 -> B...
def nom_bloc(nb):
    return chr(65+nb)


# Afficher un bloc
# pos : coin supérieur gauche
def afficher_bloc(pos, nb, actif=False, couleur1=blue, couleur2=yellow):
    x, y = pos
    if actif: # Choix de la couleur
        couleur = couleur2
    else:
        couleur = couleur1
    # On fait un rectangle pour le fond et un autre pour le contour
    pygame.draw.rect(screen,couleur,(x,y,taille_rect,taille_rect),0)
    pygame.draw.rect(screen,black,(x,y,taille_rect,taille_rect),3)
    # On affiche le texte
    texte, rect = myfont.render(nom_bloc(nb), black, size = 20)
    rect.center = (x+taille_rect//2, y+taille_rect//2)
    screen.blit(texte,rect)


# Affiche les emplacements pour placer les blocs pour la solution
def afficher_position_solutions(positions_solutions):
    x_p = 0
    for i, pos in enumerate(positions_solutions):
        x, y = pos
        pygame.draw.rect(screen,(200, 200, 200),(x,y,taille_rect,taille_rect),0)
        pygame.draw.rect(screen,black,(x,y,taille_rect,taille_rect),3)
        if i > 0: # On met les comparateurs entre chaque bloc
            texte, rect = myfont.render("<", black, size = 20)
            rect.center = ((x_p+taille_rect+x)//2, y+taille_rect//2)
            screen.blit(texte,rect)
        x_p = x


# Affiche la solution si on le demande
def afficher_solutions(rectangles_solutions, cases_solutions, solutions):
    x_p = 0
    for i, pos in enumerate(rectangles_solutions):
        x, y = pos
        # On choisit la couleur pour indiquer si le bloc placé est le
        # bon ou pas
        afficher_bloc(pos, solutions[i], solutions[i] == cases_solutions[i], (255, 150, 150), (150, 255, 150))
        if i > 0:
            texte, rect = myfont.render("<", black, size = 20)
            rect.center = ((x_p+taille_rect+x)//2, y+taille_rect//2)
            screen.blit(texte,rect)
        x_p = x


# Affiche un bouton
# etat : 0 -> desactive, 1 -> actif, 2 -> hover
def afficher_bouton(pos, dimension, nom, etat=1):
    if etat == 0:
        couleur_fond = (220, 220, 220)
        couleur_bord = (150, 150, 150)
    elif etat == 1:
        couleur_fond = (255, 255, 255)
        couleur_bord = (0, 0, 0)
    else:
        couleur_fond = (200, 255, 200)
        couleur_bord = (0, 0, 0)
    x, y = pos
    largeur, hauteur = dimension
    # On fait un rectangle pour le fond et un autre pour le contour
    pygame.draw.rect(screen, couleur_fond, (x, y, largeur, hauteur),0)
    pygame.draw.rect(screen, couleur_bord, (x, y, largeur, hauteur),3)
    # On met le texte s'il y en a
    if nom != "":
        texte, rect = myfont.render(nom, couleur_bord, size = 20)
        rect.center = (x+largeur//2, y+hauteur//2)
        screen.blit(texte,rect)

# Afficher les infos sur les comparaisons et le résultat de la dernière
# comparaison
def afficher_texte_comparaison(texte, compar, bouton):
    pos, dimension = bouton[:2]
    x_t, y_t = pos
    l, h = dimension
    # Le résultat de la dernière comparaison
    if texte:
        texte, rect = myfont.render(texte, black, size = 20)
        rect.center = (x_t+l//2, y_t-h//2-10)
        screen.blit(texte,rect)
    # Le nombre de comparaisons déjà effectuées
    texte2 = "Nb comparaisons : "+str(compar)
    texte, rect = myfont.render(texte2, black, size = 20)
    rect.midleft = (x_t+l+15, y_t+h//2)
    screen.blit(texte,rect)


# Permet d'afficher le réglage pour choisir le nombre de blocs
def afficher_choix_nombre(nb_rect, boutons_haut_bas):
    texte = f"Nombre de blocs : {nb_rect:2}"
    x, y = boutons_haut_bas[1][0]
    texte, rect = myfont.render(texte, black, size = 20)
    rect.midright = (x-10, y)
    screen.blit(texte,rect)
    # On place les 2 boutons
    for i, bouton in enumerate(boutons_haut_bas):
        pos, dimension, nom, etat = bouton
        afficher_bouton(pos, dimension, nom, etat)
        if etat == 0:
            couleur_bord = (150, 150, 150)
        else:
            couleur_bord = (0, 0, 0)
        x, y = pos
        l, h = dimension
        if i == 0:
            pygame.draw.lines(screen,couleur_bord,False,[(x,y+h),(x+l//2,y),(x+l,y+h)],3)
        else:
            pygame.draw.lines(screen,couleur_bord,False,[(x,y),(x+l//2,y+h),(x+l,y)],3)


# Affiche si on a gagné ou pas
def afficher_texte_victoire(victoire, nb_comparaisons):
    x_t, y_t = width//2, limite1+100
    if victoire == 1:
        texte = f"Bravo, vous avez trouvé en {nb_comparaisons} comparaisons !"
        couleur = (0, 150, 0)
    else:
        texte = "Non ce n'est pas correct."
        couleur = (150, 0, 0)
    texte, rect = myfont.render(texte, couleur, size = 20)
    rect.center = (x_t, y_t)
    screen.blit(texte,rect)


# Détermine sur quel bloc se trouve la souris
def trouver_actif(x, y, rects):
    for i in range(len(rects)):
        liste = rects[i]
        for j, rect in enumerate(liste):
            xr, yr = rect[0]
            if xr<=x<xr+taille_rect and yr<=y<yr+taille_rect:
                return i, j
    return -1, -1


# Détermine si la souris est sur le bouton
def dans_bouton(x, y, bouton):
    x_b, y_b = bouton[0]
    l, h = bouton[1]
    if x_b <= x < x_b+l and y_b <= y < y_b+h:
        return True
    return False


# Génère les valeurs à trouver.
# On les mélange au hasard.
def generer_valeurs(nb_valeurs):
    valeurs = list(range(nb_valeurs))
    random.shuffle(valeurs)
    val_pos = [(valeurs[i], i) for i in range(nb_valeurs)]
    val_pos.sort()
    solutions = [j for (i, j) in val_pos]
    #print(valeurs, solutions)
    return valeurs, solutions


# Détermine l'écart à mettre entre 2 blocs
def determiner_ecart(nb_valeurs):
    if (2*nb_valeurs-1)*taille_rect + 20 < width:
        return 2*taille_rect
    else:
        val = round((width-20-nb_valeurs*taille_rect)/(nb_valeurs-1))
        #print(val, 20+nb_valeurs*taille_rect + (nb_valeurs-1)*val)
        return taille_rect+val

# Affiche les messages d'aide
def afficher_infos():
    couleur = (100, 170, 100)
    texte, rect = myfont.render("Sélectionnez 2 blocs", couleur, size = 20)
    rect.midleft = (500, 100)
    screen.blit(texte,rect)
    texte, rect = myfont.render("et cliquez sur \"Comparer\"", couleur, size = 20)
    rect.midleft = (500, 130)
    screen.blit(texte,rect)
    texte, rect = myfont.render("Rangez les blocs dans l'ordre et cliquez sur \"Valider\"", couleur, size = 20)
    rect.midleft = (20, height-170)
    screen.blit(texte,rect)
    texte, rect = myfont.render("Vous pouvez voir la réponse après avoir validé", couleur, size = 20)
    rect.midleft = (150, height-35)
    screen.blit(texte,rect)


# Constantes pour l'application
limite1 = 150              # pour le trait de séparation
nb_rect = 5                # nombre de blocs au départ
ecart_x, ecart_y = 0, 0    # pour mesurer l'écart entre le coin d'un bloc et l'endroit où on clique
recommencer = True         # pour savoir s'il faut tout réinitialiser


# Liste des boutons "simples"
boutons = [[(20, limite1-30-20), (100, 30), "Comparer", 0],
           [(20, height-90), (100, 30), "Valider", 0],
           [(20, height-50), (100, 30), "Réponse", 0],
           [(width-20-150, height-50), (150, 30), "Recommencer", 1]]

# boutons pour modifier le nombre de blocs
boutons_haut_bas = [[(width-20-20, height-90), (20, 15), "", 1],
           [(width-20-20, height-75), (20, 15), "", 1]]


# Boucle principale
continuer = True
while continuer:
    if recommencer: # On doit tout réinitialiser
        liste_valeurs, solutions = generer_valeurs(nb_rect) # valeurs à trouver
        texte_comparaison = "" # résultat de la comparaison : A < B, E > G...
        nb_comparaisons = 0    # nombre de comparaisons effectuées
        aff_solut = False      # est-ce qu'on affiche la solution ?
        ecart = determiner_ecart(nb_rect) # on calcule l'écart entre blocs
        victoire = 0   # Pour savoir si on a gagné (1), perdu (-1) ou rien (0)
        actifs_haut = []  # Les blocs sélectionnés en haut
        actif_milieu = -1  # Le bloc qu'on déplace au milieu
        presses = [0, 0, 0] # pour les boutons de la souris
        recommencer = False # on ne recommence pas tout de suite
        cases_solutions = [-1]*nb_rect # pour savoir les blocs qui sont placés et où

        # On désactive les boutons
        for i in range(3):
            boutons[i][3] = 0

        # On place les blocs en haut et au milieu
        # un bloc c'est : position, numéro, actif ou pas
        rectangles_haut = []
        rectangles_milieu = []
        for i in range(nb_rect):
            rectangles_haut.append([(10+i*ecart,10), i, False])
            rectangles_milieu.append([(10+i*ecart,limite1+10), i, False])
        rectangles = [rectangles_haut, rectangles_milieu]

        # Les positions pour les solutions et les réponses
        positions_solutions = []
        rectangles_solutions = []
        for i in range(nb_rect):
            positions_solutions.append((10+i*ecart,450))
            rectangles_solutions.append((10+i*ecart,350))

    # On vide l'écran et on trace le trait en haut
    screen.fill(white)
    pygame.draw.rect(screen,black,(0,limite1,width,4),0)
    x, y = pygame.mouse.get_pos() # coordonnees du pointeur de la souris
    # Gestion des evenements
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            continuer = False  # Pour quitter
        elif event.type == pygame.MOUSEBUTTONDOWN: # On vient de cliquer
            if event.button == 1: # Bouton gauche
                presses[0] = 1
                # on regarde si on a cliqué sur un bloc
                pos_rect, n_rect = trouver_actif(x, y, rectangles)
                if pos_rect == 0: # C'est un bloc en haut
                    rect = rectangles[pos_rect][n_rect]
                    if len(actifs_haut)<2 or n_rect in actifs_haut: # On regarde si on doit activer ou pas
                        rectangles[pos_rect][n_rect][2] = not rectangles[pos_rect][n_rect][2]
                        if n_rect in actifs_haut:
                            actifs_haut.remove(n_rect)
                        else:
                            actifs_haut.append(n_rect)
                elif pos_rect == 1: # C'est un bloc au milieu
                    actif_milieu = n_rect
                    x_rect, y_rect = rectangles[pos_rect][n_rect][0]
                    ecart_x, ecart_y = x-x_rect, y-y_rect
                    if actif_milieu in cases_solutions: # On regarde s'il faut l'enlever des solutions
                        cases_solutions[cases_solutions.index(actif_milieu)] = -1
                        boutons[1][3] = 0
                        victoire = 0

                # On regarde les boutons
                if boutons[0][3] > 0 and dans_bouton(x, y, boutons[0]): # Comparer
                    nb_0, nb_1 = actifs_haut[0], actifs_haut[1]
                    if nb_0 > nb_1:
                        nb_0, nb_1 = nb_1, nb_0
                    compar = " < "
                    if liste_valeurs[nb_0] > liste_valeurs[nb_1]:
                        compar = " > "
                    texte_comparaison = nom_bloc(nb_0) +  compar + nom_bloc(nb_1)
                    nb_comparaisons += 1
                elif boutons[1][3] > 0 and dans_bouton(x, y, boutons[1]): # Valider
                    boutons[2][3] = 1
                    if solutions == cases_solutions:
                        victoire = 1
                    else:
                        victoire = -1
                elif boutons[2][3] > 0 and dans_bouton(x, y, boutons[2]): # Afficher réponse
                    aff_solut = True
                elif boutons[3][3] > 0 and dans_bouton(x, y, boutons[3]): # On recommence
                    recommencer = True
                elif boutons_haut_bas[0][3] > 0 and dans_bouton(x, y, boutons_haut_bas[0]):
                    # On augmente le nombre de blocs à trier
                    recommencer = True
                    nb_rect += 1
                    if nb_rect >= 12: # On limite à 12 max
                        boutons_haut_bas[0][3] = 0
                    boutons_haut_bas[1][3] = 1
                elif boutons_haut_bas[1][3] > 0 and dans_bouton(x, y, boutons_haut_bas[1]):
                    # On diminue le nombre de blocs à trier
                    recommencer = True
                    nb_rect -= 1
                    if nb_rect <= 2: # On limite à 2 min
                        boutons_haut_bas[1][3] = 0
                    boutons_haut_bas[0][3] = 1
        elif event.type == pygame.MOUSEBUTTONUP: # On relache un bouton
            if event.button == 1: # Le bouton du milieu
                presses[0] = 0
                if actif_milieu >= 0:
                    # Si on déplace un bloc du milieu on regarde si on
                    # le met à la bonne place
                    rect = rectangles[pos_rect][actif_milieu]
                    x_r, y_r = rect[0]
                    for i, pos_sol in enumerate(positions_solutions):
                        # On cherche si on est proche d'une position de
                        # la solution
                        x_s, y_s = pos_sol
                        if abs(x_r-x_s) < taille_rect and abs(y_r-y_s) < taille_rect:
                            if cases_solutions[i] == -1:
                                cases_solutions[i] = actif_milieu
                                rectangles[pos_rect][actif_milieu][0] = pos_sol
                            break
                    if -1 not in cases_solutions and not aff_solut:
                        boutons[1][3] = 1
                actif_milieu = -1 # On libère le bloc déplacé, s'il y en a un
        elif event.type == pygame.MOUSEMOTION: # On déplace la souris
            if presses[0]: # Bouton gauche
                if actif_milieu >= 0: # On est sur un bloc du milieu, on le déplace
                    vals = rectangles[1][actif_milieu]
                    #x_rect, y_rect = vals[0]
                    x_new, y_new = (x-ecart_x), (y-ecart_y)
                    x_new = max(0, min(x_new, width-taille_rect))
                    y_new = max(limite1, min(y_new, height-taille_rect))
                    rectangles[1][actif_milieu][0] = (x_new, y_new)

    # On regarde si on doit activer le bouton "Comparer"
    boutons[0][3] = (len(actifs_haut) == 2)
    # On surligne le bouton survolé, s'il est actif
    for bouton in boutons:
        if bouton[3] > 0:
            if dans_bouton(x, y, bouton):
                bouton[3] = 2
            else:
                bouton[3] = 1

    # On affiche les textes
    afficher_infos()
    afficher_position_solutions(positions_solutions)
    afficher_choix_nombre(nb_rect, boutons_haut_bas)
    if aff_solut: # On affiche la réponse
        afficher_solutions(rectangles_solutions, cases_solutions, solutions)
    for rects in rectangles: # On affiche les blocs
        for rect in rects:
            pos, nb, actif = rect
            afficher_bloc(pos, nb, actif)
    for bouton in boutons: # On affiche les boutons
        pos_bouton, dimensions, nom, etat = bouton
        afficher_bouton(pos_bouton, dimensions, nom, etat)
    afficher_texte_comparaison(texte_comparaison, nb_comparaisons, boutons[0])
    if victoire: # On affiche le message de victoire ou d'échec
        afficher_texte_victoire(victoire, nb_comparaisons)
    # On envoie le tout à la carte graphique
    pygame.display.flip()
    # On se limite à 60 images par seconde
    clock.tick(60)

pygame.quit()