# jeu.py
"""Classe principale du jeu Bucheron - Projet NSI Première"""

import pygame
import random
import math

from constantes import *
from joueur     import Joueur
from arbre      import Arbre
from rocher     import Rocher
from objet      import HacheAmelioree


class Jeu:
    """Classe centrale qui orchestre toute la logique du jeu."""

    def __init__(self):
        pygame.init()
        pygame.display.set_caption("Bucheron - NSI Premiere")
        self.ecran   = pygame.display.set_mode((LARGEUR, HAUTEUR))
        self.horloge = pygame.time.Clock()

        self.police_grande = pygame.font.SysFont("Arial", 38, bold=True)
        self.police_moy    = pygame.font.SysFont("Arial", 24)
        self.police_petite = pygame.font.SysFont("Arial", 18)
        self.police_micro  = pygame.font.SysFont("Arial", 14)

        self.etat = "menu"
        self.reinitialiser()

    # ── Réinitialisation ───────────────────────────────────────────────
    def reinitialiser(self) -> None:
        self.score  = 0
        self.niveau = 1
        self.commencer_niveau()

    def commencer_niveau(self) -> None:
        self.joueur           = Joueur(LARGEUR // 2, HAUTEUR // 2)
        self.rochers          = self._generer_rochers()
        self.arbres           = self._generer_arbres()
        self.objets           = self._generer_objets()
        self.particules: list = []
        self.temps_niveau     = 0
        self.timer_transition = 0
        self.surface_sol      = self._creer_sol()

    # ── Génération du niveau ───────────────────────────────────────────
    def _positions_occupees(self) -> list:
        """Liste des (x, y) déjà utilisés (rochers + spawn joueur)."""
        positions = [(LARGEUR // 2, HAUTEUR // 2)]
        positions += [(r.x, r.y) for r in self.rochers]
        return positions

    def _generer_rochers(self) -> list:
        """Place entre 3 et 6 rochers selon le niveau."""
        nb = min(3 + self.niveau // 2, 7)
        rochers    = []
        tentatives = 0
        while len(rochers) < nb and tentatives < 1000:
            tentatives += 1
            x = random.randint(80, LARGEUR - 80)
            y = random.randint(HAUTEUR_HUD + 70, HAUTEUR - 60)
            if math.dist((x, y), (LARGEUR // 2, HAUTEUR // 2)) < 100:
                continue
            if any(math.dist((x, y), (r.x, r.y)) < 80 for r in rochers):
                continue
            rochers.append(Rocher(x, y))
        return rochers

    def _generer_arbres(self) -> list:
        """Génère les arbres en mélangeant les trois types."""
        nb_arbres = ARBRES_BASE + (self.niveau - 1) * ARBRES_INCREMENT
        pv_normal = min(1 + (self.niveau - 1) // 2, 4)

        occupees = self._positions_occupees()
        arbres   = []
        tentatives = 0

        while len(arbres) < nb_arbres and tentatives < 2000:
            tentatives += 1
            x = random.randint(70, LARGEUR - 70)
            y = random.randint(HAUTEUR_HUD + 70, HAUTEUR - 60)

            if any(math.dist((x, y), p) < 90 for p in occupees):
                continue
            if any(math.dist((x, y), (a.x, a.y)) < 70 for a in arbres):
                continue

            # Choix aléatoire du type avec des probabilités
            tirage = random.random()
            if tirage < 0.25:
                type_arbre = "bouleau"
            elif tirage < 0.45:
                type_arbre = "chene"
            else:
                type_arbre = "normal"

            arbres.append(Arbre(x, y, pv_normal, type_arbre))
        return arbres

    def _generer_objets(self) -> list:
        """Spawn 1 hache améliorée par niveau à une position libre."""
        occupees = self._positions_occupees()
        occupees += [(a.x, a.y) for a in self.arbres]

        for _ in range(500):
            x = random.randint(80, LARGEUR - 80)
            y = random.randint(HAUTEUR_HUD + 80, HAUTEUR - 70)
            if any(math.dist((x, y), p) < 70 for p in occupees):
                continue
            return [HacheAmelioree(x, y)]
        return []

    def _creer_sol(self) -> pygame.Surface:
        sol = pygame.Surface((LARGEUR, HAUTEUR))
        sol.fill(HERBE)
        rng = random.Random(self.niveau * 137)
        for _ in range(130):
            gx     = rng.randint(0, LARGEUR)
            gy     = rng.randint(HAUTEUR_HUD, HAUTEUR)
            r      = rng.randint(5, 18)
            nuance = rng.randint(-18, 10)
            couleur = (
                max(0, min(255, 60  + nuance)),
                max(0, min(255, 140 + nuance)),
                max(0, min(255,       nuance)),
            )
            pygame.draw.circle(sol, couleur, (gx, gy), r)
        return sol

    # ── Logique ────────────────────────────────────────────────────────
    def arbres_restants(self) -> int:
        return sum(1 for a in self.arbres if a.est_vivant())

    def ajouter_particules(self, x, y, detruit, type_arbre="normal") -> None:
        # Couleurs selon le type d'arbre coupé
        if type_arbre == "bouleau":
            couleurs = [(220, 220, 180), (180, 215, 125), VERT_CLAIR]
        elif type_arbre == "chene":
            couleurs = [BRUN_CHENE, (20, 80, 20), MARRON_FONCE]
        else:
            couleurs = [MARRON, MARRON_FONCE, VERT_FONCE, VERT]

        nb = 14 if detruit else 7
        for _ in range(nb):
            angle   = random.uniform(0, 2 * math.pi)
            vitesse = random.uniform(1.5, 4.5)
            self.particules.append({
                "x": x, "y": y,
                "vx": math.cos(angle) * vitesse,
                "vy": math.sin(angle) * vitesse - 2.5,
                "vie":     random.randint(25, 50),
                "couleur": random.choice(couleurs),
                "taille":  random.randint(2, 5 if detruit else 3),
            })

    def _mettre_a_jour_particules(self) -> None:
        for p in self.particules:
            p["x"]  += p["vx"]
            p["y"]  += p["vy"]
            p["vy"] += 0.18
            p["vie"] -= 1
        self.particules = [p for p in self.particules if p["vie"] > 0]

    # ── Dessin ─────────────────────────────────────────────────────────
    def _dessiner_sol(self) -> None:
        self.ecran.blit(self.surface_sol, (0, 0))

    def _dessiner_particules(self) -> None:
        for p in self.particules:
            pygame.draw.circle(self.ecran, p["couleur"],
                               (int(p["x"]), int(p["y"])), p["taille"])

    def _dessiner_hud(self) -> None:
        pygame.draw.rect(self.ecran, (35, 35, 35), (0, 0, LARGEUR, HAUTEUR_HUD))
        pygame.draw.line(self.ecran, (110, 80, 10),
                         (0, HAUTEUR_HUD), (LARGEUR, HAUTEUR_HUD), 2)

        t_score = self.police_moy.render(f"Score : {self.score}", True, JAUNE)
        self.ecran.blit(t_score, (20, 14))

        t_niv = self.police_moy.render(f"Niveau {self.niveau}", True, BLANC)
        self.ecran.blit(t_niv, (LARGEUR // 2 - t_niv.get_width() // 2, 14))

        t_arb = self.police_moy.render(
            f"Arbres : {self.arbres_restants()}", True, VERT_CLAIR)
        self.ecran.blit(t_arb, (LARGEUR - t_arb.get_width() - 20, 14))

        secs  = self.temps_niveau // FPS
        t_tps = self.police_petite.render(f"{secs}s", True, GRIS_CLAIR)
        self.ecran.blit(t_tps, (LARGEUR // 2 + 80, 18))

        # Indicateur hache améliorée
        if self.joueur.hache_active:
            secs_restants = self.joueur.timer_hache_amelioree // FPS + 1
            t_h = self.police_micro.render(
                f"Hache x3  {secs_restants}s", True, JAUNE)
            bg  = pygame.Surface((t_h.get_width() + 14, 22), pygame.SRCALPHA)
            bg.fill((80, 60, 0, 180))
            self.ecran.blit(bg,  (LARGEUR // 2 - bg.get_width() // 2 - 90, 2))
            self.ecran.blit(t_h, (LARGEUR // 2 - t_h.get_width() // 2 - 90, 5))

    def _dessiner_menu(self) -> None:
        self.ecran.fill((25, 75, 25))

        titre = self.police_grande.render("BUCHERON", True, JAUNE)
        self.ecran.blit(titre, (LARGEUR // 2 - titre.get_width() // 2, 100))

        sous_titre = self.police_moy.render(
            "Coupe tous les arbres pour passer au niveau suivant !", True, BLANC)
        self.ecran.blit(sous_titre,
            (LARGEUR // 2 - sous_titre.get_width() // 2, 180))

        lignes = [
            ("Z Q S D / Fleches : Se deplacer",                       VERT_CLAIR),
            ("ESPACE / E        : Couper l'arbre le plus proche",      VERT_CLAIR),
            ("",                                                        BLANC),
            ("Bouleau (pastille verte)  :  1 PV  — rapide a couper",   VERT_CLAIR),
            ("Normal                   :  PV variable selon le niveau", BLANC),
            ("Chene  (pastille marron)  :  5 PV  — tres resistant",     (200, 140, 60)),
            ("",                                                        BLANC),
            ("Ramasse la hache doree pour couper 3x plus vite !",       JAUNE),
            ("Les rochers gris bloquent le passage.",                   GRIS_CLAIR),
        ]
        for i, (texte, couleur) in enumerate(lignes):
            t = self.police_petite.render(texte, True, couleur)
            self.ecran.blit(t, (LARGEUR // 2 - t.get_width() // 2, 260 + i * 27))

        if (pygame.time.get_ticks() // 550) % 2 == 0:
            t_start = self.police_grande.render(
                "Appuie sur ENTREE pour jouer", True, ORANGE)
            self.ecran.blit(t_start,
                (LARGEUR // 2 - t_start.get_width() // 2, 530))

    def _dessiner_niveau_termine(self) -> None:
        overlay = pygame.Surface((LARGEUR, HAUTEUR), pygame.SRCALPHA)
        overlay.fill((0, 0, 0, 145))
        self.ecran.blit(overlay, (0, 0))

        t1 = self.police_grande.render(
            f"Niveau {self.niveau - 1} termine !", True, JAUNE)
        self.ecran.blit(t1, (LARGEUR // 2 - t1.get_width() // 2, 180))

        bonus = self._calculer_bonus_temps()
        t2 = self.police_moy.render(f"Bonus temps : +{bonus}", True, ORANGE)
        self.ecran.blit(t2, (LARGEUR // 2 - t2.get_width() // 2, 260))

        t3 = self.police_moy.render(f"Score total : {self.score}", True, BLANC)
        self.ecran.blit(t3, (LARGEUR // 2 - t3.get_width() // 2, 305))

        decompte = max(0, 3 - self.timer_transition // FPS)
        t4 = self.police_grande.render(str(decompte + 1), True, BLANC)
        self.ecran.blit(t4, (LARGEUR // 2 - t4.get_width() // 2, 390))

        t5 = self.police_petite.render(
            "Prochain niveau dans...", True, GRIS_CLAIR)
        self.ecran.blit(t5, (LARGEUR // 2 - t5.get_width() // 2, 360))

    # ── Calcul du bonus ────────────────────────────────────────────────
    def _calculer_bonus_temps(self) -> int:
        return max(0, 500 - (self.temps_niveau // FPS) * 5)

    # ── Boucle principale ──────────────────────────────────────────────
    def lancer(self) -> None:
        en_cours = True
        while en_cours:
            self.horloge.tick(FPS)

            # ── Événements ──────────────────────────────────────────────
            for evenement in pygame.event.get():
                if evenement.type == pygame.QUIT:
                    en_cours = False

                elif evenement.type == pygame.KEYDOWN:
                    if self.etat == "menu":
                        if evenement.key == pygame.K_RETURN:
                            self.etat = "jeu"

                    elif self.etat == "jeu":
                        if evenement.key in (pygame.K_SPACE, pygame.K_e):
                            arbre = self.joueur.essayer_couper(self.arbres)
                            if arbre is not None:
                                arbre.recevoir_degats()
                                detruit = not arbre.est_vivant()
                                self.ajouter_particules(
                                    arbre.x, arbre.y, detruit, arbre.type_arbre)
                                if detruit:
                                    # Points selon le type
                                    bonus_type = {"bouleau": 50,
                                                  "normal":  100,
                                                  "chene":   250}
                                    self.score += bonus_type.get(
                                        arbre.type_arbre, 100) * self.niveau

                        elif evenement.key == pygame.K_ESCAPE:
                            self.etat = "menu"

            # ── Mise à jour ─────────────────────────────────────────────
            if self.etat == "jeu":
                touches = pygame.key.get_pressed()
                self.joueur.gerer_entrees(touches, self.rochers)

                for arbre in self.arbres:
                    arbre.mettre_a_jour()

                # Objets ramassables
                for objet in self.objets:
                    objet.mettre_a_jour()
                    if objet.verifier_ramassage(self.joueur.x, self.joueur.y):
                        self.joueur.activer_hache_amelioree()

                self._mettre_a_jour_particules()
                self.temps_niveau += 1

                if self.arbres_restants() == 0:
                    self.score += self._calculer_bonus_temps()
                    self.niveau += 1
                    self.timer_transition = 0
                    self.etat = "niveau_termine"

            elif self.etat == "niveau_termine":
                self.timer_transition += 1
                if self.timer_transition >= 4 * FPS:
                    self.commencer_niveau()
                    self.etat = "jeu"

            # ── Dessin ──────────────────────────────────────────────────
            if self.etat == "menu":
                self._dessiner_menu()

            elif self.etat in ("jeu", "niveau_termine"):
                self._dessiner_sol()

                # Rochers (derrière tout le reste)
                for rocher in self.rochers:
                    rocher.dessiner(self.ecran)

                # Objets ramassables
                for objet in self.objets:
                    objet.dessiner(self.ecran)

                # Arbres triés par y (profondeur)
                for arbre in sorted(self.arbres, key=lambda a: a.y):
                    arbre.dessiner(self.ecran)

                self._dessiner_particules()
                self.joueur.dessiner(self.ecran)
                self._dessiner_hud()

                if self.etat == "niveau_termine":
                    self._dessiner_niveau_termine()

            pygame.display.flip()

        pygame.quit()
