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


# ==================== CONSTANTES GLOBALES ====================
LARGEUR_FENETRE = 600
HAUTEUR_FENETRE = 500
LARGEUR_BARRE = 80
HAUTEUR_BARRE = 15
RAYON_BALLE = 6
HAUTEUR_BRIQUE = 15
ESPACE_BRIQUE = 5
NOMBRE_COLONNES = 10
NOMBRE_LIGNES = 3
FREQUENCE_JEU = 30  # 1/30e de seconde entre chaque mise à jour
POINTS_PAR_BRIQUE = 10
DEPLACEMENT_BARRE = 8  # pixels par mise à jour
ESPACE_SUR_BRIQUES = 30  # Espace entre le haut de la fenêtre et les briques


def creer_briques():
    """Crée la liste des briques initiales.
    Les briques sont représentées par une liste de listes de booléens.
    True signifie que la brique est présente, False qu'elle a été détruite.
    
    Returns:
        list: liste de listes représentant les briques
    
    >>> NOMBRE_COLONNES, NOMBRE_LIGNES = 3, 2
    >>> briques = creer_briques()
    >>> briques
    [[True, True, True], [True, True, True]]
    """
    ... # A remplir (plusieurs lignes)
    


def creer_barre():
    """Crée la barre du joueur au centre en bas de la fenêtre.
    La barre est représentée par [x1, y1, x2, y2].
    x1, y1 est le coin supérieur gauche et x2, y2 le coin inférieur droit.

    
    Returns:
        list: liste [x1, y1, x2, y2] représentant la barre
    
    >>> global LARGEUR_FENETRE, LARGEUR_BARRE, HAUTEUR_FENETRE, HAUTEUR_BARRE
    >>> LARGEUR_FENETRE, LARGEUR_BARRE, HAUTEUR_FENETRE, HAUTEUR_BARRE = 600, 80, 500, 15
    >>> barre = creer_barre()
    >>> barre
    [260, 470, 340, 485]
    >>> LARGEUR_FENETRE, LARGEUR_BARRE, HAUTEUR_FENETRE, HAUTEUR_BARRE = 400, 100, 300, 20
    >>> barre = creer_barre()
    >>> barre
    [150, 270, 250, 290]
    """
    ... # A remplir (plusieurs ligne)


def creer_balle(barre):
    """Crée la balle au centre de la barre.
    La balle est représentée par [x, y, vx, vy] où (x, y) est le centre
    et (vx, vy) est la vélocité (vitesse et direction) de la balle.
    
    Parameters:
        barre (list): position de la barre [x1, y1, x2, y2]
    
    Returns:
        list: liste [x, y, vx, vy] représentant la balle et sa vélocité
    
    >>> barre = [250, 455, 330, 470]
    >>> balle = creer_balle(barre)
    >>> balle
    [290.0, 449, 2, -3]
    >>> barre2 = [100, 400, 180, 415]
    >>> balle2 = creer_balle(barre2)
    >>> balle2
    [140.0, 394, 2, -3]
    """
    ... # A remplir (plusieurs lignes)


def afficher_briques(briques):
    """Affiche toutes les briques restantes sur la fenêtre.
    
    Parameters:
        briques (list): liste des briques
    """
    efface("briques")
    largeur_brique = (LARGEUR_FENETRE - ESPACE_BRIQUE * (NOMBRE_COLONNES + 1)) / NOMBRE_COLONNES
    for ligne in range(len(briques)):
        for col in range(len(briques[ligne])):
            if briques[ligne][col]:
                x1 = col * (largeur_brique + ESPACE_BRIQUE) + ESPACE_BRIQUE
                y1 = ligne * (HAUTEUR_BRIQUE + ESPACE_BRIQUE) + ESPACE_BRIQUE + ESPACE_SUR_BRIQUES
                x2 = x1 + largeur_brique
                y2 = y1 + HAUTEUR_BRIQUE
                rectangle(x1, y1, x2, y2, couleur="blue", remplissage="lightblue", 
                         epaisseur=2, tag="briques")

def afficher_barre(barre):
    """Affiche la barre du joueur.
    
    Parameters:
        barre (list): position de la barre [x1, y1, x2, y2]
    """
    efface("barre") # Efface l'ancienne barre (utilise les tags voir la doc de davistk)
    ... # A remplir (plusieurs lignes)


def afficher_balle(balle):
    """Affiche la balle sur la fenêtre.
    
    Parameters:
        balle (list): position de la balle [x, y, vx, vy]
    """
    efface("balle") # Efface l'ancienne balle (utilise les tags voir la doc de davistk)
    ... # A remplir (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 (utilise les tags voir la doc de davistk)
    ... # A remplir (plusieurs lignes)


def afficher_vies(vies):
    """Affiche le nombre de vies restantes en haut à droite.
    
    Parameters:
        vies (int): le nombre de vies
    """
    efface("vies") # Efface l'ancien nombre de vies (utilise les tags voir la doc de davistk)
    ... # A remplir (plusieurs lignes)


def gestion_evenement():
    """Gère les événements clavier pour contrôler la barre.
    Les touches flèches gauche/droite déplacent la barre.
    
    Returns:
        (str, bool): direction de la barre ('gauche', 'droite', 'immobile') et True si on doit quitter
    """
    mise_a_jour()
    sleep(1/FREQUENCE_JEU)
    ev = donne_ev()
    tev = type_ev(ev)
    
    if tev == 'Touche':
        key = touche(ev)
        ... # A remplir (plusieurs lignes)
    elif tev == 'Quitte':
        return 'immobile', True
    else:
        return 'immobile', False


def deplacer_barre(barre, direction):
    """Déplace la barre horizontalement selon la direction donnée.
    La barre ne peut pas sortir de la fenêtre.
    Modifie la liste de position de la barre.
    
    Parameters:
        barre (list): position de la barre [x1, y1, x2, y2]
        direction (str): 'gauche', 'droite' ou 'immobile'
    
    >>> DEPLACEMENT_BARRE = 5
    >>> barre = [260, 455, 340, 470]
    >>> deplacer_barre(barre, 'droite')
    >>> barre
    [265, 455, 345, 470]
    >>> deplacer_barre(barre, 'gauche')
    >>> barre
    [260, 455, 340, 470]
    """
    ... # A remplir (plusieurs lignes)


def deplacer_balle(balle):
    """Déplace la balle selon sa vélocité.
    Gère les rebonds sur les murs haut, gauche et droite.
    Modifie la liste de position et vélocité de la balle.
    
    Parameters:
        balle (list): position et vélocité [x, y, vx, vy]
    
    >>> LARGEUR_FENETRE, RAYON_BALLE = 600, 6
    >>> balle = [4, 250, -2, 3]
    >>> deplacer_balle(balle) # rebond à gauche
    >>> balle
    [6, 253, 2, 3]
    >>> balle = [595, 250, 2, 3]
    >>> deplacer_balle(balle) # rebond à droite
    >>> balle
    [594, 253, -2, 3]
    >>> balle = [300, 5, 2, -3]
    >>> deplacer_balle(balle) # rebond en haut
    >>> balle
    [302, 6, 2, 3]
    >>> balle = [300, 250, 2, 3]
    >>> deplacer_balle(balle) # sans rebond
    >>> balle
    [302, 253, 2, 3]
    """
    ... # A remplir (plusieurs lignes)


def collision_balle_barre(balle, barre):
    """Vérifie si la balle entre en collision avec la barre.
    En cas de collision, la balle rebondit.
    Modifie la vélocité de la balle si collision.
    
    Parameters:
        balle (list): position et vélocité [x, y, vx, vy]
        barre (list): position de la barre [x1, y1, x2, y2]
        
    Returns:
        bool: True si collision, False sinon
    
    >>> balle = [290, 457, 2, 3]
    >>> barre = [260, 455, 340, 470]
    >>> collision_balle_barre(balle, barre)
    True
    >>> balle2 = [290, 350, 2, 3]
    >>> barre2 = [260, 455, 340, 470]
    >>> collision_balle_barre(balle2, barre2)
    False
    >>> balle3 = [150, 457, 2, 3]
    >>> barre3 = [260, 455, 340, 470]
    >>> collision_balle_barre(balle3, barre3)
    False
    """
    x, y, vx, vy = balle
    x1, y1, x2, y2 = barre
    
    # Vérifier si la balle est au niveau de la barre
    if (x1 - RAYON_BALLE <= x <= x2 + RAYON_BALLE and
        y1 - RAYON_BALLE <= y <= y2 + RAYON_BALLE):
        balle[3] = -vy  # Inverser la vélocité verticale
        balle[1] = y1 - RAYON_BALLE  # Placer la balle au-dessus de la barre
        return True
    
    return False


def collision_balle_brique(balle, brique):
    """Vérifie si la balle entre en collision avec une brique.
    En cas de collision, la balle rebondit.
    Modifie la vélocité de la balle si collision.
    
    Parameters:
        balle (list): position et vélocité [x, y, vx, vy]
        brique (tuple): position de la brique (ligne, colonne)
        
    Returns:
        bool: True si collision, False sinon
    
    """
    x, y, vx, vy = balle
    ligne, col = brique
    largeur_brique = (LARGEUR_FENETRE - ESPACE_BRIQUE * (NOMBRE_COLONNES + 1)) / NOMBRE_COLONNES
    x1 = col * (largeur_brique + ESPACE_BRIQUE) + ESPACE_BRIQUE
    y1 = ligne * (HAUTEUR_BRIQUE + ESPACE_BRIQUE) + ESPACE_BRIQUE + ESPACE_SUR_BRIQUES
    x2 = x1 + largeur_brique
    y2 = y1 + HAUTEUR_BRIQUE
    # Vérifier si la balle est au niveau de la brique
    if (x1 - RAYON_BALLE <= x <= x2 + RAYON_BALLE and
        y1 - RAYON_BALLE <= y <= y2 + RAYON_BALLE):
        # Déterminer le côté de collision pour inverser la vélocité appropriée
        if x < x1 or x > x2:
            balle[2] = -vx  # Inverser la vélocité horizontale
        else:
            balle[3] = -vy  # Inverser la vélocité verticale
        return True
    return False
    
def victoire(briques):
    """Vérifie si toutes les briques ont été détruites.
    
    Parameters:
        briques (list): liste des briques
        
    Returns:
        bool: True si toutes les briques sont détruites, False sinon
    
    >>> briques = [[False, False], [False, False]]
    >>> victoire(briques)
    True
    >>> briques2 = [[True, False], [False, False]]
    >>> victoire(briques2)
    False
    """
    ... # A remplir (plusieurs lignes)

def balle_perd_vie(balle):
    """Vérifie si la balle a dépassé le bas de la fenêtre.
    
    Parameters:
        balle (list): position et vélocité [x, y, vx, vy]
        
    Returns:
        bool: True si la balle a dépassé le bas, False sinon
    
    >>> HAUTEUR_FENETRE = 500
    >>> balle_perd_vie([300, 450, 2, 3])
    False
    >>> balle_perd_vie([300, 501, 2, 3])
    True
    >>> balle_perd_vie([300, 500, 2, 3])
    False
    >>> balle_perd_vie([300, 250, 2, 3])
    False
    """
    ... # A remplir (plusieurs lignes)


def afficher_message_fin(score, victoire):
    """Affiche un message de fin de jeu avec le score final.
    
    Parameters:
        score (int): le score final
        victoire (bool): True si le joueur a gagné, False sinon
    """
    efface_tout()
    ... # A remplir (plusieurs lignes)
    mise_a_jour()


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

briques = creer_briques()
barre = creer_barre()
balle = creer_balle(barre)
direction_barre = 'immobile'
score = 0
vies = 3
jeu_en_cours = True

while jeu_en_cours and vies > 0:
    # Affichage
    afficher_briques(briques)
    afficher_barre(barre)
    afficher_balle(balle)
    afficher_score(score)
    afficher_vies(vies)
    
    # Gestion des événements
    direction_barre, quitter = gestion_evenement()
    if quitter:
        jeu_en_cours = False
    
    # Déplacement de la barre
    deplacer_barre(barre, direction_barre)
    
    # Déplacement de la balle
    deplacer_balle(balle)
    
    # Vérification des collisions avec la barre
    collision_balle_barre(balle, barre)
    
    # Vérification des collisions avec les briques
    for ligne in range(len(briques)):
        for col in range(len(briques[ligne])):
            if briques[ligne][col]:
                if collision_balle_brique(balle, (ligne, col)):
                    briques[ligne][col] = False  # Enlever la brique
                    score += POINTS_PAR_BRIQUE
                    break  # Éviter plusieurs collisions en une mise à jour
    
    # Vérification si le joueur a gagné (plus de briques)
    if victoire(briques):
        jeu_en_cours = False
        afficher_message_fin(score, True)
        attend_fermeture()
        break
    
    # Vérification si la balle a dépassé le bas
    if balle_perd_vie(balle):
        vies -= 1
        if vies > 0:
            # Réinitialiser la balle
            balle = creer_balle(barre)

# Affichage du message de fin (défaite)
if vies == 0:
    afficher_message_fin(score, False)

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