from davistk import *
from random import randint
from math import cos, sin, pi
from time import sleep


def creer_carte(joueur1, joueur2):
    """Ne pas modifier. Permet de créer les trois coordonnées de points corresponant à la carte
    
    Parameters:
        joueur1 (tuple): ensemble des données du joueur1
        joueur2 (tuple): ensemble des données du joueur2
        
    Returns:
        (int, int, int, int, int, int) : les 3 coordonnées des points de passage de la carte"""
    x1, y1, angle1, puissance1, etat1 = joueur1
    x2, y2, angle2, puissance2, etat2 = joueur2
    x = randint(x1 + (x2 - x1)//4, x1 + 3*(x2 - x1)//4)
    y = randint(0, min(y1, y2))
    return x1, y1, x, y, x2, y2

def dessiner_carte(carte):
    """Ne pas modifier. Dessine la carte
    
    Parameters:
        carte (tuple): n-uplet des points de passage de la carte"""
    x1, y1, x, y, x2, y2 = carte
    polygone([(0, 400),(x1, y1),(x, y),(x2, y2),(400, 400)], remplissage="black")

def affiche_boulet(boulet):
    """Ne pas modifier. Efface le dessin du boulet précédent et affiche le boulet sur la fenêtre.
    
    Parameters:
        boulet (tuple): ensemble des données du boulet: abscisse, ordonnee, vitesse horizontale, vitesse verticale
    """
    xb, yb, dx, dy = boulet
    efface("boulet")
    cercle(xb, yb, 2, tag="boulet")
    mise_a_jour()

def creer_boulet(joueur):
    """Ne pas modifier. Crée le boulet avec les coordonnées du bout du canon et la vitesse correspondant à l'angle de tir
    
    Parameters:
        joueur (tuple) : ensemble des données du joueur: x, y, angle, puissance, etat
        
    Returns:
        (int, int, float, float) : successivement l'abscisse, l'ordonnée, la vitesse horizontale et la vitesse verticale du boulet
    """
    x, y, angle, puissance, etat = joueur
    xb, yb = x + 20 * cos(angle * pi / 180), y + 20 * sin(angle * pi / 180)
    dx = puissance * cos(angle * pi / 180)
    dy = puissance * sin(angle * pi / 180)
    return xb, yb, dx, dy

def coefficients(xa, ya, xb, yb):
    """Ne pas modifier. Cette fonction renvoie le coefficient directeur et l'ordonnée à l'origine de l'equation de la droite passant par (xa, ya) et (xb, yb)"""
    a = (yb - ya)/(xb - xa)
    b = ya - a * xa
    return a, b

def colision(boulet, carte):
    """Ne pas modifier. Cette fonction renvoie True si le boulet a touché la carte, False sinon"""
    xb, yb, dx, dy = boulet
    x1, y1, x, y, x2, y2 = carte
    a1, b1 = coefficients(0, 400, x1, y1)
    a2, b2 = coefficients(x1, y1, x, y)
    a3, b3 = coefficients(x, y, x2, y2)
    a4, b4 = coefficients(x2, y2, 400, 400)
    if not (0 < xb < 400):
        return True
    if 0 <= xb <= x1 and yb > a1 * xb + b1:
        return True
    elif x1 <= xb <= x and yb > a2 * xb + b2:
        return True
    elif x <= xb <= x2 and yb > a3 * xb + b3:
        return True
    elif x2 <= xb <= 400 and yb > a4 * xb + b4:
        return True
    else:
        return False

def mettre_a_la_surface(boulet, carte):
    """Ne pas modifier. Cette fonction met le boulet au niveau de la surface lorsqu'il l'a traversée. Effectivement, lorsque le boulet va vite, il peut passer de très au dessus de la carte à très en dessous, ce qui pose de problème de détection de toucher de l'adversaire"""
    xb, yb, dx, dy = boulet
    x1, y1, x, y, x2, y2 = carte
    a1, b1 = coefficients(0, 400, x1, y1)
    a2, b2 = coefficients(x1, y1, x, y)
    a3, b3 = coefficients(x, y, x2, y2)
    a4, b4 = coefficients(x2, y2, 400, 400)
    if not (0 < xb < 400):
        return xb, yb, dx, dy
    if 0 <= xb <= x1 and yb > a1 * xb + b1:
        return xb, a1 * xb + b1, dx, dy
    elif x1 <= xb <= x and yb > a2 * xb + b2:
        return xb, a2 * xb + b2, dx, dy
    elif x <= xb <= x2 and yb > a3 * xb + b3:
        return xb, a3 * xb + b3, dx, dy
    elif x2 <= xb <= 400 and yb > a4 * xb + b4:
        return xb, a4 * xb + b4, dx, dy

def creer_joueur(numero):
    """Fonction permettant de créer un joueur qui sera défini par ses coordonnées, son angle de tir, sa puissance de tir et son état.
    Sa position est tirée au hasard sur la carte:
        - Dans la partie inférieure gauche si c'est le joueur 1
        - Dans la partie inférieure droite si c'est le joueur 2
    Son angle de tir initial est -90, sa puissance de tir initiale est 10 et son état est 100.
        
    Parameters:
        numero (int): numéro du joueur (1 ou 2)

    Returns:
        (int, int, int, int, int) : successivement l'abscisse, l'ordonnée, l'angle de tire, la puissance du tir et l'etat
    """
    return 150, 200, -90, 10, 100 # A modifier

def afficher(joueur):
    """Affiche un joueur dans la fenêtre. Il est représenté par un cercle de rayon 10 et une ligne rouge de longueur 20 pour son canon
    
    Parameters:
        joueur (tuple): ensemble des données du joueur: x, y, angle, puissance, etat
    """
    # NE MODIFIER QUE LES PARTIES A MODIFIER
    x, y, angle, puissance, etat = joueur
    cercle(x, y, 10)
    ligne(x, y, x + 20 * cos(angle * pi / 180), y + 20 * sin(angle * pi / 180), couleur="red", tag="canon")
    # DEBUT PARTIE A MODIFIER
    # Faire que la puissance de tir s'affiche dans le coin supérieur droite ou gauche suivant le joueur
    texte(0, 0, "puissance", couleur="red", tag="puissance")
    # FIN PARTIE A MODIFIER


def gestion_evenement():
    """Fonction permettant de gérer les événements clavier et souris.
    
    Returns:
        (int, int, bool, bool) correspondant à:
            - au changement d'angle 1 ou -1 lorsque l'on presse les touches haut et bas, 0 sinon
            - au changement de puissance 1 ou -1 lorsque l'on presse les touches droite et gauche, 0 sinon
            - au tir: True si on presse la bar espace, False sinon
            - au fait de quitter le jeu: True si on clique sur la croix, False sinon.
    """
    # NE MODIFIER QUE LES PARTIES A MODIFIER
    mise_a_jour()
    sleep(1/30)
    efface("canon")
    efface("puissance")
    ev = donne_ev()
    tev = type_ev(ev)

    # Action dépendant du type d'événement reçu :

    if tev == 'Touche':  # on indique la touche pressée
        # DEBUT PARTIE A MODIFIER
        print(touche(ev)) # A MODIFIER
        return 0, 0, False, False # A MODIFIER
        # FIN DE PARTIE A MODIFIER
    elif tev == 'Quitte':  # on sort de la boucle
        return 0, 0, False, True
    else:  # dans les autres cas, on ne fait rien
        return 0, 0, False, False

def deplacement_canon(modif_angle, joueur):
    """Fonction permettant de mettre à jour le joueur en ajoutant à l'angle de tir existant la demande de modification de l'angle
    
    Parameters:
        modif_angle (int): demande de modification d'angle. La valeur sera -1, 0 ou 1
        joueur (tuple) : ensemble des données du joueur: x, y, angle, puissance, etat

    Returns:
        (int, int, int, int, int) : successivement l'abscisse, l'ordonnée, l'angle de tire, la puissance du tir et l'etat

    >>> deplacement_canon(1, (51, 232, 30, 10, 100))
    (51, 232, 31, 10, 100)
    """
    # NE MODIFIER QUE LES PARTIES A MODIFIER
    x, y, angle, puissance, etat = joueur # Cette ligne permet de récupérer dans 5 variables différentes les caractéristiques du joueur
    # DEBUT PARTIE A MODIFIER
    return x, y, angle, puissance, etat
    # FIN DE PARTIE A MODIFIER

def modification_puissance(modif_puissance, joueur):
    """Fonction permettant de mettre à jour le joueur en ajoutant à la puissance de tir existant la demande de modification de puissance
    
    Parameters:
        modif_puissance (int): demande de modification de puissance. La valeur sera -1, 0 ou 1
        joueur (tuple) : ensemble des données du joueur: x, y, angle, puissance, etat

    Returns:
        (int, int, int, int, int) : successivement l'abscisse, l'ordonnée, l'angle de tire, la puissance du tir et l'etat

    >>> modification_puissance(-1, (51, 232, 30, 10, 100))
    (51, 232, 30, 9, 100)
    """
    # NE MODIFIER QUE LES PARTIES A MODIFIER
    x, y, angle, puissance, etat = joueur # Cette ligne permet de récupérer dans 5 variables différentes les caractéristiques du joueur
    # DEBUT PARTIE A MODIFIER
    return x, y, angle, puissance, etat

def deplacer(boulet):
    """Cette fonction permet de mettre à jour la vitesse et la position. 
    Pour cela la fonction ajoute la vitesse à la position et ajoute 1 à la vitesse verticale.
    
    Parameters:
        boulet (tuple): ensemble des données du boulet: abscisse, ordonnee, vitesse horizontale, vitesse verticale
        
    Returns:
        (int, int, float, float) : successivement l'abscisse, l'ordonnée, la vitesse horizontale et la vitesse verticale du boulet

    >>> deplacer((50, 40, 2, 3))
    (52, 43, 2, 4)
    """
    # NE MODIFIER QUE LES PARTIES A MODIFIER
    x, y, dx, dy = boulet
    # DEBUT PARTIE A MODIFIER
    x += dx
    y += dy
    dy += 1
    # FIN PARTIE A MODIFIER
    return x, y, dx, dy

def distance(xa, ya, xb, yb):
    """Ne pas modifier. Calcule la distance entre deux points de coordonnées (xa, ya) et (xb, yb)"""
    return ((xb - xa) ** 2 + (yb - ya) ** 2) ** 0.5

def reussi(boulet, joueur):
    """Cette fonction renvoie True si le boulet touche le joueur, False sinon.
    On pourra utiliser la fonction distance (ci-dessus).
    
    Parameters:
        boulet (tuple) : ensemble des données du boulet: abscisse, ordonnee, vitesse horizontale, vitesse verticale
        joueur (tuple) : ensemble des données du joueur: x, y, angle, puissance, etat
        
    returns:
        (bool) : True si la joueur est touché, False sinon

    >>> reussi((50, 230, 2, 10), (51, 229, 20, 10, 100))
    True
    >>> reussi((50, 230, 2, 10), (71, 229, 20, 10, 100))
    False
    """
    # NE MODIFIER QUE LES PARTIES A MODIFIER
    xb, yb, dx, dy = boulet
    x, y, angle, puissance, etat = joueur
    # DEBUT PARTIE A MODIFIER
    return False
    # FIN PARTIE A MODIFIER

def est_touche(joueur):
    """Cette fonction met a jour l'etat du joueur lorsqu'il est touché. Elle renvoie donc juste le joueur avec un etat à 0
    
    Parameters:
        joueur (tuple) : ensemble des données du joueur: x, y, angle, puissance, etat

    Returns:
        (int, int, int, int, int) : successivement l'abscisse, l'ordonnée, l'angle de tire, la puissance du tir et l'etat qui sera donc à 0

    >>> est_touche((51, 232, 30, 10, 100))
    (51, 232, 30, 10, 0)
    """
    # NE MODIFIER QUE LES PARTIES A MODIFIER
    x, y, angle, puissance, etat = joueur
    # DEBUT PARTIE A MODIFIER
    return x, y, angle, puissance, etat
    # FIN PARTIE A MODIFIER

def affiche_impact(boulet):
    """ Cette fonction permet d'afficher un impact rouge à l'endroit ou est le boulet.
    
    Parameters:
        boulet (tuple) : ensemble des données du boulet: abscisse, ordonnee, vitesse horizontale, vitesse verticale
    """
    ...

def tirer(attaquant, defenseur, carte):
    """Ne pas modifier. Cette fonction réalise un tir de l'attaquant sur le defenseur (tous deux sur la carte carte):
        - Elle crée un boulet
        - Tant que le boulet n'est pas en collision avec la carte:
            - Elle déplace le boulet
            - Attend 1/60e de seconde
            - Affiche le boulet
        - Une fois le boulet en collision, elle met le boulet à la surface, car le boulet allant vite, sa position passe parfois de très au dessus du sol à très en dessous du sol
        - Affiche l'impact du boulet sur le sol
        - Verifie que le boulet a touché le defenseur:
            - Si oui, renvoie True
            - Sinon, renvoie False        
    """
    boulet = creer_boulet(attaquant)
    while not colision(boulet, carte):
        boulet = deplacer(boulet)
        sleep(1/60)
        affiche_boulet(boulet)
    boulet = mettre_a_la_surface(boulet, carte)
    affiche_impact(boulet)
    return reussi(boulet, defenseur)

def tour_joueur(attaquant, defenseur, carte):
    """Ne pas modifier. Cette fonction réalise un tour de jeu de l'attaquant:
        - Tant qu'il n'y a pas de tir et que personnne ne clique sur la croix:
            - on recupère les évenements (angle, puissance, tir et fin) et assure l'affichage mis à jour
            - on déplace le canon en conséquence
            - on ajuste la puissance en conséquence
            - On affiche l'attaquant et le defenseur
        - Si il y a un tir alors on tire et si il a reussi on enleve les points au defenseur
        - On renvoie True si le jeu est fini (joueur touché ou clique sur la croix), False sinon     
    """
    tir = False
    fin = False
    while not tir and not fin:
        angle, puissance, tir, fin = gestion_evenement()
        attaquant = deplacement_canon(angle, attaquant)
        attaquant = modification_puissance(puissance, attaquant)
        afficher(attaquant)
        afficher(defenseur)
    if tir:
        reussi = tirer(attaquant, defenseur, carte)
        if reussi:
            defenseur = est_touche(defenseur)
            fin = reussi
    return attaquant, defenseur, fin


# NE PAS MODIFIER CI-DESSOUS
cree_fenetre(400, 400)
joueur1 = creer_joueur(1)
joueur2 = creer_joueur(2)
carte = creer_carte(joueur1, joueur2)
dessiner_carte(carte)
fin = False
while not fin:
    joueur1, joueur2, fin = tour_joueur(joueur1, joueur2, carte)
    joueur1, joueur2 = joueur2, joueur1
# Prévoir un message de fin à afficher sur la fenêtre