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


# ==================== CONSTANTES GLOBALES ====================
LARGEUR_FENETRE = 400
HAUTEUR_FENETRE = 600
TAILLE_OISEAU = 15
LARGEUR_TUYAU = 30
ESPACEMENT_TUYAUX = 150  # Espace entre deux tuyaux
HAUTEUR_MIN_PASSAGE = 100  # Hauteur minimale du passage entre tuyaux
VITESSE_TUYAU = 3  # pixels par mise à jour
VITESSE_CHUTE = 0.4  # accélération de la gravité
VITESSE_SAUT = 5  # vitesse de montée lors d'un saut
FREQUENCE_JEU = 60  # FPS


def creer_oiseau():
    """Crée l'oiseau au centre gauche de la fenêtre.
    
    Returns:
        list: [x, y, vitesse_y] position et vitesse verticale de l'oiseau
        
    >>> oiseau = creer_oiseau()
    >>> oiseau
    [100, 300, 0]
    """
    ... # A compléter (plusieurs lignes)


def creer_tuyaux():
    """Crée une liste de paires de tuyaux (haut et bas).
    Chaque paire est [x, hauteur_passage, depasse] où x est la position horizontale du tuyau, hauteur_passage est la position Y du passage entre les deux tuyaux, et depasse indique si l'oiseau a dépassé ce tuyau.
    Crée un premier tuyau à la droite de la fenêtre, puis d'autres tous les ESPACEMENT_TUYAUX pixels jusqu'au double de la largeur de la fenêtre.
    La hauteur du passage est choisie aléatoirement.
    
    Returns:
        list: liste de paires de tuyaux

    >>> seed(0)
    >>> tuyaux = creer_tuyaux()
    >>> tuyaux
    [[400, 297, False], [580, 488, False], [760, 315, False]]
    """
    ... # A compléter (plusieurs lignes)


def afficher_oiseau(oiseau):
    """Affiche l'oiseau sur la fenêtre.
    L'oiseau est représenté par un cercle de rayon TAILLE_OISEAU.
    
    Parameters:
        oiseau (list): [x, y, vitesse_y] de l'oiseau
    """
    efface("oiseau") # Efface l'ancien dessin de l'oiseau en utilisant les tags (voir la documentation de davistk)
    ... # A compléter (plusieurs lignes)


def afficher_tuyaux(tuyaux):
    """Affiche tous les tuyaux sur la fenêtre.
    Représente chaque tuyau par deux rectangles avec un espace entre les deux pour le passage de l'oiseau.
    
    Parameters:
        tuyaux (list): liste des paires de tuyaux
    """
    efface("tuyau") # Efface les anciens dessins de tuyaux en utilisant les tags (voir la documentation de davistk)
    ... # A compléter (plusieurs lignes)


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 en utilisant les tags (voir la documentation de davistk)
    ... # A compléter (plusieurs lignes)


def gestion_evenement():
    """Gère les événements clavier et souris.
    La touche ESPACE fait sauter l'oiseau.
    
    Returns:
        (bool, bool): (sauter, quitter) - True si saut ou fermeture demandée
    """
    mise_a_jour()
    sleep(1/FREQUENCE_JEU)
    ev = donne_ev()
    tev = type_ev(ev)
    
    if tev == 'Touche':
        key = touche(ev)
        ... # A compléter
    elif tev == 'Quitte':
        return False, True
    
    return False, False


def deplacer_oiseau(oiseau, saute):
    """Déplace l'oiseau en appliquant la gravité et le saut.
    Si saute est True, l'oiseau monte avec une vitesse de VITESSE_SAUT. Sinon, la vitesse verticale de l'oiseau augmente de VITESSE_CHUTE (gravité).
    Le déplacement vertical de l'oiseau est appliqué après la mise à jour de la vitesse.

    Parameters:
        oiseau (list): [x, y, vitesse_y] de l'oiseau
        saute (bool): True si l'oiseau doit sauter

    >>> VITESSE_SAUT, VITESSE_CHUTE = 5, 0.4
    >>> oiseau = [100, 300, 0]
    >>> deplacer_oiseau(oiseau, False)
    >>> oiseau
    [100, 300.4, 0.4]
    >>> deplacer_oiseau(oiseau, False)
    >>> oiseau
    [100, 301.2, 0.8]
    >>> deplacer_oiseau(oiseau, True)
    >>> oiseau
    [100, 296.2, -5]
    """
    ... # A compléter (plusieurs lignes)


def deplacer_tuyaux(tuyaux):
    """Déplace tous les tuyaux vers la gauche de VITESSE_TUYAU pixels.
    Supprime les tuyaux qui ont quitté l'écran et en ajoute de nouveaux si nécessaire (si le dernier tuyau est à moins de 1.5 fois la largeur de la fenêtre).

    
    Parameters:
        tuyaux (list): liste des paires de tuyaux

    >>> VITESSE_TUYAU, LARGEUR_TUYAU, ESPACEMENT_TUYAUX = 3, 30, 150
    >>> seed(0)
    >>> tuyaux = [[400, 297, False], [610, 488, False]]
    >>> deplacer_tuyaux(tuyaux)
    >>> tuyaux
    [[397, 297, False], [607, 488, False]]
    >>> deplacer_tuyaux(tuyaux)
    >>> tuyaux
    [[394, 297, False], [604, 488, False]]
    >>> deplacer_tuyaux(tuyaux)
    >>> tuyaux
    [[391, 297, False], [601, 488, False]]
    >>> deplacer_tuyaux(tuyaux)
    >>> tuyaux
    [[388, 297, False], [598, 488, False], [778, 297, False]]
    >>> deplacer_tuyaux(tuyaux)
    >>> tuyaux
    [[385, 297, False], [595, 488, False], [775, 297, False]]
    """
    #... # A compléter (plusieurs lignes)


def collision_avec_mur(oiseau):
    """Vérifie si l'oiseau a touché le haut ou le bas de la fenêtre.
    
    Parameters:
        oiseau (list): [x, y, vitesse_y] de l'oiseau
        
    Returns:
        bool: True si collision, False sinon
    """
    ... # A compléter (plusieurs lignes)


def collision_avec_tuyau(oiseau, tuyaux):
    """Vérifie si l'oiseau a touché un tuyau.
    
    Parameters:
        oiseau (list): [x, y, vitesse_y] de l'oiseau
        tuyaux (list): liste des paires de tuyaux
        
    Returns:
        bool: True si collision, False sinon
    """
    x, y, _ = oiseau
    
    for tuyau in tuyaux:
        tuyau_x, hauteur_passage, _ = tuyau
        
        # Vérifier si l'oiseau est dans la zone horizontale du tuyau
        if tuyau_x <= x <= tuyau_x + LARGEUR_TUYAU:
            # Vérifier collision avec le tuyau du haut
            if y - TAILLE_OISEAU < hauteur_passage - ESPACEMENT_TUYAUX // 2:
                return True
            # Vérifier collision avec le tuyau du bas
            if y + TAILLE_OISEAU > hauteur_passage + ESPACEMENT_TUYAUX // 2:
                return True
    
    return False


def obtenir_score(oiseau, tuyaux, score_precedent):
    """Calcule le score en comptant les tuyaux passés.
    
    Parameters:
        oiseau (list): [x, y, vitesse_y] de l'oiseau
        tuyaux (list): liste des paires de tuyaux
        score_precedent (int): score précédent
        
    Returns:
        int: nouveau score

    >>> VITESSE_TUYAU, LARGEUR_TUYAU = 3, 30
    >>> oiseau = [100, 300, 0]
    >>> tuyaux = [[90, 250, False], [200, 300, False], [300, 350, False]]
    >>> score = 0
    >>> score = obtenir_score(oiseau, tuyaux, score)
    >>> score
    0
    >>> oiseau[0] = 210
    >>> score = obtenir_score(oiseau, tuyaux, score)
    >>> score
    1
    """
    x, _, _ = oiseau
    nouveau_score = 0
    
    for tuyau in tuyaux:
        tuyau_x, _, depasse = tuyau
        # Si l'oiseau a dépassé le centre du tuyau
        if x > tuyau_x + LARGEUR_TUYAU // 2 and not depasse:
            tuyau[2] = True  # Marquer ce tuyau comme dépassé
            nouveau_score += 1 
    
    return score_precedent + nouveau_score


def afficher_message_fin(score):
    """Affiche un message de fin de jeu avec le score final.
    
    Parameters:
        score (int): le score final
    """
    efface_tout()
    ... # A compléter (plusieurs lignes)
    mise_a_jour()

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

oiseau = creer_oiseau()
tuyaux = creer_tuyaux()
score = 0
jeu_en_cours = True

while jeu_en_cours:
    # Affichage
    afficher_oiseau(oiseau)
    afficher_tuyaux(tuyaux)
    afficher_score(score)
    
    # Gestion des événements
    saute, quitter = gestion_evenement()
    if quitter:
        jeu_en_cours = False
    
    # Déplacement
    deplacer_oiseau(oiseau, saute)
    deplacer_tuyaux(tuyaux)
    
    # Vérification des collisions avec les murs
    jeu_en_cours = jeu_en_cours and not collision_avec_mur(oiseau)
    
    # Vérification des collisions avec les tuyaux
    jeu_en_cours = jeu_en_cours and not collision_avec_tuyau(oiseau, tuyaux)
    
    # Mise à jour du score
    score = obtenir_score(oiseau, tuyaux, score)

# Affichage du message de fin
afficher_message_fin(score)

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