import math
import random
import pyxel

W, H = 256, 160
GRAVITY = 0.35
JUMP_VEL = -6.5
MOVE_SPEED = 1.8
TERMINAL_VEL = 6
GROUND_Y = 130

# --- Définition des niveaux ---
LEVELS = [
    {"length": 1000, "gaps": [0, 32], "enemy_density": 0.3, "bg": 12, "platform": 3, "enemy": 8},
    {"length": 1600, "gaps": [0, 0, 32, 48], "enemy_density": 0.45, "bg": 5, "platform": 11, "enemy": 2},
    {"length": 2200, "gaps": [0, 0, 0, 32, 48, 64], "enemy_density": 0.6, "bg": 1, "platform": 7, "enemy": 14},
    {"length": 2500, "gaps": [0, 16, 32, 48], "enemy_density": 0.5, "bg": 6, "platform": 2, "enemy": 10},
    {"length": 2800, "gaps": [0, 0, 24, 64], "enemy_density": 0.55, "bg": 3, "platform": 13, "enemy": 9},
    {"length": 3000, "gaps": [0, 0, 0, 32, 48], "enemy_density": 0.65, "bg": 4, "platform": 6, "enemy": 8},
    {"length": 3200, "gaps": [0, 0, 0, 24, 48, 64], "enemy_density": 0.7, "bg": 0, "platform": 8, "enemy": 15},
    {"length": 3500, "gaps": [0, 0, 0, 0, 48, 64, 96], "enemy_density": 0.8, "bg": 13, "platform": 10, "enemy": 2},
]

difficulties = ["Facile", "Moyen", "Difficile", "Expert", "Infernal"]

class Rect:
    def __init__(self, x, y, w, h):
        self.x, self.y, self.w, self.h = x, y, w, h
    def intersects(self, other):
        return not (
            self.x + self.w <= other.x or other.x + other.w <= self.x or
            self.y + self.h <= other.y or other.y + other.h <= self.y
        )

class Player:
    def __init__(self, x, y):
        self.x, self.y = x, y
        self.vx = self.vy = 0
        self.w, self.h = 8, 10
        self.on_ground = False
        self.facing = 1
        self.spawn = (x, y)
        self.invuln = 0
    @property
    def rect(self): return Rect(self.x, self.y, self.w, self.h)

class Enemy:
    def __init__(self, x, y, left_bound, right_bound):
        self.x, self.y = x, y
        self.w, self.h = 10, 8
        self.vx = 1
        self.left_bound = left_bound
        self.right_bound = right_bound
        self.alive = True
    @property
    def rect(self): return Rect(self.x, self.y, self.w, self.h)

class Coin:
    def __init__(self, x, y):
        self.x, self.y = x, y
        self.taken = False
    @property
    def rect(self): return Rect(self.x-2, self.y-2, 4, 4)

class Platform:
    def __init__(self, x, y, w, h=6):
        self.x, self.y, self.w, self.h = x, y, w, h
    @property
    def rect(self): return Rect(self.x, self.y, self.w, self.h)

class Spring:
    def __init__(self, x, y):
        self.x, self.y = x, y
        self.w, self.h = 10, 6
    @property
    def rect(self): return Rect(self.x, self.y, self.w, self.h)

class MovingPlatform:
    def __init__(self, x, y, w, dx=0, dy=0, range_dist=40):
        self.x, self.y = x, y
        self.w, self.h = w, 6
        self.base_x, self.base_y = x, y
        self.dx, self.dy = dx, dy
        self.range = range_dist
        self.t = 0
    @property
    def rect(self): return Rect(self.x, self.y, self.w, self.h)
    def update(self):
        self.t += 1
        self.x = self.base_x + math.sin(self.t * 0.02) * self.range * self.dx
        self.y = self.base_y + math.sin(self.t * 0.02) * self.range * self.dy

# ------------------- CLASSE DU JEU -------------------
class Game:
    def __init__(self):
        pyxel.init(W, H, title="Pyxel Mario++")
        self.state = "menu"
        self.selected_level = 0
        self.selected_difficulty = 0
        self.level_colors = LEVELS[0]
        self.gameover_timer = 0
        self.gameover_anim = False
        self._init_music()
        pyxel.playm(0, loop=True)
        pyxel.run(self.update, self.draw)

    def _init_music(self):
        pyxel.sound(0).set("c3 e3 g3 c4 e4 g4 f4 d4 c4 g3 e3 c3","t","7","n",25)
        pyxel.sound(1).set("c3 e3 g3 c4 c3 e3 g3 c4","t","7","n",25)
        pyxel.sound(2).set("c3e3g3c4 c3e3g3c4","t","7","n",25)
        pyxel.music(0).set([0, 1, 2])

    def start_game(self):
        level_cfg = LEVELS[self.selected_level]
        self.level_length = level_cfg["length"]
        difficulty_multiplier = 1 + 0.25 * self.selected_difficulty
        self.enemy_density = level_cfg["enemy_density"] * difficulty_multiplier
        self.gap_pattern = level_cfg["gaps"]
        self.level_colors = level_cfg
        self.reset()
        self.state = "jeu"

    def reset(self):
        self.player = Player(16, 0)
        self.score = 0
        self.lives = 3
        self.camera_x = 0
        self.level_end = self.level_length - 64
        self._build_level()

    def _build_level(self):
        self.platforms = []
        x = 0
        while x < self.level_length:
            gap = random.choice(self.gap_pattern)
            width = random.randint(80, 160)
            self.platforms.append(Platform(x, GROUND_Y, width, 8))
            x += width + gap
        self.platforms += [Platform(120, 100, 40), Platform(300, 85, 32), Platform(550, 95, 50)]
        self.moving_platforms = []
        if self.selected_level >= 4:
            self.moving_platforms.append(MovingPlatform(800, 90, 50, dx=1))
            self.moving_platforms.append(MovingPlatform(1400, 70, 60, dy=1))
        self.coins = []
        for p in self.platforms:
            for i in range(max(1, p.w // 40)):
                cx = p.x + 10 + i*20
                if cx < p.x + p.w - 10:
                    self.coins.append(Coin(cx, p.y - 8))
        self.enemies = []
        for p in self.platforms:
            if p.y == GROUND_Y and p.w >= 80 and random.random() < self.enemy_density:
                left = p.x + 4; right = p.x + p.w - 14
                ex = random.randint(left, right)
                self.enemies.append(Enemy(ex, GROUND_Y - 8, left, right))
        self.springs = [Spring(random.randint(100, self.level_length - 100), GROUND_Y - 6) for _ in range(5)]

    def _move_and_collide(self, ent):
        ent.x += ent.vx
        for p in self.platforms + self.moving_platforms:
            if ent.rect.intersects(p.rect):
                ent.x = p.x - ent.w if ent.vx > 0 else p.x + p.w
        ent.vy = min(ent.vy + GRAVITY, TERMINAL_VEL)
        ent.y += ent.vy
        ent.on_ground = False
        for p in self.platforms + self.moving_platforms:
            if ent.rect.intersects(p.rect):
                if ent.vy > 0 and ent.y + ent.h - p.y <= 8:
                    ent.y, ent.vy, ent.on_ground = p.y - ent.h, 0, True
                elif ent.vy < 0 and p.y + p.h - ent.y <= 8:
                    ent.y, ent.vy = p.y + p.h, 0

    def _update_camera(self):
        target = max(0, min(self.player.x - W//2 + self.player.w//2, self.level_length - W))
        self.camera_x += (target - self.camera_x) * 0.15
        pyxel.camera(self.camera_x, 0)

    def update(self):
        if self.state == "menu": self.update_menu()
        elif self.state == "gameover": self.update_gameover()
        else: self.update_game()

    def update_menu(self):
        if pyxel.btnp(pyxel.KEY_UP): self.selected_level = (self.selected_level - 1) % len(LEVELS)
        if pyxel.btnp(pyxel.KEY_DOWN): self.selected_level = (self.selected_level + 1) % len(LEVELS)
        if pyxel.btnp(pyxel.KEY_LEFT): self.selected_difficulty = (self.selected_difficulty - 1) % len(difficulties)
        if pyxel.btnp(pyxel.KEY_RIGHT): self.selected_difficulty = (self.selected_difficulty + 1) % len(difficulties)
        if pyxel.btnp(pyxel.KEY_RETURN): self.start_game()

    def update_gameover(self):
        if pyxel.btnp(pyxel.KEY_RETURN): self.state = "menu"

    def update_game(self):
        if pyxel.btnp(pyxel.KEY_R): self.reset(); return
        self.player.vx = 0
        if pyxel.btn(pyxel.KEY_LEFT): self.player.vx, self.player.facing = -MOVE_SPEED, -1
        if pyxel.btn(pyxel.KEY_RIGHT): self.player.vx, self.player.facing = MOVE_SPEED, 1
        if pyxel.btnp(pyxel.KEY_SPACE) and self.player.on_ground: self.player.vy = JUMP_VEL
        for mp in self.moving_platforms: mp.update()
        self._move_and_collide(self.player)
        for s in self.springs:
            if self.player.rect.intersects(s.rect) and self.player.vy > 0: self.player.vy = -10
        if self.player.y > H + 60: self._lose_life(); return
        self.player.x = max(0, min(self.player.x, self.level_length - self.player.w))
        for c in self.coins:
            if not c.taken and self.player.rect.intersects(c.rect): c.taken, self.score = True, self.score + 1
        for e in self.enemies:
            if not e.alive: continue
            e.x += e.vx
            if e.x <= e.left_bound or e.x + e.w >= e.right_bound: e.vx *= -1
            if self.player.invuln <= 0 and self.player.rect.intersects(e.rect):
                if self.player.vy > 1 and self.player.y + self.player.h - e.y <= 6:
                    e.alive, self.player.vy, self.score = False, JUMP_VEL * 0.6, self.score + 5
                else: self._hurt()
        if self.player.invuln > 0: self.player.invuln -= 1
        if self.player.x >= self.level_end: self.state = "menu"
        self._update_camera()

    def _hurt(self):
        self.lives -= 1; self.player.invuln = 90
        if self.lives < 0: self.state = "gameover"
        else: self.player.x, self.player.y, self.player.vx, self.player.vy = *self.player.spawn, 0, 0

    def _lose_life(self):
        self.lives -= 1
        if self.lives < 0: self.state = "gameover"
        else: self.player.x, self.player.y, self.player.vx, self.player.vy = *self.player.spawn, 0, 0

    # ------------------- DESSIN DU FOND -------------------
    def draw_background(self):
        pyxel.cls(12)  # ciel
        pyxel.circ(200, 40, 12, 10)  # soleil
        # nuages animés
        for i in range(5):
            x = (self.camera_x * 0.3 + i * 60) % (W + 40) - 40
            y = 20 + (i % 2) * 10
            pyxel.circ(x, y, 6, 7)
            pyxel.circ(x + 5, y - 2, 5, 7)
            pyxel.circ(x + 10, y, 6, 7)
        # montagnes (loin)
        offset = int(self.camera_x * 0.2)
        for i in range(0, W + 64, 64):
            x = (i - offset) % (W + 64) - 64
            pyxel.tri(x, H, x + 64, H, x + 32, 100, 3)
        # collines (proches)
        offset2 = int(self.camera_x * 0.5)
        for i in range(0, W + 64, 64):
            x = (i - offset2) % (W + 64) - 64
            pyxel.tri(x, H, x + 64, H, x + 32, 120, 4)

    # ------------------- DESSIN DU JEU -------------------
    def draw(self):
        if self.state == "menu": self.draw_menu()
        elif self.state == "gameover": self.draw_gameover()
        else: self.draw_game()

    def draw_menu(self):
        pyxel.cls(0)
        pyxel.text(80, 30, "=== MENU PRINCIPAL ===", 7)
        pyxel.text(60, 60, f"Niveau: {self.selected_level+1}", 10)
        pyxel.text(60, 80, f"Difficulte: {difficulties[self.selected_difficulty]}", 10)
        pyxel.text(40, 120, "Haut/Bas = changer niveau", 7)
        pyxel.text(40, 130, "Gauche/Droite = difficulte", 7)
        pyxel.text(70, 145, "Entrer pour jouer", 11)

    def draw_gameover(self):
        pyxel.cls(0)
        pyxel.text(100, 70, "GAME OVER", 8)
        pyxel.text(70, 100, f"SCORE FINAL : {self.score}", 7)
        pyxel.text(50, 130, "Appuyez sur ENTREE pour revenir au menu", 7)

    def draw_game(self):
        self.draw_background()  # <<< Nouveau fond généré automatiquement
        # plateformes
        for p in self.platforms:
            pyxel.rect(p.x, p.y, p.w, p.h, self.level_colors["platform"])
        # plateformes mouvantes
        for mp in self.moving_platforms:
            pyxel.rect(mp.x, mp.y, mp.w, mp.h, 9)
        # pièces
        t = pyxel.frame_count
        for c in self.coins:
            if c.taken: continue
            bob = math.sin((t + c.x) * 0.1) * 1.5
            pyxel.circ(c.x, c.y + bob, 2, 10)
        # ennemis
        for e in self.enemies:
            if not e.alive: continue
            pyxel.rect(e.x, e.y, e.w, e.h, self.level_colors["enemy"])
        # ressorts
        for s in self.springs:
            pyxel.rect(s.x, s.y, s.w, s.h, 11)
        # joueur
        if self.player.invuln % 8 < 4:
            pyxel.rect(self.player.x, self.player.y, self.player.w, self.player.h, 9)
        pyxel.text(self.camera_x + 4, 4, f"SCORE: {self.score}", 7)
        pyxel.text(self.camera_x + 96, 4, f"VIES: {self.lives}", 7)
        pyxel.rect(self.level_end, GROUND_Y-30, 4, 30, 7)
        pyxel.text(self.level_end - 18, GROUND_Y-40, "FIN", 7)

# --- Lancement du jeu ---
if __name__ == "__main__":
    Game()