"""
@author : Stéphane Pasquet
@date : 2025/01/02
@URL : https://www.mathweb.fr/euclide/2025/01/02/le-jeu-de-la-moyenne-inspire-dalice-in-borderland/
---------------
Règles du jeu:
    - Le joueur choisit un avatar au début d'une partie. Ce choix n'a pas d'incidence sur la logique du jeu. Il est purement esthétique. 
    - Au début du jeu, chaque joueur part avec 10 points.
    - Il entre un nombre entre 0 et 100 (compris) à chaque manche.
    - Le gagnant d'une manche est celui le plus proche de la moyenne des nombres donnés par tous les joueurs, à laquelle on a multiplié 0.8.
      Par exemple, si trois joueurs choisissent 20, 30 et 40, la moyenne est (20 + 30 + 40) / 3 = 30. Multipliée par 0.8, cela donne 24.
      Le joueur ayant choisi 20 est le plus proche de 24, donc c'est lui qui gagne.
    - Les joueurs ayant choisi le même nombre voient leur score diminué de 2 points et ces deux joueurs ne peuvent en aucun cas être gagnants de la manche.
      Dans ce cas, c'est le joueur avec la distance minimale unique qui gagne.
    - Le gagnant de la manche gagne 1 point, les autres perdent 1 point.
    - Le joueur ne doit pas arriver à un score égal à 0 sinon, il perd la partie.
    - Les joueurs dont le score devient nul ou négatif sont éliminés.
    - Les logiques de choix des nombres diffèrent d'un joueur à l'autre, et d'une manche à l'autre selon des règles aléatoirement programmées.
      Attention au joueur 4: c'est un fin stratège...

"""

import tkinter as tk
from tkinter import Canvas, simpledialog
from PIL import Image, ImageTk
import math
from random import randint, choice

class Game:
    def __init__(self):
        self.number_of_players = 5
        self.list_of_players = ['1', '2', '3', '4', '5']
        self.dict_of_scores = {'1': 10, '2': 10, '3': 10, '4': 10, '5': 10}
        self.dict_of_numbers = {'1': 0, '2': 0, '3': 0, '4': 0, '5': 0}
        self.player = None  # Le joueur n'est pas encore choisi
        self.num_of_parts = 1
        self.win = True
        self.cancelgame = False
        self.allnumbers = [] # stocke tous les nombres proposés depuis le début de la partie

        # Créer la fenêtre principale
        self.root = tk.Tk()
        self.root.title("Le jeu de la moyenne")
        self.root.geometry("1024x768")
        self.root.iconbitmap('icone.ico')

        # Charger l'image de fond
        self.background_image = Image.open("background.jpg")
        self.background_photo = ImageTk.PhotoImage(self.background_image)

        # Créer un canvas pour afficher l'image de fond
        self.canvas = Canvas(self.root, width=1024, height=768)
        self.canvas.pack(fill="both", expand=True)

        # Afficher l'image de fond sur le canvas
        self.canvas.create_image(0, 0, image=self.background_photo, anchor="nw")

        # Charger et redimensionner les images
        self.images = []
        for i in range(1, 6):
            img = Image.open(f"player_{i}.png")
            img = img.resize((100, 100), Image.Resampling.LANCZOS)
            self.images.append(ImageTk.PhotoImage(img))

        # Charger l'image de la croix
        self.cross_image = Image.open("croix.png")
        self.cross_image = self.cross_image.resize((100, 100), Image.Resampling.LANCZOS)
        self.cross_photo = ImageTk.PhotoImage(self.cross_image)

        # Calculer les positions des images sur le cercle
        center_x, center_y = 510, 390
        radius = 330  # Rayon du cercle
        angle_step = 360 / 5  # Angle entre chaque image

        self.image_positions = []
        self.image_rects = []
        self.cross_images = []
        for i, img in enumerate(self.images):
            angle = math.radians(10 + i * angle_step)
            x = center_x + radius * math.cos(angle)
            y = center_y - radius * math.sin(angle)
            self.image_positions.append((x, y))
            image_id = self.canvas.create_image(x, y, image=img, anchor="center")
            rect_id = self.canvas.create_rectangle(x - 50, y - 50, x + 50, y + 50, outline="", width=0)
            self.image_rects.append((image_id, rect_id))

            # Ajouter le texte sous l'image
            text_x = x
            text_y = y + 60  # Décaler le texte de 60 pixels sous l'image
            self.canvas.create_text(text_x, text_y, text=f"Joueur {i+1}", font=("Arial", 12), fill="white", anchor="center")

            # Ajouter une image de croix invisible au-dessus de chaque avatar
            cross_id = self.canvas.create_image(x, y, image=self.cross_photo, anchor="center", state="hidden")
            self.cross_images.append(cross_id)

        # Afficher le dictionnaire self.dict_of_scores en haut à gauche
        self.score_texts = {}
        y_offset = 40
        self.canvas.create_text(20, 20, text="Liste des scores", font=("Arial", 12), fill="green", anchor="w")
        for key, value in self.dict_of_scores.items():
            text_id = self.canvas.create_text(20, y_offset, text=f"Joueur {key}: {value}", font=("Arial", 12), fill="white", anchor="w")
            self.score_texts[key] = text_id
            y_offset += 20

        # Afficher le dictionnaire self.dict_of_numbers en haut à droite
        self.number_texts = {}
        y_offset = 40
        self.canvas.create_text(1004, 20, text="Liste des nombres choisis", font=("Arial", 12), fill="yellow", anchor="e")
        for key, value in self.dict_of_numbers.items():
            text_id = self.canvas.create_text(1004, y_offset, text=f"Joueur {key}: {value}", font=("Arial", 12), fill="white", anchor="e")
            self.number_texts[key] = text_id
            y_offset += 20
            
        # Afficher la consigne
        self.consigne = self.canvas.create_text(10, 750, text="Si vous ne voyez pas la boîte de dialogue, cliquez sur l'image", font=("Arial", 12), fill="yellow", anchor="w")


        # Afficher le texte "Choisissez votre joueur" au centre du cercle
        self.choose_text = self.canvas.create_text(center_x, center_y, text="Choisissez votre joueur", font=("Arial", 20), fill="white", anchor="center")

        # Lier l'événement de clic de la souris à la méthode on_click
        self.canvas.bind("<Button-1>", self.on_click)

        # Lancer la boucle principale de Tkinter
        self.root.mainloop()

    def on_click(self, event):
        # Vérifier si un joueur a déjà été choisi
        if self.player is not None:
            return

        # Vérifier si le clic est dans l'une des zones des images
        for i, (image_id, rect_id) in enumerate(self.image_rects):
            coords = self.canvas.coords(rect_id)
            if coords[0] <= event.x <= coords[2] and coords[1] <= event.y <= coords[3]:
                self.player = self.list_of_players[i]
                self.canvas.delete(self.choose_text)
                self.canvas.create_text(510, 390, text=f"Vous avez choisi le Joueur {i+1}", font=("Arial", 20), fill="white", anchor="center")

                # Ajouter le bouton "Commencer la partie"
                self.start_button = tk.Button(self.root, text="Commencer la partie", command=self.partie)
                self.start_button.place(x=460, y=450)
                break

    def partie(self):
        # Supprimer le bouton après avoir cliqué dessus
        self.start_button.destroy()

        while self.dict_of_scores[self.player] != 0 and len(self.list_of_players) != 1:
            # Demander au joueur d'entrer un nombre entre 0 et 100
            number = simpledialog.askinteger("Entrer un nombre", "Entrez un nombre entre 0 et 100:", minvalue=0, maxvalue=100)

            # Vérifier si l'utilisateur a annulé la boîte de dialogue
            if number is None:
                self.show_cancel_message()
                break

            # Stocker le nombre dans self.dict_of_numbers[self.player]
            self.dict_of_numbers[self.player] = number
            self.allnumbers.append(number)

            # Les autres joueurs choisissent
            for p in self.list_of_players:
                if p != self.player:
                    if p != 4:
                        n = self.choice_number(randint(1, 6))
                    else:
                        n = self.choice_number(7)
                        
                    self.dict_of_numbers[p] = n
                    self.allnumbers.append(n)
                        

            # Mettre à jour l'affichage des nombres choisis
            self.update_numbers_display()

            # Vérifier si un score est égal à 0
            if any(score == 0 for score in self.dict_of_scores.values()):
                self.eliminate()
        
        if not self.cancelgame:
            message_a = self.canvas.create_text(510, 200, text="Vous avez gagné!", font=("Arial", 30), fill="yellow", anchor="center")
            message_b = self.canvas.create_text(510, 300, text="Félicitations!", font=("Arial", 30), fill="yellow", anchor="center")
            # Supprimer le message après 5 secondes
            self.root.after(3000, lambda: self.canvas.delete(message_a))
            self.root.after(3000, lambda: self.canvas.delete(message_b))
        
    def show_cancel_message(self):
        self.cancelgame = True
        # Afficher un message d'abandon de la partie
        message = self.canvas.create_text(510, 500, text="Partie abandonnée!", font=("Arial", 30), fill="red", anchor="center")
        # Supprimer le message après 5 secondes
        self.root.after(5000, lambda: self.canvas.delete(message))

    def eliminate(self):
        # Éliminer les joueurs dont le score est égal à 0
        self.players_to_eliminate = [player for player, score in self.dict_of_scores.items() if score == 0]
        for player in self.players_to_eliminate:
            del self.dict_of_scores[player]
            del self.dict_of_numbers[player]
            self.list_of_players.remove(player)
            self.canvas.delete(self.score_texts[player])
            self.canvas.delete(self.number_texts[player])
            del self.score_texts[player]
            del self.number_texts[player]

            # Afficher une croix au-dessus de l'avatar du joueur éliminé
            player_index = int(player) - 1
            self.canvas.itemconfig(self.cross_images[player_index], state="normal")

            # Afficher un message pendant 5 secondes
            self.show_elimination_message(player)

        # Mettre à jour l'affichage des scores et des nombres choisis
        self.update_scores_display()
        self.update_numbers_display()

    def show_elimination_message(self, player):
        txt = f'{self.players_to_eliminate[0]}'
        if len(self.players_to_eliminate) > 1:
            for i in range(1,len(self.players_to_eliminate)-1):
                txt += f', {self.players_to_eliminate[i]}'
            txt += f' et {self.players_to_eliminate[-1]}'
            texte = f'Joueurs {txt} éliminés !'
        else:
            texte = f'Joueur {txt} éliminé !'
        # Afficher un message d'élimination
        message_a = self.canvas.create_text(510, 470, text=texte, font=("Arial", 30), fill="red", anchor="center")
        message_b = self.canvas.create_text(510, 520, text="Bon débarat!", font=("Arial", 30), fill="red", anchor="center")
        # Supprimer le message après 3 secondes
        self.root.after(3000, lambda: self.canvas.delete(message_a))
        self.root.after(3000, lambda: self.canvas.delete(message_b))
        if self.player in self.players_to_eliminate:
            self.canvas.create_text(510, 590, text="Fin de partie... loser!", font=("Arial", 40), fill="green", anchor="center")

    def update_scores_display(self):
        # Mettre à jour l'affichage des scores
        y_offset = 40
        for key, value in self.dict_of_scores.items():
            if key == self.player:
                couleur = 'yellow'
            else:
                couleur = 'white'
            self.canvas.itemconfig(self.score_texts[key], text=f"Joueur {key}: {value}", fill=couleur)
            self.canvas.coords(self.score_texts[key], 20, y_offset)
            y_offset += 20

    def update_numbers_display(self):
        # Mettre à jour l'affichage des nombres choisis
        y_offset = 40
        for key, value in self.dict_of_numbers.items():
            self.canvas.itemconfig(self.number_texts[key], text=f"Joueur {key}: {value}")
            self.canvas.coords(self.number_texts[key], 1004, y_offset)
            y_offset += 20

        self.moyenne = self.mean()
        self.winner = self.gagnant()

        for player, score in self.dict_of_scores.items():
            if player == self.winner:
                self.dict_of_scores[player] += 1
            else:
                self.dict_of_scores[player] -= 1
        
        # Vérifier les nombres choisis et ajuster les scores en conséquence
        number_counts = {}
        for number in self.dict_of_numbers.values():
            if number in number_counts:
                number_counts[number] += 1
            else:
                number_counts[number] = 1
        
        for number, count in number_counts.items():
            if count > 1:
                message = self.canvas.create_text(510, 520, text="Les joueurs ayant choisi le même nombre voient leur score diminué de 2 points.", font=("Arial", 12), fill="red", anchor="center")
                self.root.after(5000, lambda: self.canvas.delete(message))
                for player, num in self.dict_of_numbers.items():
                    if num == number:
                        self.dict_of_scores[player] -= 1
                        
        # Mettre à jour l'affichage des scores
        self.update_scores_display()

    def mean(self):
        somme = 0
        for number in self.dict_of_numbers.values():
            somme += number

        return int( (somme / len(self.list_of_players)) * 0.8 )

    def gagnant(self):
        dict_dist = {}
        dict_of_dist = {}
    
        # Calculer les distances entre les nombres choisis et la moyenne
        for player, number in self.dict_of_numbers.items():
            distance = abs(self.moyenne - number)
            dict_dist[player] = distance
    
            # Compter les occurrences de chaque distance
            if distance in dict_of_dist:
                dict_of_dist[distance] += 1
            else:
                dict_of_dist[distance] = 1
    
        # Vérifier si toutes les distances sont uniques
        if all(value == 1 for value in dict_of_dist.values()):
            # Si toutes les distances sont uniques, retourner le joueur avec la distance minimale
            min_distance = min(dict_dist.values())
            for player, dist in dict_dist.items():
                if dist == min_distance:
                    return player
        else:
            # Si certaines distances ne sont pas uniques, retourner le joueur avec la distance minimale unique
            potential_winners = [(player, dist) for player, dist in dict_dist.items() if dict_of_dist[dist] == 1]
            return min(potential_winners, key=lambda t: t[1])[0]

    def choice_number(self, n):
        match n:
            case 1: return randint(0,100)
            case 2: return randint(0,50)
            case 3: return randint(50,100)
            case 4: return randint(25,75)
            case 5: return choice([0,100])
            case 6: return 51
            case 7: return 50 if len(self.allnumbers==0) else sum(self.allnumbers)*0.8//len(self.numbers)
        

# Lancer le jeu
jeu = Game()
