from tkinter import *
import copy
from PIL import Image, ImageTk
from random import *
from time import *
import pygame
from tkhtmlview import HTMLLabel
import threading
import socket
import json
import requests
import tkinter as tk
from tkinter import messagebox
import webbrowser

#pip install pygame
#pip install tkhtmlview

GITHUB_REPO = "maxime599/bataille-navale-tkinter"
VERSION_ACTUELLE = "3.0.1"

#viens d'un code git hub juste plus pratique pour mettre à jour
class GithubUpdateChecker:
    #Classe pour vérifier les mises à jour depuis GitHub
    
    def __init__(self, repo, version_actuelle):
        self.repo = repo
        self.version_actuelle = version_actuelle
        self.url_releases = f"https://api.github.com/repos/{repo}/releases/latest"
    
    def verifier_mise_a_jour(self):
        """Vérifie si une mise à jour est disponible"""
        try:
            #print(f"[DEBUG] URL appelée: {self.url_releases}")
            #print(f"[DEBUG] Version actuelle: {self.version_actuelle}")
            
            response = requests.get(self.url_releases, timeout=5)
            #print(f"[DEBUG] Status code: {response.status_code}")
            
            if response.status_code == 200:
                data = response.json()
                version_github = data['tag_name'].lstrip('v')
                url_telechargement = data['html_url']
                description = data.get('body', 'Aucune description disponible')
                
                #print(f"[DEBUG] Version GitHub trouvée: {version_github}")
                #print(f"[DEBUG] Comparaison: {version_github} > {self.version_actuelle} = {self.comparer_versions(version_github, self.version_actuelle)}")
                
                return {
                    'disponible': self.comparer_versions(version_github, self.version_actuelle),
                    'version': version_github,
                    'url': url_telechargement,
                    'description': description
                }
            elif response.status_code == 404:
                return {'erreur': 'Aucune release trouvée sur GitHub.\n\nPour corriger:\n1. Allez sur votre dépôt GitHub\n2. Cliquez sur "Releases"\n3. Créez une nouvelle release avec un tag (ex: v1.0.0)'}
            else:
                return {'erreur': f'Erreur HTTP {response.status_code}\n\nVérifiez que le dépôt existe: https://github.com/{self.repo}'}
                
        except requests.exceptions.Timeout:
            return {'erreur': 'Délai d\'attente dépassé'}
        except requests.exceptions.ConnectionError:
            return {'erreur': 'Impossible de se connecter à GitHub'}
        except Exception as e:
            #print(f"[DEBUG] Exception: {str(e)}")
            return {'erreur': f'Erreur: {str(e)}'}
    
    def comparer_versions(self, v1, v2):
        """Compare deux versions (format: x.y.z)"""
        try:
            parts1 = [int(x) for x in v1.split('.')]
            parts2 = [int(x) for x in v2.split('.')]
            
            # Assure que les deux listes ont la même longueur
            while len(parts1) < len(parts2):
                parts1.append(0)
            while len(parts2) < len(parts1):
                parts2.append(0)
            
            return parts1 > parts2
        except:
            return False

def on_close():
    musique_en_jeu.stop()
    fenetre1.fenetre.destroy()
    fenetre2.fenetre.destroy()
        
root_created = False
pygame.mixer.init()

class SonHelper:
    #Classe pour accéder aux sons depuis n'importe où dans le code
    def __init__(self):
        pass
    
    def get_son_path(self, type_son, n=None):
        #Retourne le chemin du fichier son en fonction du type
        if type_son in UI_menu.sons_personnalises:
            chemin = UI_menu.sons_personnalises[type_son]
        else:
            pack_actuel = UI_menu.pack_actuel
            if pack_actuel == "personnalise":
                pack_actuel = "principal"
            chemin = UI_menu.packs_sons[pack_actuel]["sons"].get(type_son, "")
        
        if n is not None:
            chemin = chemin.replace("{n}", str(n))
        
        return chemin

son_helper = SonHelper()
class Plateau:
    """
    0 = vide                            blanc
    1 = case ciblé sans bateaus         viollet
    2 = case avec bateau                noir
    3 = case touché avec bateau         vert
    4 = case avec bateau prévisualisé   gris
    5 = bateau coulé
    x=ligne 
    y=colonne"""

    def __init__(self):
        self.plateau = []
        self.liste_bateau_restant = [] #liste des bateaux encore en vie, chaque élément est une liste de la forme [taille_bateau, [[x1,y1], [x2,y2], ...]]
        self.liste_bateau_total = [] #liste des bateaux total, chaque élément est une liste de la forme [taille_bateau, [[x1,y1], [x2,y2], ...]]
        self.liste_bateaux_a_poser = [] #liste des tailles des bateaux à poser

    def creation_plateau(self):
        #Fonction qui créer le plateau vide, en 10x10, rempli de 0
        self.plateau = []
        for i in range(10):
            ligne = []
            for j in range(10):
                ligne.append(Case(i, j, 0))
            self.plateau.append(ligne)
        return self.plateau

    def afficher_plateau(self, afficher_1, afficher_2):
        #Fonction qui affiche dans la console le plateau
        print("  0 1 2 3 4 5 6 7 8 9")
        for numero_ligne, ligne_plateau in enumerate(self.plateau):
            ligne_print = str(numero_ligne) + ' '
            for case in ligne_plateau:
                if case.type == 0:
                    ligne_print += '· '
                elif case.type == 1:
                    if afficher_1:
                        ligne_print += 'X '
                    else:
                        ligne_print += '· '
                elif case.type == 2:
                    if afficher_2:
                        ligne_print += '□ '
                    else:
                        ligne_print += '· '
                elif case.type == 3:
                    ligne_print += '☒ '
            print(ligne_print + str(numero_ligne))
        print("  0 1 2 3 4 5 6 7 8 9")

    def modifier_case(self, coordonees_x, coordonees_y, valeur, taille_bateau = None, position_sur_bateau = None, orientation_bateau = None):
        #Modifie une case du plateau en fonction des parametres passés
        self.plateau[coordonees_x][coordonees_y].coordonees_x = coordonees_x
        self.plateau[coordonees_x][coordonees_y].coordonees_y = coordonees_y
        self.plateau[coordonees_x][coordonees_y].type = valeur
        
        if taille_bateau != None:
            self.plateau[coordonees_x][coordonees_y].taille_bateau = taille_bateau
        if position_sur_bateau != None:
            self.plateau[coordonees_x][coordonees_y].position_sur_bateau = position_sur_bateau
        if orientation_bateau != None:
            self.plateau[coordonees_x][coordonees_y].orientation_bateau = orientation_bateau
           
    def cible_case(self, coordonees_x, coordonees_y):
        # retourne True si est un bateau est présent
        if self.plateau[coordonees_x][coordonees_y].type == 2 or self.plateau[coordonees_x][coordonees_y].type == 5:
            return True
        else:
            return False

    def is_possible_cible(self, coordonees_x, coordonees_y):
        # retourne True si c'est possible de cibler la case
        if coordonees_x>9 or coordonees_x<0 or coordonees_y>9 or coordonees_y<0:
            return False
        else:
            if self.plateau[coordonees_x][coordonees_y].type == 1 or self.plateau[coordonees_x][coordonees_y].type == 3 or self.plateau[coordonees_x][coordonees_y].type == 5:
                return False
            else :
                return True

    def ajouter_bateau(self, coordonees_x, coordonees_y, orientation, taille, can_touch, juste_test_possible = False): #orientation = 0 -> droite, 1 -> bas
        #Ajoute renvoir True ou False si l'ajout est possible, et si oui l'ajoute
        
        #Renvoie False si le bateau est en dehors de l'écran ou si il y a déja un bateau sur le terrain

        if coordonees_x <0 or coordonees_x >9 or coordonees_y<0 or coordonees_y>9: #hors plateau
            return False 

        if orientation == 0:
            if not can_touch:
                #on regarde si les trois cases à droite et à gauche du bateau sont déja pleines ou non
                for i in range(-1, 2):
                    try:
                        if self.plateau[coordonees_x+i][coordonees_y-1].type != 0 and coordonees_x+i>=0 and coordonees_y-1>=0:
                            return False
                    except:
                        pass
                    
                    try:
                        if self.plateau[coordonees_x+i][coordonees_y+taille].type != 0 and coordonees_x+i>=0:
                            return False
                    except:
                        pass

            for i in range(coordonees_y, coordonees_y+taille): #on regarde si les cases du bateau sont libres et dans le plateau
                if i >= 10  :   
                    return False
                if self.plateau[coordonees_x][i].type != 0 and i>=0:
                    return False
                if not can_touch:
                    try:
                        if self.plateau[coordonees_x-1][i].type != 0 and coordonees_x-1>=0 and i>=0:
                            return False
                    except:
                        pass
                    
                    try:
                        if self.plateau[coordonees_x+1][i].type != 0 and i>=0:
                            return False
                    except:
                        pass

        elif orientation == 1:
            #on regarde si les trois cases en haut et en bas du bateau sont déja pleines ou non
            if not can_touch:
                for i in range(-1, 2):
                    try:
                        if self.plateau[coordonees_x-1][coordonees_y+i].type != 0  and coordonees_x-1>=0 and coordonees_y+i>=0:
                            return False
                    except:
                        pass
                    
                    try:
                        if self.plateau[coordonees_x+taille][coordonees_y+i].type != 0  and coordonees_x+taille>=0 and coordonees_y+i>=0:
                            return False
                    except:
                        pass
            for i in range(coordonees_x, coordonees_x+taille): #on regarde si les cases du bateau sont libres et dans le plateau
                if i >= 10:
                    return False
                if self.plateau[i][coordonees_y].type != 0  and i>=0:
                    return False
                if not can_touch:
                    try:
                        if self.plateau[i][coordonees_y-1].type != 0 and coordonees_y-1>=0 and i>=0:
                            return False
                    except:
                        pass
                    
                    try:
                        if self.plateau[i][coordonees_y+1].type != 0 and i>=0:
                            return False
                    except:
                        pass
        else:
            return False
        
        #Si on est en mode test, on arrete ici
        if juste_test_possible == False:
            self.liste_bateau_restant.append([taille,[]])
            #modifie la matrice plateau avec les bonnes valeurs
            if orientation == 1:
                for index, i in enumerate(range(coordonees_x, coordonees_x+taille)):
                    self.modifier_case(i, coordonees_y, 2, taille, index, orientation)
                    self.liste_bateau_restant[-1][1].append([i, coordonees_y])
                    

            elif orientation == 0:
                for index, i in enumerate(range(coordonees_y, coordonees_y+taille)):
                    self.modifier_case(coordonees_x, i, 2, taille, index, orientation)
                    self.liste_bateau_restant[-1][1].append([coordonees_x, i])

            return True

    def enlever_case_bateau(self, coordonees_x, coordonees_y):
        #remove dans la liste des bateaux la case corespondante
        #renvoie la taille initial du bateau et la taille après le ciblage 
        for index_bateau, bateau in enumerate(self.liste_bateau_restant):
            for coordonees_case_bateau in bateau[1]:
                if coordonees_case_bateau == [coordonees_x, coordonees_y]:
                    self.liste_bateau_restant[index_bateau][1].remove(coordonees_case_bateau)
                    return (bateau[0], len(bateau[1]))
        return (0,0)

    def nb_bateau_restant(self):
        #renvoie le nombre de bateau de bateau encore en vie
        output = 0
        for bateau in self.liste_bateau_restant:
            if len(bateau[1]) != 0:
                output += 1
        return output
    
    def nb_bateau_restant_par_taille(self):
        #renvoie le nombre de bateau de bateau encore en vie par taille
        output = []
        for taille in range(1,6):
            output_nb = 0
            for bateau in self.liste_bateau_restant:
                if bateau[0] == taille:
                    if len(bateau[1]) != 0:
                        output_nb += 1
            output.append(output_nb)
        return output

    def nb_bateau_restant_a_pose_par_taille(self):
        #renvoie le nombre de bateau à poser par taille
        output = []
        for taille in range(1,6):
            output_nb = self.liste_bateaux_a_poser.count(taille)
            output.append(output_nb)
        return output

    def pose_bateaux_aleatoire(self, can_touch):
        #Pose aléatoirement les bateaux dans la liste des bateaux à poser
        for taille in self.liste_bateaux_a_poser:
            good_position = False
            while not good_position:
                good_position = self.ajouter_bateau(randint(0,9), randint(0,9), randint(0,1), taille, can_touch, False)

    def coup_aléatoire(self, can_touch):
        #Renvoie des coordonnées semi-aléatoires pour un coup
        bon_coup = False
        coordonnee_coup_x = 0
        coordonnee_coup_y = 0
        while not bon_coup:
            coordonnee_coup_x = randint(0,9)
            coordonnee_coup_y = randint(0,9)
            bon_coup = self.is_possible_cible(coordonnee_coup_x, coordonnee_coup_y)
            if not can_touch: #si les bateaux ne peuvent pas se toucher, alors on exclut la case si elle est à coté d'un bateau coulé
                for x in range(-1, 2):
                    for y in range(-1, 2):
                        if 0 <= coordonnee_coup_x + x <= 9 and 0 <= coordonnee_coup_y + y <= 9:
                            if self.plateau[coordonnee_coup_x + x][coordonnee_coup_y + y].type == 5:
                                bon_coup = False
        return (coordonnee_coup_x, coordonnee_coup_y)

    def coup_IA(self, can_touch):
        #Renvoie des coordonnées pour un coup en mode IA
        for index_ligne, ligne in enumerate(self.plateau):
            for index_colonne, case in enumerate(ligne):
                if case.type == 3: #Si on detecte une case précedement touchée
                    liste_cases_touched_autour = []
                    liste_cases_autour_valides = []
                    #on compte combien il y des cases touchées autour et de cases que l'on peut cibler
                    for coordonnee_cases_autour in [[index_ligne-1, index_colonne], [index_ligne, index_colonne - 1], [index_ligne + 1, index_colonne], [index_ligne, index_colonne + 1]]:
                        if 0 <= coordonnee_cases_autour[0] <= 9 and 0 <= coordonnee_cases_autour[1] <= 9:
                            if self.plateau[coordonnee_cases_autour[0]][coordonnee_cases_autour[1]].type == 3:
                                liste_cases_touched_autour.append(coordonnee_cases_autour)
                            else:
                                if self.is_possible_cible(coordonnee_cases_autour[0], coordonnee_cases_autour[1]):
                                    liste_cases_autour_valides.append(coordonnee_cases_autour)
                    if len(liste_cases_touched_autour) == 0:#si il n'y a pas de cases touchée autour, alors on cible une case aléatoir parmit celle que l'on peur cibler
                        return choice(liste_cases_autour_valides)
                    
                    elif len(liste_cases_touched_autour) == 1: #si il y a une autre case touchée autour, alors on tente de viser celle à l'opposé
                        if liste_cases_touched_autour[0] == [index_ligne-1, index_colonne] and self.is_possible_cible(index_ligne+1, index_colonne):
                            return (index_ligne+1, index_colonne)
                        elif liste_cases_touched_autour[0] == [index_ligne, index_colonne - 1] and self.is_possible_cible(index_ligne, index_colonne + 1):
                            return (index_ligne, index_colonne + 1)
                        elif liste_cases_touched_autour[0] == [index_ligne+1, index_colonne] and self.is_possible_cible(index_ligne-1, index_colonne):
                            return (index_ligne-1, index_colonne)
                        elif liste_cases_touched_autour[0] == [index_ligne, index_colonne + 1] and self.is_possible_cible(index_ligne, index_colonne - 1):
                            return (index_ligne, index_colonne - 1)
                        
                    elif len(liste_cases_touched_autour) == 3: #si il y a 3 cases touchés autour, alors on cible la 4eme
                        for element in [[index_ligne-1, index_colonne], [index_ligne, index_colonne - 1], [index_ligne + 1, index_colonne], [index_ligne, index_colonne + 1]]:
                            if element not in liste_cases_touched_autour:
                                return element
        return self.coup_aléatoire(can_touch)

class UI_game:
    def __init__(self, nom, joueur):

        #Permet de créer un fenêtre principal et un fenêtre secondaire pour éviter les bug de gestion de la souris
        global root_created
        if not root_created:
            self.fenetre = Tk()
            root_created = True
        else:
            self.fenetre = Toplevel()
            
        self.fenetre.title(nom)
        self.nom = nom

        self._clicked = BooleanVar()
        self.croix_id = None
        self.canva_bind = 'droite' #definie quel canva il faut regarder pour le clique gauche
        self.canva_cacher = False
        self.joueur = joueur

        #Ajout des textes sur la fenêtre
        canvas_num_gauche = Canvas(self.fenetre, width=476, height=40, background='#f0f0f0')
        canvas_num_gauche.grid(row=0, column=0, columnspan=10, pady=(15, 0), padx=10)

        for i in range(1, 11):
            x_pos = (i - 0.5) * 47.6
            canvas_num_gauche.create_text(x_pos, 20, text=str(i), font=("Courier", 30), anchor='center')

        canvas_lettres = Canvas(self.fenetre, width=40, height=476, background='#f0f0f0')
        canvas_lettres.grid(row=1, column=10, rowspan=10, padx=5)

        alphabet = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]
        for i, k in enumerate(alphabet):
            y_pos = (i + 0.5) * 47.6
            canvas_lettres.create_text(20, y_pos, text=k, font=("Courier", 30), anchor='center')

        canvas_num_droite = Canvas(self.fenetre, width=476, height=40, background='#f0f0f0')
        canvas_num_droite.grid(row=0, column=11, columnspan=10, pady=(15, 0), padx=10)

        for i in range(1, 11):
            x_pos = (i - 0.5) * 47.6
            canvas_num_droite.create_text(x_pos, 20, text=str(i), font=("Courier", 30), anchor='center')     
        
        self.canva_gauche = Canvas(self.fenetre, width=476, height=476, background='#589ddc')
        self.canva_gauche.grid(row=1, column=0, columnspan=10, rowspan=10, padx=10, pady=(0, 5) )

        self.canva_droite = Canvas(self.fenetre, width=476, height=476, background='green')
        self.canva_droite.grid(row=1, column=11, columnspan=10, rowspan=10, padx=10, pady=(0, 5))

        self.phrase = Label(self.fenetre, text="Sert à viser l'adversaire", font=("Courier", 12))
        self.phrase.grid(row=11, column=0, columnspan=10)
        self.phrase = Label(self.fenetre, text="Sert à voir vos bateaux touchés", font=("Courier", 12))
        self.phrase.grid(row=11, column=11, columnspan=10)

        self.parametre = PhotoImage(file="images/parametre_crop.png", master=self.fenetre)
        label_parametre = Label(self.fenetre, image=self.parametre, cursor='hand2')
        label_parametre.grid(row=0, column=22, padx=10, pady=10, sticky='e')
        label_parametre.bind("<Button-1>", lambda e: self.afficher_fenetre_parametre())
        
        
        #Gestion des différentes images
        self.images = []
        self.taille_case = 45
        self.img_bleu = PhotoImage(file='images/bleu.png', master=self.fenetre)
        
        self.img_gris = PhotoImage(file='images/gris.png', master=self.fenetre)
        self.img_croix = PhotoImage(file='images/croix.png', master=self.fenetre)
        self.croix_fond_rouge = PhotoImage(file='images/croix_fond_rouge.png', master=self.fenetre)
        self.img_cible = PhotoImage(file='images/cible.png', master=self.fenetre)
        self.img_touche = PhotoImage(file='images/touché.png', master=self.fenetre)

        self.img_bateau_1 = PhotoImage(file='Images/Bateaux/1.png', master=self.fenetre)
        self.img_bateau_2 = PhotoImage(file='Images/Bateaux/2.png', master=self.fenetre)
        self.img_bateau_3 = PhotoImage(file='Images/Bateaux/3.png', master=self.fenetre)
        self.img_bateau_4 = PhotoImage(file='Images/Bateaux/4.png', master=self.fenetre)
        self.img_bateau_5 = PhotoImage(file='Images/Bateaux/5.png', master=self.fenetre)

        self.im_carre_bleu = PhotoImage(file="images/carre_bateau_bleu.png", master=self.fenetre)
        self.im_carre_rouge = PhotoImage(file="images/carre_bateau_rouge.png", master=self.fenetre)

        self.liste_img_bateaux = [self.img_bateau_1, self.img_bateau_2, self.img_bateau_3, self.img_bateau_4, self.img_bateau_5]
        self.titres = []
        self.canva_ids = []
        self.canva_text_ids = []

    def afficher_plateau(self, plateau, afficher_1, afficher_2, position_canva, afficher_croix):
        #Fonction qui affiche le plateau dans le canva spécifié (gauche ou droite)
        
        #permet de créer ces deux listes seulement une fois, la première fois que afficher_plateau() est appelée.
        if not hasattr(self, 'images_gauche'):
            self.images_gauche = []
        if not hasattr(self, 'images_droite'):
            self.images_droite = []

        if position_canva == 'gauche':
            self.canva_gauche.delete("all")
            # on réinitialise uniquement la liste gauche
            self.images_gauche = []
        else:
            self.canva_droite.delete("all")
            # on réinitialise uniquement la liste droite
            self.images_droite = []

        for index_ligne, ligne in enumerate(plateau):
            for index_colonne, colonne in enumerate(ligne):
                x = index_colonne * self.taille_case + index_colonne * 3 + 1
                y = index_ligne * self.taille_case + index_ligne * 3 + 1


                # Affiche toujours l'image bleue en fond
                if position_canva == 'gauche':
                    self.canva_gauche.create_image(x, y, image=self.img_bleu, anchor=NW)
                else:
                    self.canva_droite.create_image(x, y, image=self.img_bleu, anchor=NW)

                if colonne.type == 0:
                    image = self.img_bleu
                if afficher_croix: #affiche une croix rouge sur les cases ciblées sans bateaux autour desquelles il y a un bateau
                    for case_to_check_x in range(-1,2):
                        for case_to_check_y in range(-1,2):
                            if 0 <= (index_ligne + case_to_check_x) <= 9 and 0 <= (index_colonne + case_to_check_y) <= 9:
                                if (case_to_check_x, case_to_check_y) != (0,0) and plateau[index_ligne + case_to_check_x][index_colonne + case_to_check_y].type == 2:
                                    image = self.croix_fond_rouge
                if colonne.type == 1: #case ciblé sans bateaux
                    image = self.img_cible if afficher_1 else self.img_bleu
                elif (not afficher_2) and (colonne.type == 2): #case avec bateau non affiché
                    image = self.img_bleu

                elif (colonne.type == 2 and afficher_2) or (colonne.type == 3 and position_canva != 'gauche') or colonne.type == 5: #case avec bateau affiché ou case touché avec bateau (uniquement sur le canva de droite) ou bateau coulé
                    # Découpe l'image du bateau en fonction de sa taille, position et orientation
                    img_original_pil = Image.open(f'Images/Bateaux/{colonne.taille_bateau}.png')
                    if colonne.type == 5:
                        img_original_pil = img_original_pil.convert("RGBA")
                    x1 = (colonne.position_sur_bateau)*45+(colonne.position_sur_bateau-1)*3
                    x2 = x1 + 48  
                    image_cropped = img_original_pil.crop((x1, 0, x2, 45))
                    
                    if colonne.orientation_bateau == 1: #si le bateau est vertical on retourne l'image
                        image_cropped = image_cropped = image_cropped.rotate(-90, expand=True) #on fait une rotation de -90 degré pour que l'image soit dans le bon sens
                    
                    if colonne.type == 5: #si le bateau est coulé on le rend semi-transparent
                        alpha = image_cropped.split()[-1]
                        alpha = alpha.point(lambda p: p * 0.5)  # 0.5 = 50% transparent
                        image_cropped.putalpha(alpha)

                    image = ImageTk.PhotoImage(image_cropped, master=self.fenetre) #on convertit l'image PIL en image Tkinter
                

                elif colonne.type == 3: #
                    image = self.img_touche

                
                elif colonne.type != 0:  # colonne.type == 4
                    image = self.img_gris
                
                
                # On ajoute l'image au canva approprié et on la stocke dans la liste correspondante
                if position_canva == 'gauche': 
                    self.images_gauche.append(image)
                    self.canva_gauche.create_image(x, y, image=image, anchor=NW)
                else:
                    self.images_droite.append(image)
                    self.canva_droite.create_image(x, y, image=image, anchor=NW)
                    if colonne.type == 3:
                        self.canva_droite.create_image(x, y, image=self.img_touche, anchor=NW)
                
    def click_to_case(self, coordonnee_click_x, coordonnee_click_y):
        #Retourne les coordonnées de la case cliquée
        return (int(coordonnee_click_y/(self.taille_case+3)), int(coordonnee_click_x/(self.taille_case+3)))

    def attendre_click_case(self):
        #Attend qu'un clic de souris soit effectué sur la fenêtre, puis retourne les coordonnées de la case cliquée.      
        
        self.click_coord = (0,0)
        self._clicked.set(False)
        if self.canva_bind == 'droite': #definie quel canva il faut regarder pour le clique gauche
            self.canva_droite.bind("<Button-1>", self.on_click)
        else:
            self.canva_gauche.bind("<Button-1>", self.on_click)
            self.canva_droite.bind("<Button-1>", lambda event: self.toogle_cache_noir_droite()) #permet de cacher/montrer le canva droite au clique
        self.fenetre.wait_variable(self._clicked) 
        return self.click_coord  # retourne un tuple (x_case, y_case)
    
    def on_click(self, event):
        #Fonction qui récupère les coordonnées du click de la souris
        x_pixel = event.x
        y_pixel = event.y

        self.click_coord = self.click_to_case(x_pixel, y_pixel)
        if self.canva_bind == 'droite':
            self.canva_droite.unbind("<Button-1>")
        else:
            self.canva_gauche.unbind("<Button-1>")
            self.canva_droite.unbind("<Button-1>")
        self._clicked.set(True)

    def afficher_previsualisation(self, plateau, x, y, orientation, taille, position_canva, can_touch):
        # Affiche une prévisualisation semi-transparente d'un bateau à la position (x, y) avec l'orientation et la taille spécifiées
        # position_canva : 'gauche' ou 'droite'
        
        if plateau.ajouter_bateau(x, y, orientation, taille, can_touch, True) == False:
            possible = False
        else:
            possible = True

        # Efface uniquement la prévisualisation précédente
        if position_canva == 'gauche':
            self.canva_gauche.delete("preview")
        else:
            self.canva_droite.delete("preview")

        # Affiche la nouvelle prévisualisation
        for i in range(taille):
            if orientation == 0:
                case_x, case_y = x, y + i
            else:
                case_x, case_y = x + i, y
            if 0 <= case_x < 10 and 0 <= case_y < 10: # Vérifie que la case est dans les limites du plateau
                img_path = f'Images/Bateaux/{taille}.png'
                img_pil = Image.open(img_path).convert("RGBA")
                x1 = i * self.taille_case + i * 3
                x2 = x1 + self.taille_case + 3
                image_cropped = img_pil.crop((x1, 0, x2, self.taille_case)) # Découpe l'image du bateau
                if orientation == 1:
                    image_cropped = image_cropped.rotate(-90, expand=True) # Rotation de l'image si le bateau est vertical
                
                # Rend l'image semi-transparente
                alpha = image_cropped.split()[-1]
                alpha = alpha.point(lambda p: p * 0.5)
                image_cropped.putalpha(alpha)
                
                if not possible: # Si placement impossible, applique filtre rouge
                    # Crée un overlay rouge semi-transparent
                    rouge = Image.new("RGBA", image_cropped.size, (255, 0, 0, 120))
                    image_cropped = Image.alpha_composite(image_cropped, rouge)
                image = ImageTk.PhotoImage(image_cropped, master=self.fenetre)
                if not hasattr(self, 'preview_images'): # Initialise la liste des images de prévisualisation si elle n'existe pas encore
                    self.preview_images = []
                self.preview_images.append(image) # Stocke la référence de l'image pour éviter qu'elle soit garbage collectée
                px = case_y * self.taille_case + case_y * 3 + 1
                py = case_x * self.taille_case + case_x * 3 + 1
                
                # Affiche l'image dans le canva approprié
                if position_canva == 'gauche':
                    self.canva_gauche.create_image(px, py, image=image, anchor=NW, tags="preview")
                else:
                    self.canva_droite.create_image(px, py, image=image, anchor=NW, tags="preview")
    
    def afficher_croix(self, event):
        # Supprime l'ancienne croix si elle existe
        if self.croix_id is not None:
            self.canva_gauche.delete(self.croix_id)
        # Affiche la croix à la position de la souris
        self.croix_id = self.canva_gauche.create_image(event.x, event.y, image=self.img_cible)

    def cacher_croix(self, event=None):
        # Supprime la croix si elle existe
        if self.croix_id is not None:
            self.canva_gauche.delete(self.croix_id)
            self.croix_id = None

    def titre_information(self, parent, texte, row, column, taille):
        #Ajoute un grand titre dans le parent spécifié
        grand_titre = Label(parent, text=texte, font=("Helvetica", taille), wraplength=280, justify='center')
        grand_titre.grid(row=row, column=column, padx=(0,3))
        self.titres.append(grand_titre)

    def bloc_canva(self, parent, titre, text, text2, row, column, image):
        #Ajoute un bloc canva avec des bateaux et du texte dans le parent spécifié
        self.bloc_canva_var = Canvas(parent, width=260, height=220, background="#f8f9fb")
        self.bloc_canva_var.grid(row=row, column=column, padx=6, pady=6)
        self.bloc_canva_var.create_rectangle(4, 4, 260, 220, outline="#cfcfcf", width=2, fill="#ffffff")
        self.bloc_canva_var.create_text(130, 18, text=titre, fill="#1f2f3f", font=("Helvetica", 14, "bold"))
        self.canva_ids.append(self.bloc_canva_var)
        texte_apres_image_liste= []
        
        for bateau in range(5): #pour chaque taille de bateau
            y = 40 + 36 * bateau
            img_x = 20
            for i in range(0, bateau+1):
                if image == "bleu":
                    self.bloc_canva_var.create_image(img_x + 14*i, y+6, image=self.im_carre_bleu, anchor=NW)
                else:
                    self.bloc_canva_var.create_image(img_x + 14*i, y+6, image=self.im_carre_rouge, anchor=NW)
            text_x = img_x + 14 * (bateau + 1) + 12
            texte_apres_image = self.bloc_canva_var.create_text(text_x, y + 10, text=text + str(text2[bateau]), fill="#2b3a42", font=("Helvetica", 12), anchor='w')
            texte_apres_image_liste.append(texte_apres_image)
        self.canva_text_ids.append(texte_apres_image_liste)

    def vider_ids(self):
        #Vide les listes d'ids des canva et titres pour éviter les doublons
        self.canva_ids = []
        self.titres = []
        self.bloc_canva_var = []
        self.canva_text_ids = []

    def toogle_cache_noir_droite(self):
        # Ajoute un rectangle noir couvrant tout le canva de droite
        if self.canva_cacher:
            self.canva_droite.delete("cache_noir")
            if self.joueur == 1:
                self.afficher_plateau(plateau_joueur1.plateau, True, True, 'droite', False)
            else:
                self.afficher_plateau(plateau_joueur2.plateau, True, True, 'droite', False)
            self.canva_cacher = False
        else:
            self.canva_cacher = True
            self.canva_droite.create_rectangle(0, 0, self.canva_droite.winfo_width(), self.canva_droite.winfo_height(),fill="black", outline="", tags="cache_noir")
      
    def afficher_fenetre_parametre(self):
        # Affiche la fenêtre des paramètres
        fenetre_parametre = UI_menu(f"Volume du joueur {self.joueur}", True)
        fenetre_parametre.fenetre_menu.transient(self.fenetre)
        fenetre_parametre.fenetre_menu.grab_set()
        self.fenetre.wait_window(fenetre_parametre.fenetre_menu)

class Case:
    def __init__(self,coordonnee_x, coordonnee_y, type, taille_bateau = None, position_sur_bateau = None, orientation_bateau = None):
        self.coordonnee_x = coordonnee_x
        self.coordonnee_y = coordonnee_y
        self.type = type # 0: bateau non ciblée, 1: bateau ciblée, 2: bateau non ciblé, 3: bateau touché, 4: bateau coulé (affiché différemment), 5: bateau coulé (case finale)
        self.taille_bateau = taille_bateau
        self.position_sur_bateau = position_sur_bateau
        self.orientation_bateau = orientation_bateau

class UI_menu:
    volume_voix_global = 75
    volume_musique_global = 20
    volume_ui_global = 30
    volume_voix_avant_mute_global = volume_voix_global
    volume_musique_avant_mute_global = volume_musique_global
    volume_ui_avant_mute_global = volume_ui_global
    musique_en_jeu_ref = None
    client_pret = False
    serveur_pret = False
    pack_actuel = "principal"
    sons_personnalises = {}
    packs_sons = {
        "principal": {
            "nom_affichage": "Par défaut",
            "sons": {
                "bouton_bleu": "Sons/par_defaut/ui/par_defaut_bleu.mp3",
                "bouton_gris": "Sons/par_defaut/ui/par_defaut_gris.mp3",
                "clic": "Sons/par_defaut/ui/clic.mp3",
                "musique_menu": "Sons/par_defaut/musics/ui/musique_dascenseur.mp3",
                "musique_jeu": "Sons/par_defaut/musics/jeu/DEAF KEV - Invincible [NCS Release].mp3",
                "poser_bateaux": "Sons/par_defaut/C'est au joueur n de poser ses bateaux !/C'est au joueur {n} de poser ses bateaux !.mp3",
                "reste_bateau": "Sons/par_defaut/Il reste n bateau(x) en vie/Il reste {n} bateau(x) en vie.mp3",
                "bateau_coule": "Sons/par_defaut/Le bateau de taille n a été coulé !/Le bateau de taille {n} a été coulé !.mp3",
                "partie_terminee": "Sons/par_defaut/Partie terminée, le joueur n°n gagne !/Partie terminée, le joueur n°{n} gagne !.mp3",
                "navires_prets": "Sons/par_defaut/navires_prets.mp3",
                "bateau_touche": "Sons/par_defaut/Un bateau à été touché !.mp3"
            }
        },
        "joyeux": {
            "nom_affichage": "Joyeux",
            "sons": {
                "bouton_bleu": "Sons/joyeux/ui/par_defaut_bleu.mp3",
                "bouton_gris": "Sons/joyeux/ui/par_defaut_gris.mp3",
                "clic": "Sons/joyeux/ui/clic.mp3",
                "musique_menu": "Sons/joyeux/musics/ui/musique_dascenseur.mp3",
                "musique_jeu": "Sons/joyeux/musics/jeu/DEAF KEV - Invincible [NCS Release].mp3",
                "poser_bateaux": "Sons/joyeux/C'est au joueur n de poser ses bateaux !/C'est au joueur {n} de poser ses bateaux !.mp3",
                "reste_bateau": "Sons/joyeux/Il reste n bateau(x) en vie/Il reste {n} bateau(x) en vie.mp3",
                "bateau_coule": "Sons/joyeux/Le bateau de taille n a été coulé !/Le bateau de taille {n} a été coulé !.mp3",
                "partie_terminee": "Sons/joyeux/Partie terminée, le joueur n°n gagne !/Partie terminée, le joueur n°{n} gagne !.mp3",
                "navires_prets": "Sons/joyeux/navires_prets.mp3",
                "bateau_touche": "Sons/joyeux/Un bateau à été touché !.mp3"
            }
        }
    }
    
    def __init__(self, nom, en_jeu):        
        self.dico_bateaux_a_poser = {1:0, 2:1, 3:2, 4:1, 5:1} #dictionnaire qui indique le nombre de bateau à poser par taille
        if en_jeu:
            self.fenetre_menu = Toplevel()
        else:
            self.fenetre_menu = Tk()

        #Initialisation des variables
        self.can_touch = BooleanVar(value=False)
        self.voir_cibles_adverses = BooleanVar(value=True)
        self.volume_voix = DoubleVar(value=UI_menu.volume_voix_global)
        self.volume_musique = DoubleVar(value=UI_menu.volume_musique_global)
        self.volume_ui = DoubleVar(value=UI_menu.volume_ui_global)
        self.img_hp_0 = PhotoImage(file='sons/icones_son/haut-parleur_0_crop.png')
        self.img_hp_1 = PhotoImage(file='sons/icones_son/haut-parleur_1_crop.png')
        self.img_hp_2 = PhotoImage(file='sons/icones_son/haut-parleur_2_crop.png')
        self.img_hp_3 = PhotoImage(file='sons/icones_son/haut-parleur_3_crop.png')
        self.fenetre_menu.title(nom)
        self.en_jeu = en_jeu
        self.widgets = []
        self.couleur_fond = "#ffffff"
        self.couleur_accent = "#42a5f5"
        self.couleur_texte = "#01579b"
        self.couleur_survol = "#90caf9"
        self.fenetre_menu.configure(bg=self.couleur_fond)
        self.musique_menu = pygame.mixer.Sound(self.get_son_path("musique_menu"))
        self.musique_menu.set_volume(self.volume_musique.get()*0.01)
        self.volume_voix_avant_mute = self.volume_voix.get()
        self.volume_musique_avant_mute = self.volume_musique.get()
        self.volume_ui_avant_mute = self.volume_ui.get()
        self.ip_adresse_serveur = '0.0.0.0'
        self.client_connecte = False
        self.client_pret = False
        self.serveur_pret = False
        self.client_socket = None
        self.bouton_lancer = None
        self.label_attente_client = None
        self.ip_client_sauvegarde = None
        self.mode_jeu = None
        self.retour_menu = False
        
        if not self.en_jeu:
            self.afficher_menu_principal()
            self.musique_menu.play(loops=-1)
            self.fenetre_menu.mainloop()
        else:
            self.afficher_volume()

    def clear_widgets(self):
        #Supprime tous les widgets de la fenêtre
        for widget in self.widgets:
            widget.destroy()
        self.widgets = []

    def afficher_menu_principal(self):
        #Affiche le menu principal
        
        self.clear_widgets()
        
        label_text_principal_menu = Label(self.fenetre_menu, text='Bataille navale', font=('Helvetica', 32, 'bold'), bg=self.couleur_fond, fg="#0277bd", pady=40)
        label_text_principal_menu.grid(row=0, column=0, padx=50, pady=(30, 20))
        self.widgets.append(label_text_principal_menu)
        
        bouton_jouer = Button(self.fenetre_menu, text='Jouer', command=lambda: (self.jouer_bouton_bleu(), self.afficher_mode_jeu()), font=('Helvetica', 14, 'bold'), bg=self.couleur_accent, fg="#ffffff", activebackground=self.couleur_survol, activeforeground="#ffffff", relief='flat', bd=0, padx=40, pady=15, cursor='hand2')
        bouton_jouer.grid(row=1, column=0, padx=50, pady=15, sticky='ew')
        bouton_jouer.bind("<Enter>", lambda e: e.widget.config(bg=self.couleur_survol))
        bouton_jouer.bind("<Leave>", lambda e: e.widget.config(bg=self.couleur_accent))
        self.widgets.append(bouton_jouer)
        
        bouton_parametre = Button(self.fenetre_menu, text='Paramètres', command=lambda: (self.jouer_bouton_bleu(), self.afficher_parametres()), font=('Helvetica', 14, 'bold'), bg=self.couleur_accent, fg="#ffffff", activebackground=self.couleur_survol, activeforeground="#ffffff", relief='flat', bd=0, padx=40, pady=15, cursor='hand2')
        bouton_parametre.grid(row=2, column=0, padx=50, pady=15, sticky='ew')
        bouton_parametre.bind("<Enter>", lambda e: e.widget.config(bg=self.couleur_survol))
        bouton_parametre.bind("<Leave>", lambda e: e.widget.config(bg=self.couleur_accent))
        self.widgets.append(bouton_parametre)
        
        bouton_credits = Button(self.fenetre_menu, text='Crédits',command=lambda: (self.jouer_bouton_bleu(), self.afficher_credits()), font=('Helvetica', 14, 'bold'), bg=self.couleur_accent, fg="#ffffff", activebackground=self.couleur_survol, activeforeground="#ffffff", relief='flat', bd=0, padx=40, pady=15, cursor='hand2')
        bouton_credits.grid(row=3, column=0, padx=50, pady=15, sticky='ew')
        bouton_credits.bind("<Enter>", lambda e: e.widget.config(bg=self.couleur_survol))
        bouton_credits.bind("<Leave>", lambda e: e.widget.config(bg=self.couleur_accent))
        self.widgets.append(bouton_credits)
        
        bouton_maj = Button(self.fenetre_menu, text='Vérifier mise à jour', command=lambda: (self.jouer_bouton_bleu(), self.verifier_mise_a_jour_github()), font=('Helvetica', 14, 'bold'), bg=self.couleur_accent, fg="#ffffff", activebackground=self.couleur_survol, activeforeground="#ffffff", relief='flat', bd=0, padx=40, pady=15, cursor='hand2')
        bouton_maj.grid(row=4, column=0, padx=50, pady=15, sticky='ew')
        bouton_maj.bind("<Enter>", lambda e: e.widget.config(bg=self.couleur_survol))
        bouton_maj.bind("<Leave>", lambda e: e.widget.config(bg=self.couleur_accent))
        self.widgets.append(bouton_maj)
        
        bouton_quitter = Button(self.fenetre_menu, text='Quitter', command=lambda: (self.jouer_bouton_gris(), self.quitter_fenetre()), font=('Helvetica', 14, 'bold'), bg="#b0bec5", fg=self.couleur_texte, activebackground="#cfd8dc", activeforeground=self.couleur_texte, relief='flat', bd=0, padx=40, pady=15, cursor='hand2')
        bouton_quitter.grid(row=5, column=0, padx=50, pady=15, sticky='ew')
        bouton_quitter.bind("<Enter>", lambda e: e.widget.config(bg="#cfd8dc"))
        bouton_quitter.bind("<Leave>", lambda e: e.widget.config(bg="#b0bec5"))
        self.widgets.append(bouton_quitter)
        
        self.fenetre_menu.grid_columnconfigure(0, weight=1)

    def verifier_mise_a_jour_github(self):
        #Vérifie les mises à jour sur GitHub et affiche le résultat
        #code git hub !!!!
        # Créer une fenêtre de chargement
        fenetre_chargement = tk.Toplevel(self.fenetre_menu)
        fenetre_chargement.title("Vérification...")
        fenetre_chargement.geometry("300x100")
        fenetre_chargement.transient(self.fenetre_menu)
        fenetre_chargement.grab_set()
        
        label = tk.Label(fenetre_chargement, text="Vérification des mises à jour...", 
                        font=('Helvetica', 12), pady=30)
        label.pack()
        
        fenetre_chargement.update()
        
        # Vérifier la mise à jour
        checker = GithubUpdateChecker(GITHUB_REPO, VERSION_ACTUELLE)
        resultat = checker.verifier_mise_a_jour()
        
        fenetre_chargement.destroy()
        
        # Afficher le résultat
        if 'erreur' in resultat:
            messagebox.showerror("Erreur", 
                            f"Impossible de vérifier les mises à jour:\n{resultat['erreur']}")
        elif resultat['disponible']:
            reponse = messagebox.askyesno(
                "Mise à jour disponible",
                f"Une nouvelle version est disponible!\n\n"
                f"Version actuelle: {VERSION_ACTUELLE}\n"
                f"Nouvelle version: {resultat['version']}\n\n"
                f"Voulez-vous ouvrir la page de téléchargement?"
            )
            if reponse:
                webbrowser.open(resultat['url'])
        else:
            messagebox.showinfo("À jour", 
                            f"Vous utilisez la dernière version ({VERSION_ACTUELLE})")

    def afficher_parametres(self):
        #Affiche la fenêtre des paramètres
        self.clear_widgets()
        self.fenetre_menu.configure(bg=self.couleur_fond)

        label_text_principal_parametres = Label(self.fenetre_menu, text='Paramètres', font=('Helvetica', 32, 'bold'), bg=self.couleur_fond, fg="#0277bd", pady=40)
        label_text_principal_parametres.grid(row=0, column=0, padx=50, pady=(30, 20))
        self.widgets.append(label_text_principal_parametres)

        check_can_touch = Checkbutton(self.fenetre_menu, text="Autoriser les bateaux à se toucher", command=self.jouer_checkbutton_touch, variable=self.can_touch, font=('Helvetica', 12), bg=self.couleur_fond, fg=self.couleur_texte, selectcolor=self.couleur_fond, activebackground=self.couleur_fond, activeforeground=self.couleur_texte, cursor='hand2')
        check_can_touch.grid(row=1, column=0, padx=50, pady=15)
        self.widgets.append(check_can_touch)

        check_voir_cibles_adverses = Checkbutton(self.fenetre_menu, text="Voir les cibles adverses", command=self.jouer_checkbutton_cibles, variable=self.voir_cibles_adverses, font=('Helvetica', 12), bg=self.couleur_fond, fg=self.couleur_texte, selectcolor=self.couleur_fond, activebackground=self.couleur_fond, activeforeground=self.couleur_texte, cursor='hand2')
        check_voir_cibles_adverses.grid(row=2, column=0, padx=50, pady=15)
        self.widgets.append(check_voir_cibles_adverses)

        bouton_bateaux = Button(self.fenetre_menu, text='Nombres de bateaux', command=lambda: (self.jouer_bouton_bleu(), self.afficher_fenetre_nb_bateaux()), font=('Helvetica', 14, 'bold'), bg=self.couleur_accent, fg="#ffffff", activebackground=self.couleur_survol, activeforeground="#ffffff", relief='flat', bd=0, padx=40, pady=15, cursor='hand2')
        bouton_bateaux.grid(row=3, column=0, padx=50, pady=15, sticky='ew')
        bouton_bateaux.bind("<Enter>", lambda e: e.widget.config(bg=self.couleur_survol))
        bouton_bateaux.bind("<Leave>", lambda e: e.widget.config(bg=self.couleur_accent))
        self.widgets.append(bouton_bateaux)

        bouton_son = Button(self.fenetre_menu, text='Volume', command=lambda: (self.jouer_bouton_bleu(), self.afficher_volume()), font=('Helvetica', 14, 'bold'), bg=self.couleur_accent, fg="#ffffff", activebackground=self.couleur_survol, activeforeground="#ffffff", relief='flat', bd=0, padx=40, pady=15, cursor='hand2')
        bouton_son.grid(row=4, column=0, padx=50, pady=15, sticky='ew')
        bouton_son.bind("<Enter>", lambda e: e.widget.config(bg=self.couleur_survol))
        bouton_son.bind("<Leave>", lambda e: e.widget.config(bg=self.couleur_accent))
        self.widgets.append(bouton_son)
        
        if not self.retour_menu:
            fonction = self.afficher_menu_principal
        if self.retour_menu:
            fonction = self.afficher_mode_socket

        bouton_retour = Button(self.fenetre_menu, text='Retour', command=lambda: (self.jouer_bouton_gris(), fonction()), font=('Helvetica', 14, 'bold'), bg="#b0bec5", fg=self.couleur_texte, activebackground="#cfd8dc", activeforeground=self.couleur_texte, relief='flat', bd=0, padx=40, pady=15, cursor='hand2')
        bouton_retour.grid(row=5, column=0, padx=50, pady=15, sticky='ew')
        bouton_retour.bind("<Enter>", lambda e: e.widget.config(bg="#cfd8dc"))
        bouton_retour.bind("<Leave>", lambda e: e.widget.config(bg="#b0bec5"))
        self.widgets.append(bouton_retour)
        self.fenetre_menu.grid_columnconfigure(0, weight=1)

    def afficher_fenetre_nb_bateaux(self):
        #Affiche la fenêtre pour définir le nombre de bateaux de chaque taille
        self.clear_widgets()

        label = Label(self.fenetre_menu, text="Nombre de bateaux de chaque taille", font=('Helvetica', 24, 'bold'), bg=self.couleur_fond, fg="#0277bd", pady=30, wraplength=320)
        label.grid(row=0, column=0, columnspan=2, padx=50, pady=(30, 20))
        self.widgets.append(label)

        self.form_nb_bateaux = []
        self.valeurs_initiales = []
        for i in range(5): #pour chaque taille de bateau
            label_bateau = Label(self.fenetre_menu, text=f'Nombre de bateau de taille {i+1}', font=('Helvetica', 12), bg=self.couleur_fond, fg=self.couleur_texte)
            label_bateau.grid(row=i+1, column=0, padx=20, pady=10, sticky='w')
            self.widgets.append(label_bateau)
            entry = Entry(self.fenetre_menu, font=('Helvetica', 12), bg="#ffffff", fg=self.couleur_texte, relief='flat', bd=2, highlightthickness=1, highlightbackground=self.couleur_accent, highlightcolor=self.couleur_accent)
            entry.grid(row=i+1, column=1, padx=20, pady=10, sticky='w')
            valeurs_defaut = [0, 1, 2, 1, 1]
            cle = i + 1
            if cle in self.dico_bateaux_a_poser:
                valeur = self.dico_bateaux_a_poser[cle]
            else:
                valeur = valeurs_defaut[i]
            valeur_actuelle = str(valeur)
            entry.insert(0, valeur_actuelle)
            self.valeurs_initiales.append(valeur_actuelle)
            self.widgets.append(entry)
            self.form_nb_bateaux.append(entry)

        #Ajout des boutons Valider et Retour
        bouton_valider = Button(self.fenetre_menu, text='Valider', command=lambda: (self.jouer_bouton_bleu(), self.valider_et_quitter()), font=('Helvetica', 14, 'bold'), bg=self.couleur_accent, fg="#ffffff", activebackground=self.couleur_survol, activeforeground="#ffffff", relief='flat', bd=0, padx=40, pady=15, cursor='hand2')
        bouton_valider.grid(row=7, column=0, padx=20, pady=(30, 15), sticky='ew')
        bouton_valider.bind("<Enter>", lambda e: e.widget.config(bg=self.couleur_survol))
        bouton_valider.bind("<Leave>", lambda e: e.widget.config(bg=self.couleur_accent))
        self.widgets.append(bouton_valider)

        bouton_retour = Button(self.fenetre_menu, text='Retour', command=lambda: (self.jouer_bouton_gris(), self.verifier_modifications()), font=('Helvetica', 14, 'bold'), bg="#b0bec5", fg=self.couleur_texte, activebackground="#cfd8dc", activeforeground=self.couleur_texte, relief='flat', bd=0, padx=40, pady=15, cursor='hand2')
        bouton_retour.grid(row=7, column=1, padx=20, pady=(30, 15), sticky='ew')
        bouton_retour.bind("<Enter>", lambda e: e.widget.config(bg="#cfd8dc"))
        bouton_retour.bind("<Leave>", lambda e: e.widget.config(bg="#b0bec5"))
        self.widgets.append(bouton_retour)
        self.fenetre_menu.grid_columnconfigure(0, weight=1)

    def afficher_volume(self):
        #Affiche la fenêtre de réglage du volume
        self.clear_widgets()
        label_text_principal_volume = Label(self.fenetre_menu, text='Volume', font=('Helvetica', 32, 'bold'), bg=self.couleur_fond, fg="#0277bd", pady=30)
        label_text_principal_volume.grid(row=0, column=0, columnspan=2, padx=50, pady=(20, 10))
        self.widgets.append(label_text_principal_volume)

        label_voix = Label(self.fenetre_menu, text='Volume des voix', font=('Helvetica', 12), bg=self.couleur_fond, fg=self.couleur_texte)
        label_voix.grid(row=1, column=1, padx=10, pady=(10, 2), sticky='w')
        self.widgets.append(label_voix)

        self.label_hp_voix = Label(self.fenetre_menu, image=self.img_hp_3, bg=self.couleur_fond, cursor='hand2')
        self.label_hp_voix.grid(row=2, column=0, padx=(50, 10), pady=5)
        self.label_hp_voix.bind("<Button-1>", lambda e: self.toggle_mute_voix())
        self.widgets.append(self.label_hp_voix)

        frame_scale_voix = Frame(self.fenetre_menu, bg='#ffffff', relief='flat', bd=2, highlightbackground=self.couleur_accent, highlightthickness=1)
        frame_scale_voix.grid(row=2, column=1, padx=(10, 50), pady=5, sticky='w')
        self.widgets.append(frame_scale_voix)

        scale_volume_voix = Scale(frame_scale_voix, variable=self.volume_voix, from_=0, to=100, length=280, orient='horizontal', font=('Helvetica', 10), bg='#ffffff', fg=self.couleur_texte, troughcolor=self.couleur_accent, activebackground=self.couleur_survol, highlightthickness=0, cursor='hand2', relief='flat', bd=0, sliderrelief='raised', width=15, command=lambda v: self.changer_volume_voix())
        scale_volume_voix.pack(padx=8, pady=8)
        self.widgets.append(scale_volume_voix)

        label_musique = Label(self.fenetre_menu, text='Volume de la musique', font=('Helvetica', 12), bg=self.couleur_fond, fg=self.couleur_texte)
        label_musique.grid(row=3, column=1, padx=10, pady=(10, 2), sticky='w')
        self.widgets.append(label_musique)

        self.label_hp_musique = Label(self.fenetre_menu, image=self.img_hp_3, bg=self.couleur_fond, cursor='hand2')
        self.label_hp_musique.grid(row=4, column=0, padx=(50, 10), pady=5)
        self.label_hp_musique.bind("<Button-1>", lambda e: [self.toggle_mute_musique(), self.actuliser_musique_menu()])
        self.widgets.append(self.label_hp_musique)

        frame_scale_musique = Frame(self.fenetre_menu, bg='#ffffff', relief='flat', bd=2, highlightbackground=self.couleur_accent, highlightthickness=1)
        frame_scale_musique.grid(row=4, column=1, padx=(10, 50), pady=5, sticky='w')
        self.widgets.append(frame_scale_musique)

        scale_volume_musique = Scale(frame_scale_musique, variable=self.volume_musique, from_=0, to=100, length=280, orient='horizontal', font=('Helvetica', 10), bg='#ffffff', fg=self.couleur_texte, troughcolor=self.couleur_accent, activebackground=self.couleur_survol, highlightthickness=0, cursor='hand2', relief='flat', bd=0, sliderrelief='raised', width=15, command=lambda v: self.changer_volume_musique())
        scale_volume_musique.pack(padx=8, pady=8)
        self.widgets.append(scale_volume_musique)

        label_ui = Label(self.fenetre_menu, text='Volume du menu', font=('Helvetica', 12), bg=self.couleur_fond, fg=self.couleur_texte)
        label_ui.grid(row=5, column=1, padx=10, pady=(10, 2), sticky='w')
        self.widgets.append(label_ui)

        self.label_hp_ui = Label(self.fenetre_menu, image=self.img_hp_3, bg=self.couleur_fond, cursor='hand2')
        self.label_hp_ui.grid(row=6, column=0, padx=(50, 10), pady=5)
        self.label_hp_ui.bind("<Button-1>", lambda e: self.toggle_mute_ui())
        self.widgets.append(self.label_hp_ui)

        frame_scale_ui = Frame(self.fenetre_menu, bg='#ffffff', relief='flat', bd=2, highlightbackground=self.couleur_accent, highlightthickness=1)
        frame_scale_ui.grid(row=6, column=1, padx=(10, 50), pady=5, sticky='w')
        self.widgets.append(frame_scale_ui)

        scale_volume_ui = Scale(frame_scale_ui, variable=self.volume_ui, from_=0, to=100, length=280, orient='horizontal', font=('Helvetica', 10), bg='#ffffff', fg=self.couleur_texte, troughcolor=self.couleur_accent, activebackground=self.couleur_survol, highlightthickness=0, cursor='hand2', relief='flat', bd=0, sliderrelief='raised', width=15, command=lambda v: self.changer_volume_ui())
        scale_volume_ui.pack(padx=8, pady=8)
        self.widgets.append(scale_volume_ui)

        bouton_choix_son = Button(self.fenetre_menu, text='Choix des sons', command=lambda: (self.jouer_bouton_bleu(), self.afficher_choix_pack_volume()), font=('Helvetica', 14, 'bold'), bg=self.couleur_accent, fg="#ffffff", activebackground=self.couleur_survol, activeforeground="#ffffff", relief='flat', bd=0, padx=40, pady=15, cursor='hand2')
        bouton_choix_son.grid(row=7, column=0, columnspan=2, padx=50, pady=(20, 15), sticky='ew')
        bouton_choix_son.bind("<Enter>", lambda e: e.widget.config(bg=self.couleur_survol))
        bouton_choix_son.bind("<Leave>", lambda e: e.widget.config(bg=self.couleur_accent))
        self.widgets.append(bouton_choix_son)
        
        if not self.en_jeu:
            texte = "Retour"
            fonction = lambda: self.afficher_parametres()
        else:
            texte = "Quitter"
            fonction = self.fenetre_menu.destroy
        bouton_retour = Button(self.fenetre_menu, text=texte, command=lambda: (self.jouer_bouton_gris(), fonction()), font=('Helvetica', 14, 'bold'), bg="#b0bec5", fg=self.couleur_texte, activebackground="#cfd8dc", activeforeground=self.couleur_texte, relief='flat', bd=0, padx=40, pady=15, cursor='hand2')
        bouton_retour.grid(row=8, column=0, columnspan=2, padx=50, pady=15, sticky='ew')
        bouton_retour.bind("<Enter>", lambda e: e.widget.config(bg="#cfd8dc"))
        bouton_retour.bind("<Leave>", lambda e: e.widget.config(bg="#b0bec5"))
        self.widgets.append(bouton_retour)
        self.fenetre_menu.grid_columnconfigure(1, weight=1)
        self.update_icon_voix()
        self.update_icon_musique()
        self.update_icon_ui()

    def afficher_choix_pack_volume(self):
        #Affiche la fenêtre de choix des packs de sons
        self.clear_widgets()
        
        label_titre = Label(self.fenetre_menu, text='Choix des packs de sons', 
                        font=('Helvetica', 32, 'bold'), bg=self.couleur_fond, 
                        fg="#0277bd", pady=30)
        label_titre.grid(row=0, column=0, columnspan=2, padx=50, pady=(20, 10))
        self.widgets.append(label_titre)
        
        label_pack = Label(self.fenetre_menu, text='Pack de sons : ', 
                        font=('Helvetica', 12), bg=self.couleur_fond, 
                        fg=self.couleur_texte)
        label_pack.grid(row=1, column=0, padx=10, pady=(10, 2), sticky='e')
        self.widgets.append(label_pack)
        
        frame_liste = Frame(self.fenetre_menu, bg='#ffffff', relief='flat', 
                        bd=2, highlightbackground=self.couleur_accent, 
                        highlightthickness=1)
        frame_liste.grid(row=1, column=1, padx=(10, 50), pady=5, sticky='ew')
        self.widgets.append(frame_liste)
        
        self.listbox_packs = Listbox(frame_liste, height=5, font=('Helvetica', 11), 
                                    bg='#ffffff', fg=self.couleur_texte, 
                                    relief='flat', bd=0, highlightthickness=0, 
                                    cursor='hand2', selectmode=SINGLE)
        self.listbox_packs.pack(padx=8, pady=8, fill='both', expand=True)
        self.widgets.append(self.listbox_packs)
        
        # Remplir la listbox avec les packs disponibles
        pack_ids_list = []
        for pack_id, pack_data in UI_menu.packs_sons.items():
            if pack_id == "personnalise":
                self.listbox_packs.insert(END, pack_data["nom_affichage"])
                pack_ids_list.append(pack_id)
            else:
                self.listbox_packs.insert(END, pack_data["nom_affichage"])
                pack_ids_list.append(pack_id)
        
        if UI_menu.pack_actuel in pack_ids_list:
            self.listbox_packs.selection_set(pack_ids_list.index(UI_menu.pack_actuel))
        
        self.pack_ids_list = pack_ids_list
        
        self.listbox_packs.bind('<<ListboxSelect>>', lambda e: [self.appliquer_pack_auto()])
        
        bouton_retour = Button(self.fenetre_menu, text='Retour', 
                            command=lambda: [self.afficher_volume(), self.jouer_bouton_gris()], 
                            font=('Helvetica', 14, 'bold'), bg="#b0bec5", 
                            fg=self.couleur_texte, activebackground="#cfd8dc", 
                            activeforeground=self.couleur_texte, relief='flat', 
                            bd=0, padx=40, pady=15, cursor='hand2')
        bouton_retour.grid(row=3, column=0, columnspan=2, padx=50, pady=(20, 15), sticky='ew')
        bouton_retour.bind("<Enter>", lambda e: e.widget.config(bg="#cfd8dc"))
        bouton_retour.bind("<Leave>", lambda e: e.widget.config(bg="#b0bec5"))
        self.widgets.append(bouton_retour)
        
        self.fenetre_menu.grid_columnconfigure(1, weight=1)

    def afficher_mode_socket(self):
        #Affiche la fenêtre pour jouer en réseau via une connexion socket
        self.clear_widgets()

        label_text_principal_ip = Label(self.fenetre_menu, text='Jouer contre un joueur', font=('Helvetica', 32, 'bold'), bg=self.couleur_fond, fg="#0277bd", pady=30)
        label_text_principal_ip.grid(row=0, column=0, columnspan=2, padx=50, pady=(20, 10))
        self.widgets.append(label_text_principal_ip)
        
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(("8.8.8.8", 80))
        ip_locale = s.getsockname()[0]
        s.close()

        label_ip_titre = Label(self.fenetre_menu, text='Votre adresse IP :', font=('Helvetica', 14, 'bold'), bg=self.couleur_fond, fg=self.couleur_texte, pady=20)
        label_ip_titre.grid(row=1, column=0, columnspan=1, padx=50, pady=(10, 5))
        self.widgets.append(label_ip_titre)
        
        # Affichage de l'adresse IP dans un cadre
        frame_ip = Frame(self.fenetre_menu, bg='#ffffff', relief='flat', bd=2, highlightbackground=self.couleur_accent, highlightthickness=2)
        frame_ip.grid(row=1, column=1, columnspan=1, padx=50, pady=5)
        self.widgets.append(frame_ip)
        label_ip = Label(frame_ip, text=ip_locale, font=('Helvetica', 16, 'bold'), bg='#ffffff', fg=self.couleur_accent, padx=30, pady=15)
        label_ip.pack()
        self.widgets.append(label_ip)

        # Bouton pour copier l'adresse IP dans le presse-papiers
        bouton_copier = Button(self.fenetre_menu, text='Copier l\'adresse IP', command=lambda: [self.jouer_bouton_bleu(), self.copier(ip_locale)], font=('Helvetica', 14, 'bold'), bg=self.couleur_accent, fg="#ffffff", activebackground=self.couleur_survol, activeforeground="#ffffff", relief='flat', bd=0, padx=40, pady=15, cursor='hand2')
        bouton_copier.grid(row=3, column=0, columnspan=2, padx=50, pady=(10, 20), sticky='ew')
        bouton_copier.bind("<Enter>", lambda e: e.widget.config(bg=self.couleur_survol))
        bouton_copier.bind("<Leave>", lambda e: e.widget.config(bg=self.couleur_accent))
        self.widgets.append(bouton_copier)

        # Choix du mode : serveur ou client
        self.mode_reseau = StringVar(value="serveur")
        frame_choix = Frame(self.fenetre_menu, bg=self.couleur_fond)
        frame_choix.grid(row=4, column=0, columnspan=2, padx=50, pady=(10, 20))
        self.widgets.append(frame_choix)

        # Radio boutons pour choisir le mode
        radio_serveur = Radiobutton(frame_choix, text="Être le serveur", variable=self.mode_reseau, value="serveur", command=self.basculer_mode_reseau, font=('Helvetica', 12), bg=self.couleur_fond, fg=self.couleur_texte, selectcolor=self.couleur_fond, activebackground=self.couleur_fond, activeforeground=self.couleur_texte, cursor='hand2')
        radio_serveur.pack(side='left', padx=20)
        self.widgets.append(radio_serveur)

        radio_client = Radiobutton(frame_choix, text="Être le client", variable=self.mode_reseau, value="client", command=self.basculer_mode_reseau, font=('Helvetica', 12), bg=self.couleur_fond, fg=self.couleur_texte, selectcolor=self.couleur_fond, activebackground=self.couleur_fond, activeforeground=self.couleur_texte, cursor='hand2')
        radio_client.pack(side='left', padx=20)
        self.widgets.append(radio_client)

        # Cadre pour saisir l'adresse IP du serveur (visible uniquement si mode client)
        self.frame_client = Frame(self.fenetre_menu, bg=self.couleur_fond)
        self.frame_client.grid(row=5, column=0, columnspan=2, padx=50, pady=(10, 20))
        self.widgets.append(self.frame_client)

        # Texte et champ de saisie de l'adresse IP
        label_connect = Label(self.frame_client, text='Se connecter à un joueur', font=('Helvetica', 14, 'bold'), bg=self.couleur_fond, fg=self.couleur_texte, pady=10)
        label_connect.grid(row=0, column=0, columnspan=2, padx=0, pady=(10, 5))

        label_saisir = Label(self.frame_client, text='Adresse IP :', font=('Helvetica', 12), bg=self.couleur_fond, fg=self.couleur_texte)
        label_saisir.grid(row=1, column=0, padx=20, pady=10, sticky='e')

        self.entry_ip = Entry(self.frame_client, font=('Helvetica', 12), bg="#ffffff", fg=self.couleur_texte, relief='flat', bd=2, highlightthickness=1, highlightbackground=self.couleur_accent, highlightcolor=self.couleur_accent, width=25)
        self.entry_ip.grid(row=1, column=1, padx=20, pady=10, sticky='w')
        
        self.bouton_se_connecter = Button(self.frame_client, text='Se connecter', command=lambda: [self.jouer_bouton_bleu(), self.tester_connexion_client()], font=('Helvetica', 14, 'bold'), bg=self.couleur_accent, fg="#ffffff", activebackground=self.couleur_survol, activeforeground="#ffffff", relief='flat', bd=0, padx=40, pady=15, cursor='hand2')
        self.bouton_se_connecter.grid(row=2, column=0, columnspan=2, padx=0, pady=(10, 10), sticky='ew')
        self.bouton_se_connecter.bind("<Enter>", lambda e: e.widget.config(bg=self.couleur_survol) if e.widget['state'] == 'normal' else None)
        self.bouton_se_connecter.bind("<Leave>", lambda e: e.widget.config(bg=self.couleur_accent) if e.widget['state'] == 'normal' else None)
        
        # Label pour afficher les messages d'erreur/succès
        self.label_status_connexion = Label(self.frame_client, text='', font=('Helvetica', 11), bg=self.couleur_fond, fg='red')
        self.label_status_connexion.grid(row=3, column=0, columnspan=2, padx=0, pady=(5, 0))
        
        # Variable pour indiquer si le client est connecté
        self.client_connecte = False
        

        if self.mode_reseau.get() == "serveur":
            self.bouton_parametre = Button(
                self.fenetre_menu, text='Paramètres',
                command=lambda: (self.jouer_bouton_bleu(), self.afficher_parametres()),
                font=('Helvetica', 14, 'bold'), bg=self.couleur_accent, fg="#ffffff",
                activebackground=self.couleur_survol, activeforeground="#ffffff",
                relief='flat', bd=0, padx=40, pady=15, cursor='hand2'
            )
            self.bouton_parametre.grid(row=6, column=0, columnspan=2, padx=50, pady=15, sticky='ew')
            self.bouton_parametre.bind("<Enter>", lambda e: e.widget.config(bg=self.couleur_survol))
            self.bouton_parametre.bind("<Leave>", lambda e: e.widget.config(bg=self.couleur_accent))
            self.widgets.append(self.bouton_parametre)
        else:
            self.bouton_parametre = None


        # Bouton pour valider et lancer la partie en réseau
        self.bouton_valider = Button(self.fenetre_menu, text='Valider', command=lambda: (self.jouer_bouton_bleu(), self.valider_connexion()), font=('Helvetica', 14, 'bold'), bg=self.couleur_accent, fg="#ffffff", activebackground=self.couleur_survol, activeforeground="#ffffff", relief='flat', bd=0, padx=40, pady=15, cursor='hand2', state='normal')
        self.bouton_valider.grid(row=7, column=0, columnspan=2, padx=50, pady=15, sticky='ew')
        self.bouton_valider.bind("<Enter>", lambda e: e.widget.config(bg=self.couleur_survol) if e.widget['state'] == 'normal' else None)
        self.bouton_valider.bind("<Leave>", lambda e: e.widget.config(bg=self.couleur_accent) if e.widget['state'] == 'normal' else None)
        self.widgets.append(self.bouton_valider)
        
        # Bouton retour au menu précédent
        self.bouton_retour_socket = Button(self.fenetre_menu, text='Retour', command=lambda: [self.jouer_bouton_gris(), self.afficher_mode_jeu(), self.changer_retour_parametre_false()], font=('Helvetica', 14, 'bold'), bg="#b0bec5", fg=self.couleur_texte, activebackground="#cfd8dc", activeforeground=self.couleur_texte, relief='flat', bd=0, padx=40, pady=15, cursor='hand2')
        self.bouton_retour_socket.grid(row=8, column=0, columnspan=2, padx=50, pady=(20, 15), sticky='ew')
        self.bouton_retour_socket.bind("<Enter>", lambda e: e.widget.config(bg="#cfd8dc"))
        self.bouton_retour_socket.bind("<Leave>", lambda e: e.widget.config(bg="#b0bec5"))
        self.widgets.append(self.bouton_retour_socket)

        # Configuration des colonnes de la grille
        self.fenetre_menu.grid_columnconfigure(0, weight=1)
        self.fenetre_menu.grid_columnconfigure(1, weight=1)
        self.frame_client.grid_remove()
    
    def basculer_mode_reseau(self):
        #Change l'affichage en fonction du mode sélectionné (serveur ou client)
        if self.mode_reseau.get() == "client":
            self.jouer_bouton_bleu()
            self.frame_client.grid()
            self.bouton_valider.config(state='disabled', bg="#90caf9", cursor='arrow')
            self.client_connecte = False
            if hasattr(self, 'bouton_parametre') and self.bouton_parametre:
                self.bouton_parametre.grid_remove()
            self.bouton_valider.grid(row=6)
            self.bouton_retour_socket.grid(row=7)

        else:
            self.jouer_bouton_gris()
            self.frame_client.grid_remove()
            self.bouton_valider.config(state='normal', bg=self.couleur_accent, cursor='hand2')
            if hasattr(self, 'bouton_parametre') and self.bouton_parametre:
                self.bouton_parametre.grid()
            self.bouton_valider.grid(row=7)
            self.bouton_retour_socket.grid(row=8)

    def tester_connexion_client(self):
        #Teste la connexion au serveur en mode client
        ip = self.entry_ip.get().strip()
        
        # Validation basique de l'IP
        if not self.valider_ip(ip):
            self.label_status_connexion.config(text='Adresse IP invalide', fg='red')
            return
        
        self.label_status_connexion.config(text='Connexion en cours...', fg='orange')
        self.bouton_se_connecter.config(state='disabled')
        
        thread = threading.Thread(target=self.connexion_client_thread, args=(ip,))
        thread.daemon = True
        thread.start()
    
    def connexion_client_thread(self, ip):
        #Thread pour tester la connexion sans bloquer l'interface
        try:
            test_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            test_socket.settimeout(5)
            test_socket.connect((ip, 5000))
            test_socket.close()
            
            # Succès de la connexion
            self.fenetre_menu.after(0, self.connexion_reussie)
        except Exception as e:
            # Échec de la connexion
            self.fenetre_menu.after(0, self.connexion_echouee)
    
    def connexion_reussie(self):
        #"Appelé quand la connexion au serveur réussit"""
        self.client_connecte = True
        self.label_status_connexion.config(text='Joueur trouvé', fg='green')
        self.entry_ip.config(state='disabled')
        self.bouton_se_connecter.grid_remove()
        
        # Sauvegarder l'adresse IP avant de désactiver l'entry
        self.ip_client_sauvegarde = self.entry_ip.get().strip()
        
        # Changer le texte du bouton Valider en "Je suis prêt"
        self.bouton_valider.config(text='Je suis prêt', 
                                command=lambda: [self.jouer_bouton_bleu(), self.client_est_pret()])
        self.bouton_valider.config(state='normal', bg=self.couleur_accent, cursor='hand2')
        self.bouton_valider.bind("<Enter>", lambda e: e.widget.config(bg=self.couleur_survol))
        self.bouton_valider.bind("<Leave>", lambda e: e.widget.config(bg=self.couleur_accent))
    
    def connexion_echouee(self):
        #Appelé quand la connexion au serveur échoue
        self.label_status_connexion.config(text='Joueur non trouvé', fg='red')
        self.bouton_se_connecter.config(state='normal')
        self.client_connecte = False

    def client_est_pret(self):
        #Appelé quand le client est prêt
        # Utiliser uniquement les variables d'instance
        self.client_pret = True
        
        # Changer le bouton pour indiquer l'attente
        self.bouton_valider.config(text='En attente du serveur...', 
                                state='disabled', 
                                bg="#90caf9", 
                                cursor='arrow')
        self.bouton_valider.unbind("<Enter>")
        self.bouton_valider.unbind("<Leave>")
        
        # Envoyer un signal au serveur que le client est prêt
        try:
            signal_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            signal_socket.connect((self.ip_client_sauvegarde, 5001))
            signal_socket.sendall(b'CLIENT_PRET\n')
            signal_socket.close()
        except Exception as e:
            print(f"Erreur d'envoi du signal prêt: {e}")
        
        # Afficher la fenêtre d'attente
        self.afficher_attente_client()

    def valider_ip(self, ip):
        #Valide le format d'une adresse IP
        parties = ip.split('.')
        if len(parties) != 4:
            return False
        try:
            return all(0 <= int(partie) <= 255 for partie in parties)
        except ValueError:
            return False
    
    def valider_connexion(self):
        #Valide la connexion et affiche la fenêtre d'attente appropriée
        if self.mode_reseau.get() == "serveur":
            self.afficher_attente_serveur()
        else:
            if self.client_connecte:
                self.afficher_attente_client()
    
    def afficher_attente_serveur(self):
        #Affiche la fenêtre d'attente pour le serveur
        self.clear_widgets()
        
        label_titre = Label(self.fenetre_menu, text='Attente d\'un joueur', font=('Helvetica', 32, 'bold'), bg=self.couleur_fond, fg="#0277bd", pady=30)
        label_titre.grid(row=0, column=0, padx=50, pady=(30, 20))
        self.widgets.append(label_titre)
        
        # Chargement et affichage du GIF animé
        self.gif_frames = []
        self.gif_index = 0
        try:
            gif = Image.open("Images/load.gif")
            while True:
                frame = gif.copy()
                frame = frame.resize((200, 200), Image.Resampling.LANCZOS)
                self.gif_frames.append(ImageTk.PhotoImage(frame))
                gif.seek(len(self.gif_frames))
        except EOFError:
            pass
        
        self.label_gif = Label(self.fenetre_menu, bg=self.couleur_fond)
        self.label_gif.grid(row=1, column=0, padx=50, pady=30)
        self.widgets.append(self.label_gif)
        
        self.animer_gif()
        
        # Démarrer le serveur dans un thread
        self.serveur_en_attente = True
        thread = threading.Thread(target=self.demarrer_serveur_thread)
        thread.daemon = True
        thread.start()
        
        bouton_retour = Button(self.fenetre_menu, text='Annuler', command=lambda: [self.jouer_bouton_gris(), self.annuler_attente_serveur()], font=('Helvetica', 14, 'bold'), bg="#b0bec5", fg=self.couleur_texte, activebackground="#cfd8dc", activeforeground=self.couleur_texte, relief='flat', bd=0, padx=40, pady=15, cursor='hand2')
        bouton_retour.grid(row=2, column=0, padx=50, pady=15, sticky='ew')
        bouton_retour.bind("<Enter>", lambda e: e.widget.config(bg="#cfd8dc"))
        bouton_retour.bind("<Leave>", lambda e: e.widget.config(bg="#b0bec5"))
        self.widgets.append(bouton_retour)
    
    def demarrer_serveur_thread(self):
        #Thread pour démarrer le serveur et attendre une connexion
        try:
            HOST = "0.0.0.0"
            PORT = 5000
            self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.server_socket.bind((HOST, PORT))
            self.server_socket.listen(1)
            self.server_socket.settimeout(1)  # Timeout pour permettre l'annulation
            
            while self.serveur_en_attente:
                try:
                    conn, addr = self.server_socket.accept()
                    self.conn = conn
                    self.fenetre_menu.after(0, self.joueur_connecte_serveur)
                    break
                except socket.timeout:
                    continue
        except Exception as e:
            print(f"Erreur serveur: {e}")
    
    def joueur_connecte_serveur(self):
        #Appelé quand un joueur se connecte au serveur
        self.serveur_en_attente = False
        self.label_gif.grid_remove()
        
        label_connecte = Label(self.fenetre_menu, text='Joueur connecté', 
            font=('Helvetica', 24, 'bold'), bg=self.couleur_fond, fg='green', pady=30)
        label_connecte.grid(row=1, column=0, padx=50, pady=30)
        self.widgets.append(label_connecte)
        
        # Supprimer le bouton Annuler en filtrant la liste
        widgets_filtres = [w for w in self.widgets if not (isinstance(w, Button) and w.cget('text') == 'Annuler')]
        for widget in self.widgets:
            if isinstance(widget, Button) and widget.cget('text') == 'Annuler':
                widget.destroy()
        self.widgets = widgets_filtres
        
        # Créer le label d'attente du client
        self.label_attente_client = Label(self.fenetre_menu, 
            text='En attente que le client soit prêt...', 
            font=('Helvetica', 14), bg=self.couleur_fond, fg='orange', pady=15)
        self.label_attente_client.grid(row=2, column=0, padx=50, pady=10)
        self.widgets.append(self.label_attente_client)
        
        # Le bouton Lancer est désactivé au début
        self.bouton_lancer = Button(self.fenetre_menu, text='Lancer', 
            command=lambda: [self.jouer_bouton_bleu(), self.lancer_partie_serveur()], 
            font=('Helvetica', 14, 'bold'), bg="#90caf9", fg="#ffffff", 
            activebackground=self.couleur_survol, activeforeground="#ffffff", 
            relief='flat', bd=0, padx=40, pady=15, cursor='arrow', state='disabled')
        self.bouton_lancer.grid(row=3, column=0, padx=50, pady=15, sticky='ew')
        self.widgets.append(self.bouton_lancer)
        
        # Démarrer un thread pour écouter le signal du client
        thread = threading.Thread(target=self.attendre_client_pret)
        thread.daemon = True
        thread.start()

    def attendre_signal_serveur(self):
        #Thread pour attendre le signal de lancement du serveur
        try:
            # Créer un socket d'écoute sur le port 5002
            listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            listen_socket.bind(("0.0.0.0", 5002))
            listen_socket.listen(1)
            listen_socket.settimeout(300)  # Timeout de 5 minutes
            
            conn, addr = listen_socket.accept()
            data = conn.recv(1024).decode().strip()
            
            if data == 'LANCER_PARTIE':
                self.fenetre_menu.after(0, self.lancer_partie_client)
            
            conn.close()
            listen_socket.close()
        except Exception as e:
            print(f"Erreur d'attente du signal serveur: {e}")

    def annuler_attente_serveur(self):
        #Annule l'attente du serveur
        self.serveur_en_attente = False
        if hasattr(self, 'server_socket'):
            try:
                self.server_socket.close()
            except:
                pass
        self.afficher_mode_socket()
    
    def afficher_attente_client(self):
        #Affiche la fenêtre d'attente pour le client
        self.clear_widgets()
        
        label_titre = Label(self.fenetre_menu, text='En attente du serveur', 
            font=('Helvetica', 32, 'bold'), bg=self.couleur_fond, fg="#0277bd", pady=30)
        label_titre.grid(row=0, column=0, padx=50, pady=(30, 20))
        self.widgets.append(label_titre)
        
        # Chargement et affichage du GIF animé
        self.gif_frames = []
        self.gif_index = 0
        try:
            gif = Image.open("Images/load.gif")
            while True:
                frame = gif.copy()
                frame = frame.resize((200, 200), Image.Resampling.LANCZOS)
                self.gif_frames.append(ImageTk.PhotoImage(frame))
                gif.seek(len(self.gif_frames))
        except EOFError:
            pass
        
        self.label_gif = Label(self.fenetre_menu, bg=self.couleur_fond)
        self.label_gif.grid(row=1, column=0, padx=50, pady=30)
        self.widgets.append(self.label_gif)
        
        self.animer_gif()
        
        # Démarrer un thread pour attendre le signal du serveur
        thread = threading.Thread(target=self.attendre_signal_serveur)
        thread.daemon = True
        thread.start()
    
    def attendre_client_pret(self):
        #Thread pour attendre que le client soit prêt
        try:
            # Créer un socket d'écoute sur le port 5001
            listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            listen_socket.bind(("0.0.0.0", 5001))
            listen_socket.listen(1)
            listen_socket.settimeout(300)  # Timeout de 5 minutes
            
            conn, addr = listen_socket.accept()
            data = conn.recv(1024).decode().strip()
            
            if data == 'CLIENT_PRET':
                # Utiliser les variables d'instance au lieu de globales
                self.serveur_pret = True
                self.fenetre_menu.after(0, self.activer_bouton_lancer)
            
            conn.close()
            listen_socket.close()
        except Exception as e:
            print(f"Erreur d'attente du client: {e}")
    
    def lancer_partie_client(self):
        #Lance la partie côté client
        # Utiliser l'IP sauvegardée au lieu de l'entry
        self.jouer_contre_joueur_socket('client', self.ip_client_sauvegarde)

    def animer_gif(self):
        #Anime le GIF en changeant les frames
        if self.gif_frames and hasattr(self, 'label_gif') and self.label_gif.winfo_exists():
            self.label_gif.config(image=self.gif_frames[self.gif_index])
            self.gif_index = (self.gif_index + 1) % len(self.gif_frames)
            self.fenetre_menu.after(50, self.animer_gif)

    def attendre_signal_serveur(self):
        #Thread pour attendre le signal de lancement du serveur
        try:
            # Créer un socket d'écoute sur le port 5002
            listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            listen_socket.bind(("0.0.0.0", 5002))
            listen_socket.listen(1)
            listen_socket.settimeout(300)
            
            conn, addr = listen_socket.accept()
            data = conn.recv(1024).decode().strip()
            
            if data == 'LANCER_PARTIE':
                self.fenetre_menu.after(0, self.lancer_partie_client)
            
            conn.close()
            listen_socket.close()
        except Exception as e:
            print(f"Erreur d'attente du signal serveur: {e}")

    def activer_bouton_lancer(self):
        #Active le bouton Lancer quand le client est prêt
        self.label_attente_client.config(text='Client prêt', fg='green')
        self.bouton_lancer.config(state='normal', bg=self.couleur_accent, cursor='hand2')
        self.bouton_lancer.bind("<Enter>", lambda e: e.widget.config(bg=self.couleur_survol))
        self.bouton_lancer.bind("<Leave>", lambda e: e.widget.config(bg=self.couleur_accent))

    def lancer_partie_serveur(self):
        #Lance la partie côté serveur
        # Envoyer un signal au client pour lancer la partie
        try:
            signal_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            client_ip = self.conn.getpeername()[0]
            signal_socket.connect((client_ip, 5002))
            signal_socket.sendall(b'LANCER_PARTIE\n')
            signal_socket.close()
        except Exception as e:
            print(f"Erreur d'envoi du signal de lancement: {e}")
        
        self.jouer_contre_joueur_socket('serveur', '')

    def lancer_partie_client(self):
        #Lance la partie côté client
        self.jouer_contre_joueur_socket('client', self.ip_client_sauvegarde)

    def jouer_contre_joueur_socket(self, mode_reseau, ip_adresse):
        #Lance une partie contre un autre joueur en réseau via une connexion socket
        self.mode_jeu = 'socket_' + str(mode_reseau)
        self.ip_adresse_serveur = ip_adresse if mode_reseau == 'client' else ''
        self.musique_menu.stop()
        self.fenetre_menu.destroy()

        self.bouton_se_connecter.config(state='normal', bg=self.couleur_accent, cursor='hand2')

    def activer_bouton_lancer(self):
        #Active le bouton Lancer quand le client est prêt
        self.label_attente_client.config(text='Client prêt', fg='green')
        self.bouton_lancer.config(state='normal', bg=self.couleur_accent, cursor='hand2')
        self.bouton_lancer.bind("<Enter>", lambda e: e.widget.config(bg=self.couleur_survol))
        self.bouton_lancer.bind("<Leave>", lambda e: e.widget.config(bg=self.couleur_accent))

    def lancer_partie_serveur(self):
        #Lance la partie côté serveur
        # Envoyer un signal au client pour lancer la partie
        try:
            signal_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            # Récupérer l'IP du client depuis conn
            client_ip = self.conn.getpeername()[0]
            signal_socket.connect((client_ip, 5002))
            signal_socket.sendall(b'LANCER_PARTIE\n')
            signal_socket.close()
        except Exception as e:
            print(f"Erreur d'envoi du signal de lancement: {e}")
        
        # Fermer la fenêtre et lancer le jeu
        self.jouer_contre_joueur_socket('serveur', '')

    def update_icon_voix(self):
        # Met à jour l'icône du volume de la voix en fonction de la valeur du scale
        volume = self.volume_voix.get()
        if volume == 0:
            self.label_hp_voix.config(image=self.img_hp_0)
        elif volume <= 33:
            self.label_hp_voix.config(image=self.img_hp_1)
        elif volume <= 66:
            self.label_hp_voix.config(image=self.img_hp_2)
        else:
            self.label_hp_voix.config(image=self.img_hp_3)

    def update_icon_musique(self):
        # Met à jour l'icône du volume de la musique en fonction de la valeur du scale
        volume = self.volume_musique.get()
        if volume == 0:
            self.label_hp_musique.config(image=self.img_hp_0)
        elif volume <= 33:
            self.label_hp_musique.config(image=self.img_hp_1)
        elif volume <= 66:
            self.label_hp_musique.config(image=self.img_hp_2)
        else:
            self.label_hp_musique.config(image=self.img_hp_3)

    def update_icon_ui(self):
        # Met à jour l'icône du volume du menu en fonction de la valeur du scale
        volume = self.volume_ui.get()
        if volume == 0:
            self.label_hp_ui.config(image=self.img_hp_0)
        elif volume <= 33:
            self.label_hp_ui.config(image=self.img_hp_1)
        elif volume <= 66:
            self.label_hp_ui.config(image=self.img_hp_2)
        else:
            self.label_hp_ui.config(image=self.img_hp_3)

    def toggle_mute_voix(self):
        # Bascule le mode muet pour la voix
        if self.volume_voix.get() == 0:
            self.volume_voix.set(UI_menu.volume_voix_avant_mute_global)
            UI_menu.volume_voix_global = UI_menu.volume_voix_avant_mute_global
            self.jouer_bouton_bleu()
        else:
            UI_menu.volume_voix_avant_mute_global = self.volume_voix.get()
            self.volume_voix.set(0)
            UI_menu.volume_voix_global = 0
            self.jouer_bouton_gris()
        self.update_icon_voix()

    def toggle_mute_musique(self):
        # Bascule le mode muet pour la musique
        if self.volume_musique.get() == 0:
            self.volume_musique.set(UI_menu.volume_musique_avant_mute_global)
            UI_menu.volume_musique_global = UI_menu.volume_musique_avant_mute_global
            if UI_menu.musique_en_jeu_ref != None:
                UI_menu.musique_en_jeu_ref.set_volume(self.volume_musique.get()*0.01)
            self.jouer_bouton_bleu()
        else:
            UI_menu.volume_musique_avant_mute_global = self.volume_musique.get()
            self.volume_musique.set(0)
            UI_menu.volume_musique_global = 0
            if UI_menu.musique_en_jeu_ref != None:
                UI_menu.musique_en_jeu_ref.set_volume(self.volume_musique.get()*0.01)
            self.jouer_bouton_gris()
        self.update_icon_musique()

    def toggle_mute_ui(self):
        # Bascule le mode muet pour le menu
        if self.volume_ui.get() == 0:
            self.volume_ui.set(UI_menu.volume_ui_avant_mute_global)
            UI_menu.volume_ui_global = UI_menu.volume_ui_avant_mute_global
            self.jouer_bouton_bleu()
        else:
            UI_menu.volume_ui_avant_mute_global = self.volume_ui.get()
            self.volume_ui.set(0)
            UI_menu.volume_ui_global = 0
            self.jouer_bouton_gris()
        self.update_icon_ui()

    def afficher_mode_jeu(self):
        #Affiche la fenêtre de choix du mode de jeu
        self.clear_widgets()

        # Texte principal
        label_text_principal_mode = Label(self.fenetre_menu, text='Mode de jeu', font=('Helvetica', 32, 'bold'), bg=self.couleur_fond, fg="#0277bd", pady=40)
        label_text_principal_mode.grid(row=0, column=0, padx=50, pady=(30, 20))
        self.widgets.append(label_text_principal_mode)


        # Boutons de choix du mode de jeu
        bouton_joueur = Button(self.fenetre_menu, text='Jouer contre un joueur', command=lambda: (self.jouer_bouton_bleu(), self.jouer_contre_joueur()), font=('Helvetica', 14, 'bold'), bg=self.couleur_accent, fg="#ffffff", activebackground=self.couleur_survol, activeforeground="#ffffff", relief='flat', bd=0, padx=40, pady=15, cursor='hand2')
        bouton_joueur.grid(row=1, column=0, padx=50, pady=15, sticky='ew')
        bouton_joueur.bind("<Enter>", lambda e: e.widget.config(bg=self.couleur_survol))
        bouton_joueur.bind("<Leave>", lambda e: e.widget.config(bg=self.couleur_accent))
        self.widgets.append(bouton_joueur)

        bouton_ia = Button(self.fenetre_menu, text='Jouer contre une ia', command=lambda: (self.jouer_bouton_bleu(), self.jouer_contre_ia()), font=('Helvetica', 14, 'bold'), bg=self.couleur_accent, fg="#ffffff", activebackground=self.couleur_survol, activeforeground="#ffffff", relief='flat', bd=0, padx=40, pady=15, cursor='hand2')
        bouton_ia.grid(row=2, column=0, padx=50, pady=15, sticky='ew')
        bouton_ia.bind("<Enter>", lambda e: e.widget.config(bg=self.couleur_survol))
        bouton_ia.bind("<Leave>", lambda e: e.widget.config(bg=self.couleur_accent))
        self.widgets.append(bouton_ia)

        bouton_jouer_contre_joueur = Button(self.fenetre_menu, text='Jouer contre un joueur à distance', command=lambda: (self.jouer_bouton_bleu(), self.afficher_mode_socket(), self.changer_retour_parametre_true()), font=('Helvetica', 14, 'bold'), bg=self.couleur_accent, fg="#ffffff", activebackground=self.couleur_survol, activeforeground="#ffffff", relief='flat', bd=0, padx=40, pady=15, cursor='hand2')
        bouton_jouer_contre_joueur.grid(row=3, column=0, padx=50, pady=15, sticky='ew')
        bouton_jouer_contre_joueur.bind("<Enter>", lambda e: e.widget.config(bg=self.couleur_survol))
        bouton_jouer_contre_joueur.bind("<Leave>", lambda e: e.widget.config(bg=self.couleur_accent))
        self.widgets.append(bouton_jouer_contre_joueur)


        # Bouton retour au menu principal
        bouton_retour = Button(self.fenetre_menu, text='Retour', command=lambda: (self.jouer_bouton_gris(), self.afficher_menu_principal()), font=('Helvetica', 14, 'bold'), bg="#b0bec5", fg=self.couleur_texte, activebackground="#cfd8dc", activeforeground=self.couleur_texte, relief='flat', bd=0, padx=40, pady=15, cursor='hand2')
        bouton_retour.grid(row=4, column=0, padx=50, pady=15, sticky='ew')
        bouton_retour.bind("<Enter>", lambda e: e.widget.config(bg="#cfd8dc"))
        bouton_retour.bind("<Leave>", lambda e: e.widget.config(bg="#b0bec5"))
        self.widgets.append(bouton_retour)
        self.fenetre_menu.grid_columnconfigure(0, weight=1)

    def afficher_credits(self):
        #Affiche la fenêtre des crédits
        self.clear_widgets()
        label_text_principal_credits = Label(self.fenetre_menu, text='Crédits', font=('Helvetica', 32, 'bold'), bg=self.couleur_fond, fg="#0277bd", pady=40)
        label_text_principal_credits.grid(row=0, column=0, padx=50, pady=(30, 20))
        self.widgets.append(label_text_principal_credits)

        frame_scrollbar = Frame(self.fenetre_menu, bg=self.couleur_fond)
        frame_scrollbar.grid(row=1, column=0, padx=50, pady=15, sticky='nsew')
        self.widgets.append(frame_scrollbar)

        scrollbar = Scrollbar(frame_scrollbar)
        scrollbar.pack(side='right', fill='y')
        html_label = HTMLLabel(frame_scrollbar, yscrollcommand=scrollbar.set, background="#ffffff")
        html_label.pack(side='left', fill='both', expand=True)
        scrollbar.config(command=html_label.yview)
        self.widgets.append(scrollbar)
        self.widgets.append(html_label)

        with open('bataille_des_mers_eternelles.html', 'r', encoding='utf-8') as f:
            contenu_html = f.read()
            html_label.set_html(contenu_html)
        bouton_retour = Button(self.fenetre_menu, text='Retour', command=lambda: (self.jouer_bouton_gris(), self.afficher_menu_principal()), font=('Helvetica', 14, 'bold'), bg="#b0bec5", fg=self.couleur_texte, activebackground="#cfd8dc", activeforeground=self.couleur_texte, relief='flat', bd=0, padx=40, pady=15, cursor='hand2')
        bouton_retour.grid(row=2, column=0, padx=50, pady=15, sticky='ew')
        bouton_retour.bind("<Enter>", lambda e: e.widget.config(bg="#cfd8dc"))
        bouton_retour.bind("<Leave>", lambda e: e.widget.config(bg="#b0bec5"))
        self.widgets.append(bouton_retour)
        self.fenetre_menu.grid_rowconfigure(1, weight=1)
        self.fenetre_menu.grid_columnconfigure(0, weight=1)

    def changer_retour_parametre_true(self):
        self.retour_menu = True

    def changer_retour_parametre_false(self):
        self.retour_menu = False

    def valider_et_quitter(self):
        #Récupère les nouvelles valeurs des paramètres et retourne au menu principal
        self.dico_bateaux_a_poser = self.recuperer_taille_bateaux()
        self.afficher_parametres()

    def quitter_fenetre(self):
        #Quitte complètement le menu et arrête la musique
        self.musique_menu.stop()
        self.fenetre_menu.destroy()
      
    def recuperer_taille_bateaux(self):
        #Récupère le nombre de bateaux de chaque taille à poser
        try:
            nb_bateaux_1 = int(self.form_nb_bateaux[0].get())
            nb_bateaux_2 = int(self.form_nb_bateaux[1].get())
            nb_bateaux_3 = int(self.form_nb_bateaux[2].get())
            nb_bateaux_4 = int(self.form_nb_bateaux[3].get())
            nb_bateaux_5 = int(self.form_nb_bateaux[4].get())
            if nb_bateaux_1 == 0 and nb_bateaux_2 == 0 and nb_bateaux_3 == 0 and nb_bateaux_4 == 0 and nb_bateaux_5 == 0:
                nb_bateaux_1 = 0
                nb_bateaux_2 = 1
                nb_bateaux_3 = 2
                nb_bateaux_4 = 1
                nb_bateaux_5 = 1
        except:
            nb_bateaux_1 = 0
            nb_bateaux_2 = 1
            nb_bateaux_3 = 2
            nb_bateaux_4 = 1
            nb_bateaux_5 = 1
        return {1 : nb_bateaux_1, 2 : nb_bateaux_2, 3 : nb_bateaux_3, 4 : nb_bateaux_4, 5 : nb_bateaux_5}
    
    def verifier_modifications(self):
        #Vérifie si des modifications ont été apportées aux paramètres des bateaux
        modifications = False
        for i, entry in enumerate(self.form_nb_bateaux):
            if entry.get() != self.valeurs_initiales[i]:
                modifications = True
        if modifications:
            self.afficher_popup_confirmation()
        else:
            self.afficher_parametres()

    def afficher_popup_confirmation(self):
        #Affiche une popup demandant si l'utilisateur veut enregistrer les modifications
        overlay = Frame(self.fenetre_menu, bg="#00233e")
        overlay.place(x=0, y=0, relwidth=1, relheight=1)
        self.widgets.append(overlay)
        popup_frame = Frame(overlay, bg="#ffffff", relief='solid', bd=3, highlightbackground=self.couleur_accent, highlightthickness=3)
        popup_frame.place(relx=0.5, rely=0.5, anchor='center', width=400, height=200)
        label_popup = Label(popup_frame, text="Des modifications ont été détectées.\nVoulez-vous enregistrer les changements ?", font=('Helvetica', 12), bg="#ffffff", fg=self.couleur_texte, pady=30, wraplength=350)
        label_popup.pack()
        frame_boutons = Frame(popup_frame, bg="#ffffff")
        frame_boutons.pack(pady=10)
        bouton_oui = Button(frame_boutons, text='Oui', command=lambda: [self.jouer_bouton_bleu(), overlay.destroy(), self.valider_et_quitter()], font=('Helvetica', 12, 'bold'), bg=self.couleur_accent, fg="#ffffff", relief='flat', bd=0, padx=30, pady=10, cursor='hand2')
        bouton_oui.pack(side='left', padx=10)
        bouton_oui.bind("<Enter>", lambda e: e.widget.config(bg=self.couleur_survol))
        bouton_oui.bind("<Leave>", lambda e: e.widget.config(bg=self.couleur_accent))
        bouton_non = Button(frame_boutons, text='Non', command=lambda: [self.jouer_bouton_gris(), overlay.destroy(), self.afficher_parametres()], font=('Helvetica', 12, 'bold'), bg="#b0bec5", fg=self.couleur_texte, relief='flat', bd=0, padx=30, pady=10, cursor='hand2')
        bouton_non.pack(side='left', padx=10)
        bouton_non.bind("<Enter>", lambda e: e.widget.config(bg="#cfd8dc"))
        bouton_non.bind("<Leave>", lambda e: e.widget.config(bg="#b0bec5"))

    def jouer_contre_ia(self):
        #Lance une partie contre l'IA
        self.mode_jeu = 'IA'
        self.musique_menu.stop()
        self.fenetre_menu.destroy()

    def jouer_contre_joueur(self):
        #Lance une partie contre un autre joueur en local
        self.mode_jeu = '2_j'
        self.musique_menu.stop()
        self.fenetre_menu.destroy()

    def jouer_contre_joueur_socket(self, mode_reseau, ip_adresse):
        #Lance une partie contre un autre joueur en réseau via une connexion socket
        self.mode_jeu = 'socket_' + str(mode_reseau)
        self.ip_adresse_serveur = ip_adresse
        self.musique_menu.stop()
        self.fenetre_menu.destroy()

    def jouer_bouton_bleu(self):
        #Joue le son d'un bouton bleu
        bouton_bleu = pygame.mixer.Sound(self.get_son_path("bouton_bleu"))
        bouton_bleu.set_volume(self.volume_ui.get()*0.01)
        bouton_bleu.play()

    def jouer_bouton_gris(self):
        #Joue le son d'un bouton gris
        bouton_gris = pygame.mixer.Sound(self.get_son_path("bouton_gris"))
        bouton_gris.set_volume(self.volume_ui.get()*0.01)
        bouton_gris.play()

    def jouer_clic(self):
        #Joue le son d'un clic
        clic = pygame.mixer.Sound(self.get_son_path("clic"))
        clic.set_volume(self.volume_ui.get()*0.01)
        clic.play()

    def actuliser_musique_menu(self):
        # Actualise le volume de la musique du menu
        self.musique_menu.set_volume(self.volume_musique.get()*0.01)

    def jouer_checkbutton_cibles(self):
        # Joue le bon son en fonction de si on coche ou pas la case checkbutton_cibles
        if self.voir_cibles_adverses.get():
            self.jouer_bouton_bleu()
        else:
            self.jouer_bouton_gris()

    def jouer_checkbutton_touch(self):
        # Joue le bon son en fonction de si on coche ou pas la case checkbutton_touch
        if self.can_touch.get():
            self.jouer_bouton_bleu()
        else:
            self.jouer_bouton_gris()

    def changer_volume_voix(self):
        # Change le volume global de la voix
        UI_menu.volume_voix_global = self.volume_voix.get()
        self.update_icon_voix()
        self.jouer_clic()

    def changer_volume_musique(self):
        # Change le volume global de la musique
        UI_menu.volume_musique_global = self.volume_musique.get()
        self.update_icon_musique()
        self.actuliser_musique_menu()
        if UI_menu.musique_en_jeu_ref != None:
            UI_menu.musique_en_jeu_ref.set_volume(self.volume_musique.get()*0.01)
        self.jouer_clic()

    def changer_volume_ui(self):
        # Change le volume global du menu
        UI_menu.volume_ui_global = self.volume_ui.get()
        self.update_icon_ui()
        self.jouer_clic()

    def copier(self, texte):
        # Copie le texte donné dans le presse-papiers
        self.fenetre_menu.clipboard_clear()
        self.fenetre_menu.clipboard_append(str(texte))
        self.fenetre_menu.update()

    def get_son_path(self, type_son, n=None):
        #Retourne le chemin du fichier son en fonction du type
        # Récupérer le chemin (personnalisé ou du pack actuel)
        if type_son in UI_menu.sons_personnalises:
            chemin = UI_menu.sons_personnalises[type_son]
        else:
            pack_actuel = UI_menu.pack_actuel
            if pack_actuel == "personnalise":
                pack_actuel = "principal"
            chemin = UI_menu.packs_sons[pack_actuel]["sons"].get(type_son, "")

        if n is not None:
            chemin = chemin.replace("{n}", str(n))
        
        return chemin

    def appliquer_pack_auto(self):
        #Applique automatiquement le pack sélectionne
        selection = self.listbox_packs.curselection()
        if not selection:
            return
        
        self.jouer_clic()
        
        pack_id = self.pack_ids_list[selection[0]]
        
        if pack_id == "personnalise":
            UI_menu.pack_actuel = "personnalise"
        else:
            UI_menu.pack_actuel = pack_id

            UI_menu.sons_personnalises = {}
            
            if not self.en_jeu:
                self.musique_menu.stop()
                son_path = UI_menu.packs_sons[pack_id]["sons"]["musique_menu"]
                self.musique_menu = pygame.mixer.Sound(son_path)
                self.musique_menu.set_volume(self.volume_musique.get()*0.01)
                self.musique_menu.play(loops=-1)
            
            if UI_menu.musique_en_jeu_ref is not None:
                UI_menu.musique_en_jeu_ref.stop()
                son_path = UI_menu.packs_sons[pack_id]["sons"]["musique_jeu"]
                UI_menu.musique_en_jeu_ref = pygame.mixer.Sound(son_path)
                UI_menu.musique_en_jeu_ref.set_volume(self.volume_musique.get()*0.01)
                UI_menu.musique_en_jeu_ref.play(loops=-1)


def on_mouvement(event, fenetre, plateau, orientation, taille, position_canva, can_touch):
    #Affiche la prévisualisation du bateau à la position de la souris
    x_case, y_case = fenetre.click_to_case(event.x, event.y)
    fenetre.afficher_previsualisation(plateau, x_case, y_case, orientation, taille, position_canva, can_touch)

def on_molette(event, fenetre, plateau, orientation, taille, position_canva, can_touch):
    #Change l'orientation du bateau et affiche la prévisualisation à la position de la souris
    orientation[0] = 1 - orientation[0]  # alterne entre 0 et 1
    x_case, y_case = fenetre.click_to_case(event.x, event.y)
    fenetre.afficher_previsualisation(plateau, x_case, y_case, orientation[0], taille, position_canva, can_touch)

def on_clique_droit(event, plateau, fenetre, canva_placement, text_ids, afficher_croix):
    #Enlève un bateau déjà placé sur le plateau
    x_case, y_case = fenetre.click_to_case(event.x, event.y)    #Converti les coordonnée de px en coordonnée de cases
    if plateau.plateau[x_case][y_case].type == 2:   #On regarde si la case cliquée est bien un bateau
        bateau_touche = [0,[0,0]]

        #On parcourt la liste de bateau sur le plateau pour trouver les autres cases du bateau
        for bateau in plateau.liste_bateau_restant: 
            for case in bateau[1]:
                if case == [x_case, y_case]:
                    bateau_touche = bateau
        taille_bateau = bateau_touche[0]

        #On convertit les cases de bateaux en cases vides
        for case_a_supprimer in bateau_touche[1]:
            plateau.plateau[case_a_supprimer[0]][case_a_supprimer[1]].type = 0

        fenetre.afficher_plateau(plateau.plateau, True, True, 'droit', afficher_croix) #Mise à jour de l'affichage
        plateau.liste_bateaux_a_poser.append(taille_bateau)            #Mise à jour de la liste des bateaux à poser

        index = taille_bateau - 1
        canva_placement.itemconfig(text_ids[0][index], text='× ' + str(plateau.liste_bateaux_a_poser.count(taille_bateau)))
        return taille_bateau
    return None





menu = UI_menu("Bataille navale", False)

musique_en_jeu = pygame.mixer.Sound(son_helper.get_son_path("musique_jeu"))
musique_en_jeu.set_volume(UI_menu.volume_musique_global*0.01)
musique_en_jeu.play(loops=-1)
UI_menu.musique_en_jeu_ref = musique_en_jeu

option_voir_cibles_adverses = menu.voir_cibles_adverses.get()
option_can_touch = menu.can_touch.get()
afficher_croix = False if option_can_touch == True else True
mode_jeu = menu.mode_jeu



plateau_joueur1 = Plateau()
plateau_joueur1.creation_plateau()
plateau_joueur2 = Plateau()
plateau_joueur2.creation_plateau()
fenetre1 = UI_game("joueur 1", 1)
fenetre2 = UI_game("Joueur 2", 2)

if mode_jeu == 'IA' or mode_jeu == 'socket_serveur':
    fenetre2.fenetre.withdraw()
elif mode_jeu == 'socket_client':
    fenetre1.fenetre.withdraw()

fenetre1.afficher_plateau(plateau_joueur1.plateau, True, True, 'gauche', afficher_croix)
fenetre1.afficher_plateau(plateau_joueur2.plateau, option_voir_cibles_adverses, True, 'droit', afficher_croix)
fenetre1.fenetre.update()

fenetre2.afficher_plateau(plateau_joueur1.plateau, True, True, 'gauche', afficher_croix)
fenetre2.afficher_plateau(plateau_joueur2.plateau, option_voir_cibles_adverses, True, 'droit', afficher_croix)
fenetre2.fenetre.update()



dico_bateaux_a_poser = menu.dico_bateaux_a_poser




#Définition des listes de joueurs en fonction du mode de jeu
if mode_jeu == '2_j':
    liste_joueur_humain = [1,2]
    liste_joueur_ia = []
    liste_joueur_socket = []
elif mode_jeu == 'IA':
    liste_joueur_humain = [1]
    liste_joueur_ia = [2]
    liste_joueur_socket = []
elif mode_jeu == 'socket_serveur':
    liste_joueur_humain = [1]
    liste_joueur_ia = []
    liste_joueur_socket = [2]
    HOST = "0.0.0.0"   # Pour écouter sur toutes les interfaces réseau
    PORT = 5500        # Port d'écoute du serveur
    # Création du socket TCP
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind((HOST, PORT))
    server.listen(1)

    # Accepter un client
    conn, addr = server.accept()
    print(f"[SERVEUR] Client connecté : {addr}")
    
    parametres = {
        "option_can_touch": option_can_touch,
        "option_voir_cibles_adverses": option_voir_cibles_adverses,
        "dico_bateaux_a_poser": dico_bateaux_a_poser
    }
    conn.sendall((json.dumps(parametres) + '\n').encode())
else:# mode_jeu == 'socket_client':
    liste_joueur_humain = [2]
    liste_joueur_ia = []
    liste_joueur_socket = [1]
    HOST = menu.ip_adresse_serveur   # Adresse du serveur (localhost)
    PORT = 5500          # Port du serveur

    # Création du socket TCP
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.connect((HOST, PORT))
    buffer = ""
    continuer = True
    while continuer:
        buffer += server.recv(4096).decode()
        if '\n' in buffer:
            data, buffer = buffer.split('\n', 1)
            continuer = False
    params = json.loads(data)
    option_can_touch = params["option_can_touch"]
    option_voir_cibles_adverses = params["option_voir_cibles_adverses"]
    dico_bateaux_a_poser = params["dico_bateaux_a_poser"]

#Création des listes de bateaux à poser pour chaque joueur
liste_bateaux_a_poser = []
for clef in dico_bateaux_a_poser:
    for i in range(dico_bateaux_a_poser[clef]):
        plateau_joueur1.liste_bateaux_a_poser.append(int(clef))
        plateau_joueur2.liste_bateaux_a_poser.append(int(clef))
       
afficher_croix = False if option_can_touch == True else True #On définit si on affiche les croix de non-contact ou pas

fenetre1.titre_information(fenetre1.fenetre,"C'est à vous de poser les bateaux",1,22,15)
fenetre1.bloc_canva(fenetre1.fenetre, "Vos bateaux à poser", '× ', plateau_joueur1.nb_bateau_restant_a_pose_par_taille(), 2, 22, "bleu")
fenetre1.titre_information(fenetre1.fenetre,"Click droit pour enlever un bateau",3,22,12)


fenetre2.titre_information(fenetre2.fenetre,"C'est à l'adversaire de poser ces bateaux",1,22,15)
fenetre2.titre_information(fenetre2.fenetre,"Click droit pour enlever un bateau",3,22,12)
fenetre2.bloc_canva(fenetre2.fenetre, "Vos bateaux à poser", '× ', plateau_joueur2.nb_bateau_restant_a_pose_par_taille(), 2, 22, "bleu")

fenetre1.canva_bind = 'droite'
fenetre2.canva_bind = 'droite'

#Boucle de placement des bateaux
for joueur in [1,2]:
    fenetre1.fenetre.protocol("WM_DELETE_WINDOW", on_close)
    fenetre2.fenetre.protocol("WM_DELETE_WINDOW", on_close)  
    #Placement des bateaux pour les joueurs humains
    if joueur in liste_joueur_humain:
        if joueur == 1:
            #plateau_joueur1.afficher_plateau(True, True)
            fenetre1.afficher_plateau(plateau_joueur1.plateau, option_voir_cibles_adverses, True, 'droit', afficher_croix)
            liste_bateaux_a_poser = plateau_joueur1.liste_bateaux_a_poser
            fenetre1.titres[0].configure(text="C'est à vous de poser les bateaux")
            fenetre2.titres[0].configure(text="C'est à l'adversaire de poser ces bateaux")
            fenetre1.fenetre.update()
            fenetre2.fenetre.update()
        else:
            #plateau_joueur2.afficher_plateau(True, True)
            fenetre2.afficher_plateau(plateau_joueur2.plateau, option_voir_cibles_adverses, True, 'droit', afficher_croix)
            liste_bateaux_a_poser = plateau_joueur2.liste_bateaux_a_poser
            fenetre1.titres[0].configure(text="C'est à l'adversaire de poser ces bateaux")
            fenetre2.titres[0].configure(text="C'est à vous de poser les bateaux")
            
            fenetre1.fenetre.update()
            fenetre2.fenetre.update()


        while len(liste_bateaux_a_poser) > 0:
            fenetre1.fenetre.protocol("WM_DELETE_WINDOW", on_close)
            fenetre2.fenetre.protocol("WM_DELETE_WINDOW", on_close)  
            taille = liste_bateaux_a_poser[0]
            bonne_position_bateau = False  
            bateau_supprime = False
            while not bonne_position_bateau:
                #coordonnee_case_x = int(input(f"Dans quel numéro de ligne veut tu placer le coin de ton bateau n° {indice+1} de taille {taille} ? : "))
                #coordonnee_case_y = int(input(f"Dans quel numéro de colonne veut tu placer le coin de ton bateau n° {indice+1} de taille {taille} ? : "))

                #orientation = int(input("Dans quel orientation ? (0 = droite, 1 = bas)"))

                orientation = [0]

                if joueur == 1:
                    fenetre1.canva_droite.bind("<Motion>", lambda event: on_mouvement(event, fenetre1, plateau_joueur1, orientation[0], taille, 'droite', option_can_touch))
                    fenetre1.canva_droite.bind("<MouseWheel>", lambda event: on_molette(event, fenetre1, plateau_joueur1, orientation, taille, 'droite', option_can_touch))
                    fenetre1.canva_droite.bind("<Button-3>", lambda event: on_clique_droit(event, plateau_joueur1, fenetre1, fenetre1.bloc_canva_var, fenetre1.canva_text_ids, afficher_croix))

                else:
                    fenetre2.canva_droite.bind("<Motion>", lambda event: on_mouvement(event, fenetre2, plateau_joueur2, orientation[0], taille, 'droite', option_can_touch))
                    fenetre2.canva_droite.bind("<MouseWheel>", lambda event: on_molette(event, fenetre2, plateau_joueur2, orientation, taille, 'droite', option_can_touch))
                    fenetre2.canva_droite.bind("<Button-3>", lambda event: on_clique_droit(event, plateau_joueur2, fenetre2, fenetre2.bloc_canva_var, fenetre2.canva_text_ids, afficher_croix))

                if joueur == 1:
                    coordonnee_case = fenetre1.attendre_click_case()
                else:
                    coordonnee_case = fenetre2.attendre_click_case()

                fenetre1.canva_droite.unbind("<Motion>")
                fenetre1.canva_droite.unbind("<MouseWheel>")
                fenetre1.canva_droite.unbind("<Button-3>")
                fenetre2.canva_droite.unbind("<Motion>")
                fenetre2.canva_droite.unbind("<MouseWheel>")
                fenetre2.canva_droite.unbind("<Button-3>")

                coordonnee_case_x = coordonnee_case[0]
                coordonnee_case_y = coordonnee_case[1]

                if joueur == 1:
                    bonne_position_bateau = plateau_joueur1.ajouter_bateau(coordonnee_case_x, coordonnee_case_y, orientation[0], taille, option_can_touch)
                    #plateau_joueur1.afficher_plateau(True, True)
                    fenetre1.afficher_plateau(plateau_joueur1.plateau, option_voir_cibles_adverses, True, 'droite', afficher_croix)
                    fenetre1.fenetre.update()
                    if bonne_position_bateau == True:
                        liste_bateaux_a_poser.remove(taille)
                        index = taille - 1
                        fenetre1.bloc_canva_var.itemconfig(fenetre1.canva_text_ids[0][index], text='× ' + str(liste_bateaux_a_poser.count(taille)))
                    num_joueur = 2
                else:
                    bonne_position_bateau = plateau_joueur2.ajouter_bateau(coordonnee_case_x, coordonnee_case_y, orientation[0], taille, option_can_touch)   
                    #plateau_joueur2.afficher_plateau(True, True)
                    fenetre2.afficher_plateau(plateau_joueur2.plateau, option_voir_cibles_adverses, True, 'droite', afficher_croix)
                    fenetre2.fenetre.update()
                    if bonne_position_bateau == True:
                        liste_bateaux_a_poser.remove(taille)
                        index = taille - 1
                        fenetre2.bloc_canva_var.itemconfig(fenetre2.canva_text_ids[0][index], text='× ' + str(liste_bateaux_a_poser.count(taille)))
        fenetre1.titres[0].configure(text="C'est à l'adversaire de poser ces bateaux")
        fenetre2.titres[0].configure(text="C'est à vous de poser les bateaux")
        fenetre1.fenetre.update()
        fenetre2.fenetre.update()   
        if mode_jeu == 'socket_serveur' or mode_jeu == 'socket_client':
            if joueur == 1:
                matrice = [[{ "type": case.type,"taille_bateau": case.taille_bateau,"position_sur_bateau": case.position_sur_bateau,"orientation_bateau": case.orientation_bateau} for case in ligne] for ligne in plateau_joueur1.plateau]
                payload = {"matrice": matrice,"liste_bateau_restant": plateau_joueur1.liste_bateau_restant}
            else:
                matrice = [[{ "type": case.type,"taille_bateau": case.taille_bateau,"position_sur_bateau": case.position_sur_bateau,"orientation_bateau": case.orientation_bateau} for case in ligne] for ligne in plateau_joueur2.plateau]
                payload = {"matrice": matrice,"liste_bateau_restant": plateau_joueur2.liste_bateau_restant}
            data = json.dumps(payload)   
            if mode_jeu == 'socket_serveur': 
                conn.sendall(data.encode() + b'\n')
            else:
                server.sendall(data.encode() + b'\n')
    
    #Placement des bateaux pour les joueurs IA
    elif joueur in liste_joueur_ia:
        if joueur == 1:
            plateau_joueur1.pose_bateaux_aleatoire(option_can_touch)
        else:
            plateau_joueur2.pose_bateaux_aleatoire(option_can_touch)
    
    #Placement des bateaux pour les joueurs en réseau (reception des données)
    else:#joueur in liste_joueur_socket
        if joueur == 1:
            fenetre1.titres[0].configure(text="C'est à vous de poser les bateaux")
            fenetre2.titres[0].configure(text="C'est à l'adversaire de poser ces bateaux")
            fenetre1.fenetre.update()
            fenetre2.fenetre.update()
        else:
            fenetre1.titres[0].configure(text="C'est à l'adversaire de poser ces bateaux")
            fenetre2.titres[0].configure(text="C'est à vous de poser les bateaux")
            
            fenetre1.fenetre.update()
            fenetre2.fenetre.update()
        
        # Réception des données
        buffer = ""
        continue_receiving = True
        while continue_receiving:
            if mode_jeu == 'socket_serveur':
                buffer += conn.recv(4096).decode()
            else:
                buffer += server.recv(4096).decode()
            if '\n' in buffer:
                data, buffer = buffer.split('\n', 1)
                matrice = json.loads(data)
                continue_receiving = False
        
        # Traitement des données reçues
        payload = json.loads(data)
        matrice = payload["matrice"]
        liste_bateau_restant = payload["liste_bateau_restant"]
        if joueur == 1:
            plateau_joueur1.liste_bateau_restant = liste_bateau_restant
        else:   
            plateau_joueur2.liste_bateau_restant = liste_bateau_restant
        
        # Mise à jour du plateau avec les données reçues
        for i, ligne in enumerate(matrice):
            for j, case_dict in enumerate(ligne):
                if joueur == 1:
                    case = plateau_joueur1.plateau[i][j]
                else:
                    case = plateau_joueur2.plateau[i][j]
                case.type = case_dict["type"]
                case.taille_bateau = case_dict["taille_bateau"]
                case.position_sur_bateau = case_dict["position_sur_bateau"]
                case.orientation_bateau = case_dict["orientation_bateau"]

#Préparation du début de la partie
plateau_joueur1.liste_bateau_total = copy.deepcopy(plateau_joueur1.liste_bateau_restant)
plateau_joueur2.liste_bateau_total = copy.deepcopy(plateau_joueur2.liste_bateau_restant)

# Nettoyage des fenêtres pour le début de la partie
for i in range(2):
    fenetre1.titres[i].destroy()
    fenetre2.titres[i].destroy()
for widget in fenetre1.canva_ids:
    widget.destroy()
for widget in fenetre2.canva_ids:
    widget.destroy()
fenetre1.vider_ids()
fenetre2.vider_ids()

# Affichage des informations de début de partie
fenetre1.titre_information(fenetre1.fenetre,"C'est à vous de jouer",1,22,15)
fenetre2.titre_information(fenetre2.fenetre,"C'est à l'adversaire de jouer",1,22,15)

fenetre1.bloc_canva(fenetre1.fenetre, "Vos bateaux restant", '× ', plateau_joueur1.nb_bateau_restant_par_taille(), 2, 22, "bleu")
fenetre2.bloc_canva(fenetre2.fenetre, "Vos bateaux restant", '× ', plateau_joueur2.nb_bateau_restant_par_taille(), 2, 22, "bleu")
fenetre1.bloc_canva(fenetre1.fenetre, "Les bateaux adverse", '× ', plateau_joueur1.nb_bateau_restant_par_taille(), 3, 22, "rouge")
fenetre2.bloc_canva(fenetre2.fenetre, "Les bateaux adverse", '× ', plateau_joueur2.nb_bateau_restant_par_taille(), 3, 22, "rouge")

joueur = 1
joueur_perdu = None
fin_du_jeux = False
coordonnee_case_x, coordonnee_case_y = 0,0
fenetre1.canva_bind = 'gauche'
fenetre2.canva_bind = 'gauche'

next_coordonnee_case = [None, None]
#Boucle principale du jeu
while not fin_du_jeux:
    fenetre1.fenetre.protocol("WM_DELETE_WINDOW", on_close)
    fenetre2.fenetre.protocol("WM_DELETE_WINDOW", on_close)  
    # affiche les plateaux
    #print("\n","Ton propre plateau qui sert à viser l'adversaire")
    if joueur == 1:
        #plateau_joueur2.afficher_plateau(True, False)
        #print("\n" , "Ton plateau avec tes bateaux")
        #plateau_joueur1.afficher_plateau(True, True)
        fenetre1.titres[0].configure(text="C'est à vous de jouer")
        fenetre2.titres[0].configure(text="C'est à l'adversaire de jouer")
        

    if joueur == 2:
        #plateau_joueur1.afficher_plateau(True, False)
        #print("\n" , "Ton plateau avec tes bateaux")
        #plateau_joueur2.afficher_plateau(True, True)
        fenetre1.titres[0].configure(text="C'est à l'adversaire de jouer")
        fenetre2.titres[0].configure(text="C'est à vous de jouer")

    fenetre1.afficher_plateau(plateau_joueur2.plateau, True, False, 'gauche', False)
    if not fenetre1.canva_cacher:
        fenetre1.afficher_plateau(plateau_joueur1.plateau, option_voir_cibles_adverses, True, 'droite', False)
    fenetre2.afficher_plateau(plateau_joueur1.plateau, True, False, 'gauche', False)
    if not fenetre2.canva_cacher:
        fenetre2.afficher_plateau(plateau_joueur2.plateau, option_voir_cibles_adverses, True, 'droite', False)
    if joueur not in liste_joueur_humain:
        fenetre1.fenetre.update()
        sleep(0.5)

    #print(f"C'est au joueur du joueur {joueur} de jouer")

    # Demande la case à ciblé
    bonne_position_cible = False
    while not bonne_position_cible:
        #coordonner_case_x = int(input("Dans quel numero de ligne veux tu cibler ?"))
        #coordonner_case_y = int(input("Dans quel numero de colonne veux tu cibler ? "))

        # Obtention des coordonnées de la case à cibler
        if joueur == 1:
            if 1 in liste_joueur_humain:
                fenetre1.canva_gauche.config(cursor="none")
                fenetre1.canva_gauche.bind("<Motion>", fenetre1.afficher_croix)
                fenetre1.canva_gauche.bind("<Leave>", fenetre1.cacher_croix)
                coordonnee_case = fenetre1.attendre_click_case()
                fenetre1.canva_gauche.config(cursor="arrow")
                fenetre1.canva_gauche.unbind("<Motion>")
                fenetre1.canva_gauche.unbind("<Leave>")
            elif 1 in liste_joueur_ia :
                coordonnee_case = plateau_joueur2.coup_IA(option_can_touch)
        else:
            if 2 in liste_joueur_humain:
                fenetre2.canva_gauche.config(cursor="none")
                fenetre2.canva_gauche.bind("<Motion>", fenetre2.afficher_croix)
                fenetre2.canva_gauche.bind("<Leave>", fenetre2.cacher_croix)
                coordonnee_case = fenetre2.attendre_click_case()
                fenetre2.canva_gauche.config(cursor="arrow")
                fenetre2.canva_gauche.unbind("<Motion>")
                fenetre2.canva_gauche.unbind("<Leave>")
            elif 1 in liste_joueur_ia :
                coordonnee_case = plateau_joueur1.coup_IA(option_can_touch)
        
        # réception des coordonnées si joueur en socket
        if joueur in liste_joueur_socket:
            if next_coordonnee_case == [None, None]:
                if mode_jeu == 'socket_serveur':
                    data = conn.recv(1024)
                else:
                    data = server.recv(1024)
                
                splited_data = data.decode().split()
                print(splited_data)
                if len(splited_data) == 2:
                    coordonnee_case_x, coordonnee_case_y = splited_data
                    coordonnee_case = [coordonnee_case_x, coordonnee_case_y]
                    next_coordonnee_case = [None, None]
                    """elif len(splited_data[0]) == 1 and len(splited_data[1]) == 2 and len(splited_data[2]) == 1:
                        coordonnee_case = [splited_data[0], splited_data[1][0]]
                        next_coordonnee_case = [splited_data[1][1], splited_data[2]]"""
                else:
                    coordonnee_case = [splited_data[0], splited_data[1][0]]
                    next_coordonnee_case = [splited_data[1][1]] + splited_data[2:]
            elif len(next_coordonnee_case) == 2:
                coordonnee_case = [next_coordonnee_case[0], next_coordonnee_case[1]]
                next_coordonnee_case = [None, None]
            else:
                coordonnee_case = [next_coordonnee_case[0], next_coordonnee_case[1][0]]
                next_coordonnee_case = [next_coordonnee_case[1][1]] + next_coordonnee_case[2:]
        
        
        coordonnee_case_x = int(coordonnee_case[0])
        coordonnee_case_y = int(coordonnee_case[1])

        # vérification de la validité de la case ciblée
        if joueur == 1:
            bonne_position_cible = plateau_joueur2.is_possible_cible(coordonnee_case_x, coordonnee_case_y)
        else:
            bonne_position_cible = plateau_joueur1.is_possible_cible(coordonnee_case_x, coordonnee_case_y)
    
    # envoi des coordonnées si joueur humain en mode socket
    if joueur in liste_joueur_humain and (mode_jeu == 'socket_serveur' or mode_jeu == 'socket_client'):
        concactened_data = f"{coordonnee_case_x} {coordonnee_case_y}"
        if mode_jeu == 'socket_serveur':
            conn.send(concactened_data.encode())
        else:
            server.send(concactened_data.encode())

    # cible une case et gestion bateaux plus fin de game
    if joueur == 1:
        if plateau_joueur2.cible_case(coordonnee_case_x, coordonnee_case_y) == True: # si bateau présent
            taille_bateau_restant = plateau_joueur2.enlever_case_bateau(coordonnee_case_x, coordonnee_case_y)
            plateau_joueur2.modifier_case(coordonnee_case_x, coordonnee_case_y, 3)
            if taille_bateau_restant[1] != 0: # s'il reste des parties non découvertes du bateau trouvé
                #print(f"Le bateau de taille {taille_bateau_restant[0]} a été touché il lui reste {taille_bateau_restant[1]} vie(s)")
                son = pygame.mixer.Sound(son_helper.get_son_path("bateau_touche"))
                son.set_volume(UI_menu.volume_voix_global*0.01)
                son.play()
            else: # si tout le bateau a été découvert
                nb_bateaux_restant = plateau_joueur2.nb_bateau_restant()
                son_bateaux_coule = pygame.mixer.Sound(son_helper.get_son_path("bateau_coule", taille_bateau_restant[0]))
                son_bateaux_coule.set_volume(UI_menu.volume_voix_global*0.01)
                channel = son_bateaux_coule.play()
                #print(f"Le bateau de taille {taille_bateau_restant[0]} a été coulé")

                idx = taille_bateau_restant[0] - 1   
                fenetre1.canva_ids[1].itemconfig(fenetre1.canva_text_ids[1][idx], text='× '+ str(plateau_joueur2.nb_bateau_restant_par_taille()[idx]))
                fenetre2.canva_ids[0].itemconfig(fenetre2.canva_text_ids[0][idx], text='× '+ str(plateau_joueur2.nb_bateau_restant_par_taille()[idx]))

                index_bateau_touche = 0
                for index_bateau, bateau in enumerate(plateau_joueur2.liste_bateau_total):
                    for case in bateau[1]:
                        if case == [coordonnee_case_x, coordonnee_case_y]:
                            index_bateau_touche = index_bateau
                for case in plateau_joueur2.liste_bateau_total[index_bateau_touche][1]:
                    plateau_joueur2.modifier_case(case[0], case[1], 5)
                                      

                if nb_bateaux_restant != 0: # s'il reste des bateaux
                    son_nb_bateaux_en_vie = pygame.mixer.Sound(son_helper.get_son_path("reste_bateau", nb_bateaux_restant))
                    son_nb_bateaux_en_vie.set_volume(UI_menu.volume_voix_global*0.01)
                    try:
                        channel.queue(son_nb_bateaux_en_vie)
                    except:
                        pass
                else: # s'il n'y a plus de bateau restant
                    son = pygame.mixer.Sound(son_helper.get_son_path("partie_terminee", 1))
                    son.set_volume(UI_menu.volume_voix_global*0.01)
                    son.play()                    
                    #print("Partie terminée, le joueur 1 a gagné")
                    joueur_perdu = 2
                    fin_du_jeux = True
        else: # une case vide
            plateau_joueur2.modifier_case(coordonnee_case_x, coordonnee_case_y, 1)
            #print("La case ne contient pas de bateaux")
            if next_coordonnee_case == [None, None]:
                joueur = 2

    elif joueur == 2:
        if plateau_joueur1.cible_case(coordonnee_case_x, coordonnee_case_y) == True: # si bateau présent
            taille_bateau_restant = plateau_joueur1.enlever_case_bateau(coordonnee_case_x, coordonnee_case_y)
            plateau_joueur1.modifier_case(coordonnee_case_x, coordonnee_case_y, 3)
            if taille_bateau_restant[1] != 0: # s'il reste des parties non découvertes du bateau trouvé
                #print(f"Le bateau de taille {taille_bateau_restant[0]} a été touché il lui reste {taille_bateau_restant[1]} vie(s)")
                son = pygame.mixer.Sound(son_helper.get_son_path("bateau_touche"))
                son.set_volume(UI_menu.volume_voix_global*0.01)
                son.play()
            else: # si tout le bateau a été découvert
                nb_bateaux_restant = plateau_joueur1.nb_bateau_restant()
                son_bateaux_coule = pygame.mixer.Sound(son_helper.get_son_path("bateau_coule", taille_bateau_restant[0]))
                son_bateaux_coule.set_volume(UI_menu.volume_voix_global*0.01)
                channel = son_bateaux_coule.play()
                idx = taille_bateau_restant[0] - 1
                fenetre1.canva_ids[0].itemconfig(fenetre1.canva_text_ids[0][idx], text='× '+ str(plateau_joueur1.nb_bateau_restant_par_taille()[idx]))
                fenetre2.canva_ids[1].itemconfig(fenetre2.canva_text_ids[1][idx], text='× '+ str(plateau_joueur1.nb_bateau_restant_par_taille()[idx]))

                index_bateau_touche = 0
                for index_bateau, bateau in enumerate(plateau_joueur1.liste_bateau_total):
                    for case in bateau[1]:
                        if case == [coordonnee_case_x, coordonnee_case_y]:
                            index_bateau_touche = index_bateau

                for case in plateau_joueur1.liste_bateau_total[index_bateau_touche][1]:
                    plateau_joueur1.modifier_case(case[0], case[1], 5)
                    


                if nb_bateaux_restant != 0: # s'il reste des bateaux
                    son_nb_bateaux_en_vie = pygame.mixer.Sound(son_helper.get_son_path("reste_bateau", nb_bateaux_restant))
                    son_nb_bateaux_en_vie.set_volume(UI_menu.volume_voix_global*0.01)
                    try:
                        channel.queue(son_nb_bateaux_en_vie)
                    except:
                        pass
                else: # s'il n'y a plus de bateau restant
                    #print(f"Partie terminée, le joueur 2 a gagné")
                    son = pygame.mixer.Sound(son_helper.get_son_path("partie_terminee", 2))
                    son.set_volume(UI_menu.volume_voix_global*0.01)
                    son.play() 
                    joueur_perdu = 1
                    fin_du_jeux = True
        else: # une case vide
            plateau_joueur1.modifier_case(coordonnee_case_x, coordonnee_case_y, 1)
            #print("La case ne contient pas de bateaux")
            if next_coordonnee_case == [None, None]:
                joueur = 1

# Affichage des plateaux mis à jour
fenetre1.afficher_plateau(plateau_joueur2.plateau, True, False, 'gauche', False)
fenetre1.afficher_plateau(plateau_joueur1.plateau, option_voir_cibles_adverses, True, 'droite', False)
fenetre2.afficher_plateau(plateau_joueur1.plateau, True, False, 'gauche', False)
fenetre2.afficher_plateau(plateau_joueur2.plateau, option_voir_cibles_adverses, True, 'droite', False)

vert = Image.new("RGBA", (480,480), (0, 255, 0, 120))
rouge = Image.new("RGBA", (480,480), (255, 0, 0, 120))

# Affichage de l'écran de fin
if joueur_perdu == 2:
    tk_vert = ImageTk.PhotoImage(vert, master=fenetre1.fenetre)
    tk_rouge = ImageTk.PhotoImage(rouge, master=fenetre2.fenetre)
    fenetre1.canva_droite.create_image(1,1,image=tk_vert, anchor=NW)
    fenetre1.canva_gauche.create_image(1,1,image=tk_vert, anchor=NW)
    fenetre2.canva_droite.create_image(1,1,image=tk_rouge, anchor=NW)
    fenetre2.canva_gauche.create_image(1,1,image=tk_rouge, anchor=NW)

    fenetre1.canva_droite.create_text(238,238,text="GAGNÉ",font=('Helvetica', 100, 'bold'),fill="#ffffff",anchor='center')
    fenetre1.canva_gauche.create_text(238,238,text="GAGNÉ",font=('Helvetica', 100, 'bold'),fill="#ffffff",anchor='center')
    fenetre2.canva_droite.create_text(238,238,text="PERDU",font=('Helvetica', 100, 'bold'),fill="#ffffff",anchor='center')
    fenetre2.canva_gauche.create_text(238,238,text="PERDU",font=('Helvetica', 100, 'bold'),fill="#ffffff",anchor='center')
if joueur_perdu == 1:
    tk_vert = ImageTk.PhotoImage(vert, master=fenetre2.fenetre)
    tk_rouge = ImageTk.PhotoImage(rouge, master=fenetre1.fenetre)

    fenetre1.canva_droite.create_image(1,1,image=tk_rouge, anchor=NW)
    fenetre1.canva_gauche.create_image(1,1,image=tk_rouge, anchor=NW)
    fenetre2.canva_droite.create_image(1,1,image=tk_vert, anchor=NW)
    fenetre2.canva_gauche.create_image(1,1,image=tk_vert, anchor=NW)
    
    fenetre1.canva_droite.create_text(238,238,text="PERDU",font=('Helvetica', 100, 'bold'),fill="#ffffff",anchor='center')
    fenetre1.canva_gauche.create_text(238,238,text="PERDU",font=('Helvetica', 100, 'bold'),fill="#ffffff",anchor='center')
    fenetre2.canva_droite.create_text(238,238,text="GAGNÉ",font=('Helvetica', 100, 'bold'),fill="#ffffff",anchor='center')
    fenetre2.canva_gauche.create_text(238,238,text="GAGNÉ",font=('Helvetica', 100, 'bold'),fill="#ffffff",anchor='center')


fenetre1.fenetre.protocol("WM_DELETE_WINDOW", on_close)
fenetre2.fenetre.protocol("WM_DELETE_WINDOW", on_close)   
mainloop()