from davistk import *
from random import randint
from time import sleep


# ==================== CONSTANTES GLOBALES ====================
LARGEUR_FENETRE = 400
HAUTEUR_FENETRE = 400
TAILLE_CARRE = 20
FREQUENCE_JEU = 10  # 1/10e de seconde entre chaque déplacement
POINTS_PAR_NOURRITURE = 10


def creer_serpent():
    """Crée le serpent initial au centre de la fenêtre.
    Le serpent est représenté par une liste de positions (x, y).
    Sa position initiale est au centre et il a une longueur de 3.
    
    Returns:
        list: liste de tuples (x, y) représentant le corps du serpent du cou à la queue
        
    >>> serpent = creer_serpent()
    >>> serpent
    [(200, 200), (180, 200), (160, 200)]
    >>> len(serpent)
    3
    >>> serpent[0]
    (200, 200)
    >>> serpent[1]
    (180, 200)
    """
    ... # A remplir (plusieurs lignes)

def creer_nourriture(serpent):
    """Crée une nourriture à une position aléatoire sur la fenêtre.
    La nourriture ne doit pas apparaître sur le corps du serpent.
    
    Parameters:
        serpent (list): liste des positions du serpent
        
    Returns:
        (int, int): position (x, y) de la nourriture

    >>> serpent = [(100, 100), (80, 100), (60, 100)]
    >>> nourriture = creer_nourriture(serpent)
    >>> nourriture not in serpent
    True
    >>> 0 <= nourriture[0] < LARGEUR_FENETRE
    True
    >>> 0 <= nourriture[1] < HAUTEUR_FENETRE
    True
    """
    ... # A remplir (plusieurs lignes)


def afficher_serpent(serpent):
    """Affiche le serpent sur la fenêtre. Chaque segment est un carré.
    
    Parameters:
        serpent (list): liste des positions du serpent
    """
    efface("serpent") # Efface l'ancien serpent grace aux tags (cf doc davistk)
    ... # Affiche chaque segment du serpent


def afficher_nourriture(nourriture):
    """Affiche la nourriture sur la fenêtre sous forme d'un carré rouge.
    
    Parameters:
        nourriture (tuple): position (x, y) de la nourriture
    """
    efface("nourriture") # Efface l'ancienne nourriture grace aux tags (cf doc davistk)
    ... # Affiche la nourriture


def afficher_score(score):
    """Affiche le score en haut à gauche de la fenêtre.
    
    Parameters:
        score (int): le score actuel
    """
    efface("score") # Efface l'ancien score grace aux tags (cf doc davistk)
    ... # Affiche le score


def gestion_evenement(direction_actuelle):
    """Gère les événements clavier et souris.
    Les touches fléchées contrôlent la direction du serpent.
    
    Parameters:
        direction_actuelle (str): la direction actuelle ('haut', 'bas', 'gauche', 'droite')
        
    Returns:
        (str, bool): nouvelle direction et True si on doit quitter le jeu
    """
    mise_a_jour()
    sleep(1/FREQUENCE_JEU)
    ev = donne_ev()
    tev = type_ev(ev)
    
    if tev == 'Touche':
        key = touche(ev)
        # Empêcher le serpent de faire demi-tour
        ... # A remplir (plusieurs lignes)
    elif tev == 'Quitte':
        return direction_actuelle, True
    else:
        return direction_actuelle, False


def deplacer_serpent(serpent, direction):
    """Déplace le serpent dans la direction donnée.
    Ajoute une nouvelle tête et enlève la queue.
    Modifie la liste des positions du serpent.
    
    Parameters:
        serpent (list): liste des positions du serpent
        direction (str): direction du mouvement ('haut', 'bas', 'gauche', 'droite')
        
    >>> serpent = [(100, 100), (80, 100), (60, 100)]   
    >>> deplacer_serpent(serpent, 'droite')
    >>> serpent
    [(120, 100), (100, 100), (80, 100)]
    >>> len(serpent)
    3
    >>> deplacer_serpent(serpent, 'haut')
    >>> serpent
    [(120, 80), (120, 100), (100, 100)]
    """
    ... # A remplir (plusieurs lignes)


def allonger_serpent(serpent, direction):
    """Allonge le serpent en ajoutant un nouveau segment à la tête.
    Utilisé quand le serpent mange de la nourriture.
    Modifie la liste des positions du serpent.
    
    Parameters:
        serpent (list): liste des positions du serpent
        direction (str): direction du mouvement
        
    >>> serpent = [(100, 100), (80, 100), (60, 100)]   
    >>> allonger_serpent(serpent, 'droite')
    >>> serpent
    [(120, 100), (100, 100), (80, 100), (60, 100)]
    >>> len(serpent)
    4
    >>> allonger_serpent(serpent, 'haut')
    >>> serpent
    [(120, 80), (120, 100), (100, 100), (80, 100), (60, 100)]
    >>> len(serpent)
    5  
    """
    ... # A remplir (plusieurs lignes)
    


def collision_avec_mur(serpent):
    """Vérifie si la tête du serpent a touché un mur.
    
    Parameters:
        serpent (list): liste des positions du serpent
        
    Returns:
        bool: True si collision avec un mur, False sinon
        
    >>> collision_avec_mur([(100, 100), (80, 100), (60, 100)])
    False
    >>> collision_avec_mur([(-10, 100), (80, 100), (60, 100)])
    True
    >>> collision_avec_mur([(400, 100), (80, 100), (60, 100)])
    True
    >>> collision_avec_mur([(100, -5), (80, 100), (60, 100)])
    True
    >>> collision_avec_mur([(100, 400), (80, 100), (60, 100)])
    True
    """
    ... # A remplir (plusieurs lignes)


def collision_avec_serpent(serpent):
    """Vérifie si la tête du serpent a touché son corps.
    
    Parameters:
        serpent (list): liste des positions du serpent
        
    Returns:
        bool: True si collision avec le corps, False sinon
        
    >>> collision_avec_serpent([(100, 100), (80, 100), (60, 100), (40, 100)])
    False
    >>> collision_avec_serpent([(100, 100), (120, 100), (100, 100), (80, 100)])
    True
    >>> collision_avec_serpent([(100, 100), (80, 100), (60, 100), (40, 100), (100, 100)])
    True
    """
    ... # A remplir (plusieurs lignes)


def mange_nourriture(serpent, nourriture):
    """Vérifie si la tête du serpent touche la nourriture.
    
    Parameters:
        serpent (list): liste des positions du serpent
        nourriture (tuple): position (x, y) de la nourriture
        
    Returns:
        bool: True si le serpent mange la nourriture, False sinon
        
    >>> mange_nourriture([(100, 100), (80, 100), (60, 100)], (100, 100))
    True
    >>> mange_nourriture([(100, 100), (80, 100), (60, 100)], (120, 100))
    False
    >>> mange_nourriture([(200, 200), (180, 200)], (200, 200))
    True
    """
    ... # A remplir (éventuellement plusieurs lignes)


def afficher_message_fin(score):
    """Affiche un message de fin de jeu avec le score final.
    
    Parameters:
        score (int): le score final
    """
    efface_tout()
    texte(200, 150, "GAME OVER", couleur="red", ancrage="center", 
         police="Helvetica", taille=48, tag="fin")
    texte(200, 250, f"Score final: {score}", couleur="black", ancrage="center", 
         police="Helvetica", taille=32, tag="fin")
    texte(200, 320, "Ferme la fenetre pour quitter", couleur="gray", ancrage="center", 
         police="Helvetica", taille=16, tag="fin")
    ... # A remplir (éventuellement plusieurs lignes)


# ==================== PROGRAMME PRINCIPAL ====================
cree_fenetre(LARGEUR_FENETRE, HAUTEUR_FENETRE)

serpent = creer_serpent()
nourriture = creer_nourriture(serpent)
direction = 'droite'
score = 0
jeu_en_cours = True

while jeu_en_cours:
    # Affichage
    afficher_serpent(serpent)
    afficher_nourriture(nourriture)
    afficher_score(score)
    
    # Gestion des événements
    direction, quitter = gestion_evenement(direction)
    if quitter:
        jeu_en_cours = False
    
    # Déplacement du serpent
    deplacer_serpent(serpent, direction)
    
    # Vérification des collisions avec les murs
    jeu_en_cours = jeu_en_cours and not collision_avec_mur(serpent)
    
    # Vérification des collisions avec le serpent lui-même
    jeu_en_cours = jeu_en_cours and not collision_avec_serpent(serpent)
    
    # Vérification si le serpent mange la nourriture
    if mange_nourriture(serpent, nourriture):
        score += POINTS_PAR_NOURRITURE
        allonger_serpent(serpent, direction)
        nourriture = creer_nourriture(serpent)

# Affichage du message de fin
afficher_message_fin(score)

# Attendre la fermeture de la fenêtre
attend_fermeture()
