"""Alle Spielzustaende (State-Machine)."""
import pyxel
import math
import random
from constants import *
from levels import LEVEL_MAPS, LEVEL_CFG
from collectibles import Clock, Armor, Potion


class State:
    def __init__(self, game):
        self.game = game

    def enter(self):
        pass

    def update(self):
        pass

    def draw(self):
        pass


class IntroState(State):

    def enter(self):
        self.tick = 0

    def update(self):
        self.tick += 1
        if (pyxel.btnp(pyxel.KEY_RETURN) or pyxel.btnp(pyxel.KEY_SPACE) or
                pyxel.btnp(pyxel.KEY_UP) or pyxel.btnp(pyxel.KEY_DOWN) or
                pyxel.btnp(pyxel.KEY_LEFT) or pyxel.btnp(pyxel.KEY_RIGHT)):
            self.game.change_state("menu")

    def draw(self):
        g = self.game
        t = self.tick
        g.draw_bg()

        for i in range(6):
            phase = (t * 0.5 + i * 43) % (SCREEN_W + 32)
            ypos  = int((i * 61 + 16) % (SCREEN_H - 16))
            wx    = int(phase) - 16
            if g.has_res:
                pyxel.blt(wx, ypos, 1, HEDGE_U, HEDGE_V, TILE, TILE, 0)
            else:
                pyxel.rect(wx + 1, ypos + 1, 14, 14, COL_LGREEN)
                pyxel.rectb(wx, ypos, 16, 16, COL_DARK)

        pyxel.rect(16, 10, 224, 50, COL_DARK)
        pyxel.rectb(16, 10, 224, 50, COL_YELLOW)
        pyxel.rectb(18, 12, 220, 46, COL_GOLD)
        t1 = "LABYRINTH QUEST"
        pyxel.text((SCREEN_W - len(t1) * 4) // 2 - 1, 17, t1, 0)
        pyxel.text((SCREEN_W - len(t1) * 4) // 2, 16, t1, COL_YELLOW)
        pyxel.text(32, 30, "Hecken bauen sich auf - navigiere dich frei!", COL_LGREEN)
        pyxel.text(28, 42, "Sammle Items, bringe sie zur Truhe zurueck!", COL_GOLD)

        iy = 66
        pyxel.rect(16, iy, 224, 104, COL_DARK)
        pyxel.rectb(16, iy, 224, 104, COL_WHITE)
        lines = [
            (COL_YELLOW, "WIE SPIELT MAN?"),
            (0, ""),
            (COL_RED,    "Phase 1: Hecken fliegen in Wellen ein!"),
            (COL_WHITE,  "  Ausweichen! Truhe = Safe-Zone"),
            (COL_RED,    "  Treffer = getragene Items VERLOREN!"),
            (0, ""),
            (COL_LGREEN, "Phase 2: Labyrinth ist fertig"),
            (COL_WHITE,  "  Items einsammeln und zur Truhe bringen"),
            (COL_GOLD,   "  Truhe = Abgabestation (mehrmals moeglich)"),
            (COL_WHITE,  "  Mehr auf einmal ablegen = Combo-Bonus!"),
            (COL_BROWN,  "  Alle Schluessel abgelegt = GEWONNEN!"),
        ]
        for idx, (col, txt) in enumerate(lines):
            if txt and col:
                pyxel.text(24, iy + 6 + idx * 8, txt, col)

        ly = 176
        pyxel.rect(16, ly, 224, 58, COL_DARK)
        pyxel.rectb(16, ly, 224, 58, COL_WHITE)
        pyxel.text(24, ly + 4, "GEGENSTAENDE:", COL_YELLOW)
        items_info = [
            ("K", COL_YELLOW, "Schluessel 100P (Pflicht)"),
            ("G", COL_LBLUE,  "Edelstein +200P"),
            ("S", COL_GOLD,   "Stern +500P (selten!)"),
            ("T", COL_GOLD,   "Uhr +5s (sofort)"),
            ("A", COL_LBLUE,  "Ruestung=Schild (sofort)"),
            ("H", COL_RED,    "Heiltrank (sofort)"),
        ]
        for i, (sym, col, desc) in enumerate(items_info):
            row_idx = i // 2
            col_idx = i % 2
            px = 24 + col_idx * 112
            py = ly + 16 + row_idx * 16
            pyxel.text(px, py, sym, col)
            pyxel.text(px + 8, py, desc, COL_WHITE)

        pyxel.rect(16, 238, 224, 10, COL_DARK)
        pyxel.text(24, 240, "Lv1:90s langsam", COL_LGREEN)
        pyxel.text(104, 240, "Lv2:75s mittel", COL_YELLOW)
        pyxel.text(180, 240, "Lv3:60s schnell", COL_RED)

        if (t // 15) % 2 == 0:
            hint = "ENTER oder Pfeiltaste zum Starten"
            pyxel.text((SCREEN_W - len(hint) * 4) // 2, SCREEN_H - 4, hint, COL_LBLUE)
        if not g.has_res:
            pyxel.text(4, SCREEN_H - 14, "! Keine .pyxres  Fallback aktiv", COL_RED)


class MenuState(State):

    def enter(self):
        self.timer = 0

    def update(self):
        self.timer += 1
        if self.timer > 20:
            if pyxel.btnp(pyxel.KEY_1):
                self._start_game(1)
            elif pyxel.btnp(pyxel.KEY_2):
                self._start_game(2)
            elif pyxel.btnp(pyxel.KEY_3):
                self._start_game(3)

    def _start_game(self, level):
        g = self.game
        g.level = level
        g.score = 0
        g.total_keys = 0
        g.total_time = 0
        g.total_bonus = 0
        g.first_run = False
        g.change_state("flying")

    def draw(self):
        pyxel.cls(0)
        g = self.game

        # Titel
        pyxel.text(80, 20, "LABYRINTH QUEST", COL_YELLOW)
        pyxel.text(80, 28, "===============", COL_GOLD)

        # Letztes Ergebnis (falls vorhanden)
        if not g.first_run:
            if g.time_up:
                pyxel.text(80, 45, "ZEIT ABGELAUFEN!", COL_RED)
            else:
                pyxel.text(80, 45, "GEWONNEN!", COL_LGREEN)
            pyxel.text(80, 55, f"Score: {g.score}P", COL_WHITE)

            if g.last_score_rank == 0:
                pyxel.text(80, 65, "*** NEUER HIGHSCORE! ***", COL_GOLD)
            elif g.last_score_rank > 0:
                pyxel.text(80, 65, f"Platz {g.last_score_rank + 1}", COL_LBLUE)

        # Highscore-Tabelle
        if g.highscores:
            table_y = 90
            pyxel.text(60, table_y, "── HIGHSCORES ──", COL_GOLD)
            for i, entry in enumerate(g.highscores):
                y = table_y + 12 + i * 10
                rank_col = COL_GOLD if i == 0 else COL_WHITE
                marker = ">" if i == g.last_score_rank else " "
                text = (f"{marker}{i+1}. {entry['score']:>5}P  "
                        f"Lvl {entry['level']}  "
                        f"{entry['keys']} Keys  "
                        f"{entry['time']}s")
                pyxel.text(40, y, text, rank_col)

        # Level-Auswahl
        select_y = 170 if g.highscores else 100
        pyxel.text(72, select_y, "LEVEL WAEHLEN:", COL_WHITE)
        pyxel.text(72, select_y + 14, "[1] Leicht  (90s)", COL_LGREEN)
        pyxel.text(72, select_y + 24, "[2] Mittel  (75s)", COL_GOLD)
        pyxel.text(72, select_y + 34, "[3] Schwer  (60s)", COL_RED)

        # Blinkender Hinweis
        if (self.timer // 20) % 2 == 0:
            pyxel.text(64, select_y + 52, "Taste 1, 2 oder 3 druecken", COL_WHITE)


class FlyingState(State):

    def enter(self):
        g = self.game
        g.reset_round()
        g.player.hit_cooldown = START_SHIELD_FRAMES

    def update(self):
        g = self.game
        p = g.player
        g.round_time += 1

        if g.check_time_up():
            return
        g.check_alarm()

        if g.update_walls():
            g.change_state("locked")
            return

        g.push_player_out_of_snapped()

        dx, dy = p.get_input()
        p.move_blocked(dx, dy, g.snapped_wall_blocks)

        g.collect_items()
        g.check_deposit()

        if not p.is_invincible:
            if g.check_wall_collision():
                return
        p.tick_cooldown()
        p.tick_footsteps()

    def draw(self):
        self.game.draw_game()
        self.game.draw_progress_bar()


class FlyingResumeState(FlyingState):

    def enter(self):
        pass


class LockedState(State):

    def enter(self):
        g = self.game
        g.locked = True
        g.push_player_out_of_walls()

    def update(self):
        g = self.game
        p = g.player
        g.round_time += 1

        if g.check_time_up():
            return
        g.check_alarm()
        g.push_player_out_of_walls()

        dx, dy = p.get_input()
        p.move_blocked(dx, dy, g.wall_blocks)

        g.collect_items()
        g.check_deposit()
        p.tick_cooldown()
        p.tick_footsteps()

    def draw(self):
        self.game.draw_game()


class GhostState(State):

    def enter(self):
        self.timer = 60                    # ~2s Ghost-Animation
        self.shake = 0
        self.is_death = self.game.time_up
        g = self.game
        # Bei Time-Up ist das Spiel vorbei → Highscore aufzeichnen
        if g.time_up:
            g.record_highscore()

    def update(self):
        g = self.game

        if g.update_walls():
            g.locked = True

        self.timer -= 1
        self.shake += 1

        if not (self.timer > 0):
            if self.is_death:
                g.hide_player = False
                g.change_state("menu")
            else:
                if g.locked:
                    g.change_state("locked")
                else:
                    g.change_state("flying_resume")
                if g.has_res and not g.time_up:
                    limit_secs = LEVEL_CFG[g.level]["time_limit"]
                    elapsed    = g.round_time // 30
                    if limit_secs - elapsed > 0 and not (limit_secs - elapsed > 10):
                        g.alarm_playing = True
                        pyxel.play(CH_ALARM, SND_ALARM, loop=True)

    def draw(self):
        g = self.game
        g.draw_game()
        shake_x = int(math.sin(self.shake * 0.8) * 4)
        g.player.draw_at(g.player.x + shake_x, g.player.y, DIR_GHOST)
        if not g.locked:
            g.draw_progress_bar()

# ═══════════════════════════════════════════════════════════════════════════════
#  CELEBRATING STATE – Jubel-Animation nach Gewinn
# ═══════════════════════════════════════════════════════════════════════════════
class CelebratingState(State):

    def enter(self):
        g = self.game
        self.timer = CELEBRATE_DURATION
        self.tick = 0

        # Spieler auf Truhe positionieren (Vorderansicht)
        g.player.x = g.chest_x
        g.player.y = g.chest_y
        g.player.direction = DIR_DOWN

        # Partikel erzeugen
        self.sparkles = []
        for _ in range(SPARKLE_COUNT):
            self.sparkles.append({
                "x": g.chest_x + 8 + random.uniform(-20, 20),
                "y": g.chest_y + random.uniform(-30, 10),
                "vx": random.uniform(-0.8, 0.8),
                "vy": random.uniform(-1.5, -0.3),
                "life": random.randint(30, 90),
                "max_life": 90,
                "color": random.choice([COL_YELLOW, COL_GOLD, COL_WHITE, COL_LGREEN]),
            })

        # Jubel-Sound: 3 Kanaele gleichzeitig fuer vollen Klang
        if g.has_res:
            pyxel.play(CH_MUSIC, SND_WIN)           # Melodie
            pyxel.play(CH_BASS, SND_WIN_BASS)       # Bass-Begleitung
            pyxel.play(CH_SFX, SND_WIN_SPARKLE)     # Sparkle-Plings

    def update(self):
        g = self.game
        self.tick += 1
        self.timer -= 1

        # Sparkles updaten
        for s in self.sparkles:
            s["x"] += s["vx"]
            s["y"] += s["vy"]
            s["vy"] += 0.03  # Leichte Gravitation
            s["life"] -= 1

        # Tote Partikel durch neue ersetzen
        for i, s in enumerate(self.sparkles):
            if not (s["life"] > 0):
                self.sparkles[i] = {
                    "x": g.chest_x + 8 + random.uniform(-24, 24),
                    "y": g.chest_y + random.uniform(-20, -5),
                    "vx": random.uniform(-0.8, 0.8),
                    "vy": random.uniform(-1.5, -0.3),
                    "life": random.randint(30, 70),
                    "max_life": 70,
                    "color": random.choice([COL_YELLOW, COL_GOLD, COL_WHITE, COL_LGREEN]),
                }

        # Nach Ablauf: Win-Dialog
        if not (self.timer > 0):
            g.change_state("win")

    def draw(self):
        g = self.game
        g.draw_game()

        # Spieler springend zeichnen (Sinus-Sprung)
        jump_y = -abs(math.sin(self.tick * CELEBRATE_JUMP_SPD)) * CELEBRATE_JUMP_H
        px = g.chest_x
        py = g.chest_y + int(jump_y)

        # Spieler-Sprite (Vorderansicht)
        g.player.draw_at(px, py, DIR_DOWN)

        # Schatten unter dem Spieler (wird kleiner wenn hoeher)
        shadow_w = max(4, 10 + int(jump_y // 2))
        shadow_x = px + 8 - shadow_w // 2
        shadow_y = g.chest_y + 14
        pyxel.line(shadow_x, shadow_y, shadow_x + shadow_w, shadow_y, COL_DARK)

        # Sparkles zeichnen
        for s in self.sparkles:
            if s["life"] > 0:
                # Blinken in den letzten Frames
                if 10 > s["life"] and (s["life"] // 2) % 2 == 0:
                    continue
                sx = int(s["x"])
                sy = int(s["y"])
                # Groessere Partikel am Anfang
                if s["life"] > 40:
                    pyxel.pset(sx, sy, s["color"])
                    pyxel.pset(sx + 1, sy, s["color"])
                    pyxel.pset(sx, sy + 1, s["color"])
                else:
                    pyxel.pset(sx, sy, s["color"])

        # Sterne die nach oben fliegen (Bonus-Effekt)
        for i in range(3):
            star_y = g.chest_y - 20 - ((self.tick * 2 + i * 40) % 60)
            star_x = g.chest_x + 8 + int(math.sin(self.tick * 0.1 + i * 2) * 15)
            if star_y > g.chest_y - 50:
                col = COL_YELLOW if (self.tick // 4 + i) % 2 == 0 else COL_GOLD
                pyxel.pset(star_x, star_y, col)
                pyxel.pset(star_x - 1, star_y, col)
                pyxel.pset(star_x + 1, star_y, col)
                pyxel.pset(star_x, star_y - 1, col)
                pyxel.pset(star_x, star_y + 1, col)

        # Text "GESCHAFFT!" pulsierend
        if (self.tick // 8) % 2 == 0:
            txt = "GESCHAFFT!"
            tx = (SCREEN_W - len(txt) * 4) // 2
            pyxel.text(tx - 1, 40, txt, COL_DARK)
            pyxel.text(tx, 39, txt, COL_YELLOW)

class WinState(State):

    def enter(self):
        self.timer = 0
        g = self.game
        g.record_highscore()
        if g.has_res:
            pyxel.play(CH_SFX, SND_WIN)

    def update(self):
        self.timer += 1
        if self.timer > 60 and pyxel.btnp(pyxel.KEY_RETURN):
            self.game.change_state("menu")

    def draw(self):
        pyxel.cls(0)
        g = self.game

        pyxel.text(70, 30, "*** GEWONNEN! ***", COL_GOLD)
        pyxel.text(70, 50, f"Endpunktzahl: {g.score}P", COL_YELLOW)
        pyxel.text(70, 62, f"Keys:  {g.total_keys}", COL_WHITE)
        pyxel.text(70, 72, f"Bonus: {g.total_bonus}P", COL_LBLUE)
        pyxel.text(70, 82, f"Zeit:  {g.total_time // 30}s", COL_WHITE)

        if g.last_score_rank == 0:
            if (self.timer // 10) % 2 == 0:
                pyxel.text(55, 105, "!!! NEUER HIGHSCORE !!!", COL_GOLD)
        elif g.last_score_rank >= 0:
            pyxel.text(70, 105, f"Platz {g.last_score_rank + 1} der Session", COL_LBLUE)

        # Highscore-Liste
        if g.highscores:
            pyxel.text(70, 125, "── TOP 5 ──", COL_GOLD)
            for i, entry in enumerate(g.highscores):
                y = 137 + i * 10
                marker = ">" if i == g.last_score_rank else " "
                col = COL_GOLD if i == g.last_score_rank else COL_WHITE
                pyxel.text(60, y, f"{marker}{i+1}. {entry['score']:>5}P (Lvl {entry['level']})", col)

        if self.timer > 60 and (self.timer // 20) % 2 == 0:
            pyxel.text(65, 210, "[ENTER] HAUPTMENUE", COL_WHITE)
