"""
Module de définition de la classe jeu du pendu
"""
# imports et définitions
from random import randint
from string import ascii_lowercase

liste_mots = (
    "armoire",
    "boucle",
    "buisson",
    "bureau",
    "chaise",
    "carton",
    "couteau",
    "fichier",
    "garage",
    "glace",
    "journal",
    "kiwi",
    "lampe",
    "liste",
    "montagne",
    "remise",
    "sandale",
    "taxi",
    "vampire",
    "volant",
)


class Pendu:
    """
    Classe pour simuler une partie du jeu de pendu
    """

    # propriétés de classe, utiles pour le déroulement du jeu
    # le nombre d'erreurs du joueur
    nb_erreurs: int = 0
    # le mot choisi au hasard
    mot: str
    # le mot qui se dévoile au fil du jeu
    mot_revele: list

    def __init__(self, max_erreurs: int = 11) -> None:
        """
        constructeur: on rend possible le paramétrage du nombre d'erreurs max;
        par défaut, 11
        :param max_erreurs: type int, nombre d'erreurs max; 11 par défaut
        """
        # préparation de la partie
        # mise à jour de la propriété
        self.__max_erreurs = max_erreurs
        # détermination d'une position au hasard
        position = randint(0, len(liste_mots) - 1)
        # récupération du mot pour cette position
        self.mot = liste_mots[position]
        # préparation du mot à dévoiler (uniquement des "-" pour le moment)
        self.mot_revele = list("-" * len(self.mot))

    def get_max_erreurs(self) -> int:
        """
        Méthode pour accéder au nombre d'erreurs max en dehors de la classe (publique)
        :return: type int, le nombre max d'erreurs
        """
        return self.__max_erreurs

    def __controler_lettre(self, lettre: str) -> bool:
        """
        méthode pour vérifier que le caractère est correct !
        :param lettre: type str, la saisie à contrôler
        :return: type bool, le résultat du test
        """
        return lettre in ascii_lowercase and len(lettre) == 1

    def __demander_lettre(self) -> str:
        """
        méthode pour demander une lettre au joueur
        :return: str, la lettre saisie
        """
        # saisie de la lettre
        ok = False
        while not ok:
            lettre = input("Saisir une lettre: ")
            ok = self.__controler_lettre(lettre)
        return lettre

    def tester_lettre(self, lettre: str) -> None:
        """
        méthode vérifier que la lettre est bonne,et compléter le mot qui se dévoile
        dans le cas d'une bonne pioche
        :param lettre:
        :return:
        """
        if lettre in self.mot:
            self.__completer_mot(lettre)
        else:
            self.nb_erreurs += 1
            print(f"Nombre erreurs: {self.nb_erreurs}")

    def __completer_mot(self, lettre: str) -> None:
        """
        méthode pour compléter le mot au fil de l'eau
        :param lettre: type str, la lettre à dévoiler dans le mot mystère
        :return: None
        """
        # pour chaque position du mot choisi
        for position_lettre, lettre_mot in enumerate(self.mot):
            # est ce une bonne lettre ?
            if lettre_mot == lettre:
                # oui ! Révélons la !
                self.mot_revele[position_lettre] = lettre

    def __str__(self) -> str:
        """
        méthode pour effectuer le rendu string de notre objet
        utile par exemple sur un print(jeu)
        :return: type str, le rendu
        """
        # appel à la méthode dédiée, centrale, privée
        return self.__afficher_jeu()

    def __add__(self, other: int) -> None:
        """
        hack: permettre d'ajouter des essais en plus
        via l'opération +
        :param other: type int, le nombre d'essais en plus
        :return: None, agit directement sur l'objet
        """
        # un petit test tout de même
        if isinstance(other, int):
            self.__max_erreurs += other
        else:
            raise TypeError(
                f"opération add non compatible avec le type {type(other)}. Integer uniquement."
            )

    def __afficher_jeu(self) -> str:
        """
        Méthode centrale pour effectuer un affichage en string du jeu
        :return: type str, le rendu du jeu
        """
        # uniquement un formatage, sur plusieurs lignes.
        return f"""
Nb erreurs: {self.nb_erreurs} / {self.__max_erreurs}
Mot en cours: {''.join(self.mot_revele)}
"""

    def lancer_partie(self):
        """
        méthode centrale,lancement de la partie !
        :return:None
        """
        # TODO: proposer une autre boucle pour effectuer plusieurs parties ?
        # condition de sortie de la partie, avec les valeurs par défaut
        condition_perdu = False
        condition_gagne = False
        # tant qu'une des conditions n'est pas vraie...
        while not condition_perdu and not condition_gagne:
            # on donne de la visibilité: affichage de l'état de la partie
            print(self.__afficher_jeu())
            # print(self) -> même résultat ! Appelle la méthode
            # __str__ directement
            # saisie de la lettre par l'utilisateur
            lettre = self.__demander_lettre()
            # tester et complétion du mot à découvrir
            self.tester_lettre(lettre)
            # recalcul des conditions de sortie
            condition_perdu = self.nb_erreurs == self.__max_erreurs
            condition_gagne = "-" not in self.mot_revele
        # nous sommes sortis de la boucle... Mais pourquoi ?
        if condition_gagne:
            print("Bravo !")
        else:
            print(f"Perdu: {self.mot}")
        #un dernier affichage
        self.__afficher_jeu()
