import pyxel
import math

# =========================
# KONSTANTEN
# =========================
SCREEN_WIDTH  = 160
SCREEN_HEIGHT = 120
PLAYER_SIZE   = 8
SPIKE_W       = 8
SPIKE_H       = 10
TIME_LIMIT    = 180
WIN_SCORE     = 2000

# Pyxel-Farben:
#  4 = braun          -> Säulen
#  5 = dunkelgrau     -> Bodensäulen
#  6 = dunkelblau     -> Plattform-Rand
#  7 = weiss          -> Plattform-Highlight
# 12 = hellblau       -> Plattformen
# 13 = grau           -> runde Hindernisse
# 14 = hellbraun      -> Säulen-Maserung

# =========================
# SPIELER-VARIABLEN
# =========================
player_x      = 20
player_y      = float(SCREEN_HEIGHT - PLAYER_SIZE)
player_speed  = 2
player_jump_v = -5.5
gravity       = 0.4
y_velocity    = 0.0
is_jumping    = False
on_platform   = False
camera_x      = 0
score         = 0
time_left     = float(TIME_LIMIT)
game_over     = False
game_won      = False

# =========================
# HINDERNISSE
# Typen:
#  0 = Spike (Zacken)     -> sofort Game Over
#  4 = Plattform hellblau -> betretbar von oben
#  5 = Ziel-Wand          -> Gewonnen
#  7 = Sprungfeder        -> Hochsprung
# 10 = Lava               -> sofort Game Over
# 20 = Bodensäule (braun) -> von links/rechts/oben tödlich
# 21 = Runde Gefahr (grau)-> Kreis, Berührung = Game Over
# =========================

obstacles = [
    # ---- Zone 1: Einstieg, lernbar ----
    (75,  SCREEN_HEIGHT - SPIKE_H, SPIKE_W, SPIKE_H, 0),  # Zacken
    (110, SCREEN_HEIGHT - SPIKE_H, SPIKE_W, SPIKE_H, 0),

    # Erste Plattformen zum Üben
    (150, SCREEN_HEIGHT - 30, 28, 6, 4),
    (195, SCREEN_HEIGHT - 45, 28, 6, 4),
    (240, SCREEN_HEIGHT - 30, 28, 6, 4),

    # ---- Zone 2: Lava-Überquerung ----
    (290, SCREEN_HEIGHT - 8, 50, 8, 10),  # Lava
    (305, SCREEN_HEIGHT - 26, 20, 6, 4),  # Sprungstein drüber

    # Bodensäule danach
    (355, SCREEN_HEIGHT - 22, 12, 22, 20),  # kurze Bodensäule

    # ---- Zone 3: Feder + Plattform ----
    (390, SCREEN_HEIGHT - 6,  18, 6, 7),   # Feder
    (418, SCREEN_HEIGHT - 44, 28, 6, 4),   # Plattform hoch

    # Zacken nach der Plattform
    (460, SCREEN_HEIGHT - SPIKE_H, SPIKE_W, SPIKE_H, 0),
    (475, SCREEN_HEIGHT - SPIKE_H, SPIKE_W, SPIKE_H, 0),

    # ---- Zone 4: Runde Hindernisse ----
    # Runde Hindernisse auf dem Boden (Radius 7, Mittelpunkt y = SCREEN_HEIGHT-7)
    (510, SCREEN_HEIGHT - 7, 14, 14, 21),  # Kreis
    (545, SCREEN_HEIGHT - 7, 14, 14, 21),

    # Plattform drüber als Alternativroute
    (520, SCREEN_HEIGHT - 38, 32, 6, 4),

    # ---- Zone 5: Bodensäulen-Slalom ----
    (600, SCREEN_HEIGHT - 28, 12, 28, 20),  # Bodensäule mittel
    (640, SCREEN_HEIGHT - 40, 12, 40, 20),  # Bodensäule hoch
    (680, SCREEN_HEIGHT - 20, 12, 20, 20),  # Bodensäule kurz

    # Plattform als Hilfe
    (618, SCREEN_HEIGHT - 52, 24, 6, 4),

    # Lava dazwischen
    (720, SCREEN_HEIGHT - 8, 40, 8, 10),
    (730, SCREEN_HEIGHT - 28, 18, 6, 4),

    # ---- Zone 6: Spike-Teppich ----
    (775, SCREEN_HEIGHT - SPIKE_H, SPIKE_W, SPIKE_H, 0),
    (787, SCREEN_HEIGHT - SPIKE_H, SPIKE_W, SPIKE_H, 0),
    (799, SCREEN_HEIGHT - SPIKE_H, SPIKE_W, SPIKE_H, 0),
    (811, SCREEN_HEIGHT - SPIKE_H, SPIKE_W, SPIKE_H, 0),
    # Platten drüber
    (773, SCREEN_HEIGHT - 34, 26, 6, 4),
    (807, SCREEN_HEIGHT - 34, 26, 6, 4),

    # ---- Zone 7: Runde + Säulen Kombo ----
    (860, SCREEN_HEIGHT - 7, 14, 14, 21),
    (895, SCREEN_HEIGHT - 34, 12, 34, 20),  # Bodensäule
    (858, SCREEN_HEIGHT - 50, 26, 6, 4),    # Plattform

    # ---- Zone 8: Lava breit ----
    (940,  SCREEN_HEIGHT - 8, 70, 8, 10),
    (952,  SCREEN_HEIGHT - 28, 18, 6, 4),
    (982,  SCREEN_HEIGHT - 46, 18, 6, 4),
    (1000, SCREEN_HEIGHT - 28, 18, 6, 4),

    # ---- Zone 9: Feder-Hochsprung ----
    (1025, SCREEN_HEIGHT - 6, 18, 6, 7),    # Feder
    (1050, SCREEN_HEIGHT - 55, 28, 6, 4),   # Plattform hoch

    # Zacken danach
    (1090, SCREEN_HEIGHT - SPIKE_H, SPIKE_W, SPIKE_H, 0),
    (1105, SCREEN_HEIGHT - SPIKE_H, SPIKE_W, SPIKE_H, 0),

    # ---- Zone 10: Bodensäulen-Reihe ----
    (1140, SCREEN_HEIGHT - 30, 12, 30, 20),
    (1172, SCREEN_HEIGHT - 45, 12, 45, 20),
    (1204, SCREEN_HEIGHT - 30, 12, 30, 20),
    # Helfer-Plattform
    (1155, SCREEN_HEIGHT - 58, 24, 6, 4),

    # ---- Zone 11: Runde Gefahr-Gasse ----
    (1250, SCREEN_HEIGHT - 7, 14, 14, 21),
    (1278, SCREEN_HEIGHT - 7, 14, 14, 21),
    (1306, SCREEN_HEIGHT - 7, 14, 14, 21),
    # Plattform drüber
    (1248, SCREEN_HEIGHT - 38, 60, 6, 4),

    # ---- Zone 12: Gemischte Gefahr ----
    (1360, SCREEN_HEIGHT - SPIKE_H, SPIKE_W, SPIKE_H, 0),
    (1375, SCREEN_HEIGHT - SPIKE_H, SPIKE_W, SPIKE_H, 0),
    (1390, SCREEN_HEIGHT - 7, 14, 14, 21),  # Rund

    # Lava
    (1420, SCREEN_HEIGHT - 8, 55, 8, 10),
    (1432, SCREEN_HEIGHT - 30, 18, 6, 4),
    (1462, SCREEN_HEIGHT - 48, 18, 6, 4),

    # ---- Zone 13: Feder + Bodensäulen ----
    (1495, SCREEN_HEIGHT - 6, 18, 6, 7),    # Feder
    (1525, SCREEN_HEIGHT - 38, 12, 38, 20),
    (1560, SCREEN_HEIGHT - 50, 12, 50, 20),
    (1595, SCREEN_HEIGHT - 30, 12, 30, 20),
    # Plattform
    (1538, SCREEN_HEIGHT - 62, 28, 6, 4),

    # ---- Zone 14: Finale Gauntlet ----
    (1640, SCREEN_HEIGHT - SPIKE_H, SPIKE_W, SPIKE_H, 0),
    (1655, SCREEN_HEIGHT - SPIKE_H, SPIKE_W, SPIKE_H, 0),
    (1640, SCREEN_HEIGHT - 40, 26, 6, 4),

    (1690, SCREEN_HEIGHT - 8, 50, 8, 10),   # Lava
    (1700, SCREEN_HEIGHT - 32, 18, 6, 4),
    (1728, SCREEN_HEIGHT - 50, 18, 6, 4),

    (1750, SCREEN_HEIGHT - 7, 14, 14, 21),
    (1782, SCREEN_HEIGHT - 42, 12, 42, 20),
    (1748, SCREEN_HEIGHT - 56, 26, 6, 4),

    (1820, SCREEN_HEIGHT - SPIKE_H, SPIKE_W, SPIKE_H, 0),
    (1835, SCREEN_HEIGHT - SPIKE_H, SPIKE_W, SPIKE_H, 0),
    (1850, SCREEN_HEIGHT - SPIKE_H, SPIKE_W, SPIKE_H, 0),
    (1818, SCREEN_HEIGHT - 40, 28, 6, 4),
    (1852, SCREEN_HEIGHT - 55, 22, 6, 4),

    # Letzter Lava-Graben
    (1900, SCREEN_HEIGHT - 8, 60, 8, 10),
    (1912, SCREEN_HEIGHT - 32, 18, 6, 4),
    (1942, SCREEN_HEIGHT - 50, 18, 6, 4),

    # ---- ZIEL ----
    (1980, 0, 16, SCREEN_HEIGHT, 5),
]

# =========================
# SOUND SETUP
# =========================
def setup_sounds():
    pyxel.sound(0).set(
        "e2e2c2e2 g2g2e2g2 a2a2g2a2 b2b2a2b2",
        "p", "4", "n", 28
    )
    pyxel.sound(1).set("c2e2g2",    "s", "765", "n", 8)   # Sprung
    pyxel.sound(2).set("g0e0c0",    "n", "754", "n", 7)   # Schaden/Game Over
    pyxel.sound(3).set("c2e2g2c3e3g3c4", "s", "5566677", "n", 10)  # Gewonnen
    pyxel.sound(4).set("c2g2c3g3",  "s", "5677", "n", 6)  # Feder
    pyxel.music(0).set([0], [], [], [])

# =========================
# SOFORT GAME OVER
# =========================
def trigger_game_over():
    global game_over
    pyxel.play(2, 2)
    pyxel.stop()
    game_over = True

# =========================
# UPDATE
# =========================
def update():
    global player_x, player_y, y_velocity, is_jumping, on_platform
    global camera_x, score, time_left, game_over, game_won

    if game_over or game_won:
        if pyxel.btnp(pyxel.KEY_R):
            restart()
        return

    # --- Steuerung ---
    if pyxel.btn(pyxel.KEY_RIGHT):
        camera_x += player_speed
        score    += player_speed
        if score >= WIN_SCORE:
            score = WIN_SCORE
            game_won = True
            pyxel.play(3, 3)
            pyxel.stop()
            return

    if pyxel.btn(pyxel.KEY_LEFT):
        camera_x = max(0, camera_x - player_speed)

    if pyxel.btnp(pyxel.KEY_UP) and (not is_jumping or on_platform):
        is_jumping  = True
        on_platform = False
        y_velocity  = player_jump_v
        pyxel.play(1, 1)

    # --- Physik ---
    y_velocity += gravity
    player_y   += y_velocity

    # Boden
    ground = float(SCREEN_HEIGHT - PLAYER_SIZE)
    if player_y >= ground:
        player_y   = ground
        y_velocity = 0.0
        is_jumping  = False
        on_platform = False

    # --- Zeit ---
    time_left -= 1.0 / 30.0
    if time_left <= 0:
        time_left = 0
        trigger_game_over()
        return

    on_platform = False

    # --- Kollision ---
    px = player_x
    py = player_y
    pw = PLAYER_SIZE
    ph = PLAYER_SIZE

    for ox, oy, ow, oh, t in obstacles:
        sx = ox - camera_x  # Bildschirm-X des Hindernisses

        if t == 21:
            # Kreisförmig: Mittelpunkt + Radius
            radius = ow // 2
            cx_w = ox + radius          # Welt-Mittelpunkt X
            cy_w = oy + radius          # Welt-Mittelpunkt Y (oy = SCREEN_HEIGHT-radius)
            # Korrektur: oy ist so gesetzt dass Kreis am Boden sitzt
            # Bei Definition (x, SCREEN_HEIGHT-r, 2r, 2r, 21) => cy = SCREEN_HEIGHT-r+r = SCREEN_HEIGHT
            # Wir nutzen cy_w = oy + radius als Mittelpunkt
            pcx = px + pw // 2
            pcy = py + ph // 2
            cx_s = cx_w - camera_x
            dx = pcx - cx_s
            dy = pcy - cy_w
            dist2 = dx * dx + dy * dy
            hit_r = radius + pw // 2 - 1
            if dist2 < hit_r * hit_r:
                trigger_game_over()
                return
            continue

        # Rechteck-AABB
        if not (px + pw > sx and sx + ow > px and
                py + ph > oy and py < oy + oh):
            continue

        prev_y = py - y_velocity

        if t == 0:   # Spike -> Game Over
            trigger_game_over()
            return

        elif t == 4:  # Plattform -> betreten von oben
            if oy + 3 >= prev_y + ph:
                player_y   = oy - ph
                y_velocity = 0.0
                is_jumping  = False
                on_platform = True

        elif t == 5:  # Ziel
            game_won = True
            pyxel.play(3, 3)
            pyxel.stop()
            return

        elif t == 7:  # Feder
            if oy + 4 >= prev_y + ph:
                player_y   = oy - ph
                y_velocity = player_jump_v * 2.2
                pyxel.play(2, 4)

        elif t == 10:  # Lava -> Game Over
            trigger_game_over()
            return

        elif t == 20:  # Bodensäule -> von oben = Game Over (man muss drüber springen)
            # Von oben: wenn Spieler drauf landet -> Game Over
            # (man MUSS darüber springen, nicht draufstehen)
            trigger_game_over()
            return

# =========================
# DRAW HELPERS
# =========================
def draw_spike(sx, oy, ow, oh):
    pyxel.tri(sx, oy + oh, sx + ow, oy + oh, sx + ow // 2, oy, 8)

def draw_platform(sx, oy, ow, oh):
    pyxel.rect(sx, oy, ow, oh, 12)      # hellblau
    pyxel.rect(sx, oy, ow, 2, 7)        # weisser Rand oben
    pyxel.rectb(sx, oy, ow, oh, 6)      # dunkelblauer Rahmen

def draw_lava(sx, oy, ow, oh):
    fc = pyxel.frame_count
    c  = 9 if (fc // 8) % 2 == 0 else 8
    pyxel.rect(sx, oy, ow, oh, c)
    for i in range(0, ow, 4):
        wave = int(math.sin(fc * 0.2 + i * 0.5) * 2)
        yy   = oy + max(0, min(oh - 1, wave))
        pyxel.pset(sx + i, yy, 10)

def draw_spring(sx, oy, ow, oh):
    pyxel.rect(sx,     oy + oh - 4, ow,     4,      10)
    pyxel.rect(sx + 2, oy,          ow - 4, oh - 4, 11)
    pyxel.rectb(sx + 2, oy,         ow - 4, oh - 4, 3)

def draw_goal(sx, oy, ow, oh):
    fc = pyxel.frame_count
    c  = 10 if (fc // 12) % 2 == 0 else 9
    pyxel.rect(sx, oy, ow, oh, c)
    pyxel.text(sx + 2, SCREEN_HEIGHT // 2 - 4, "!", 7)

def draw_ground_column(sx, oy, ow, oh):
    # Braune Bodensäule mit Holzmaserung
    pyxel.rect(sx, oy, ow, oh, 4)
    for yy in range(oy, oy + oh, 7):
        pyxel.line(sx + 2, yy, sx + ow - 2, yy, 14)
    pyxel.rectb(sx, oy, ow, oh, 2)
    # Spitze oben (Dreieck)
    half = ow // 2
    pyxel.tri(sx, oy, sx + ow, oy, sx + half, oy - 6, 2)

def draw_circle_obstacle(sx, oy, ow, oh):
    # Grauer Kreis
    cx = sx + ow // 2
    cy = oy + oh // 2
    r  = ow // 2
    pyxel.circ(cx, cy, r, 13)       # grau gefüllt
    pyxel.circb(cx, cy, r, 5)       # dunklerer Rand
    # Glanzpunkt
    pyxel.pset(cx - r // 3, cy - r // 3, 7)

# =========================
# DRAW
# =========================
def draw():
    pyxel.cls(1)

    # Boden-Linie
    pyxel.line(0, SCREEN_HEIGHT - 1, SCREEN_WIDTH, SCREEN_HEIGHT - 1, 5)

    # Hindernisse
    for ox, oy, ow, oh, t in obstacles:
        sx = ox - camera_x
        if sx + ow < -8 or sx > SCREEN_WIDTH + 8:
            continue

        if t == 0:
            draw_spike(sx, oy, ow, oh)
        elif t == 4:
            draw_platform(sx, oy, ow, oh)
        elif t == 5:
            draw_goal(sx, oy, ow, oh)
        elif t == 7:
            draw_spring(sx, oy, ow, oh)
        elif t == 10:
            draw_lava(sx, oy, ow, oh)
        elif t == 20:
            draw_ground_column(sx, oy, ow, oh)
        elif t == 21:
            draw_circle_obstacle(sx, oy, ow, oh)

    # Spieler
    pyxel.blt(player_x, int(player_y) - 7, 0, 0, 0, 16, 34, 0)

    # HUD
    pyxel.text(5, 5,  f"Time {int(time_left)}", 9)
    pyxel.text(5, 14, f"Score {score}/{WIN_SCORE}", 10)

    # Score-Balken
    bar_w      = SCREEN_WIDTH - 10
    bar_filled = int(bar_w * min(score, WIN_SCORE) / WIN_SCORE)
    pyxel.rect(5,  SCREEN_HEIGHT - 6, bar_w,      4, 5)
    pyxel.rect(5,  SCREEN_HEIGHT - 6, bar_filled, 4, 12)
    pyxel.rectb(5, SCREEN_HEIGHT - 6, bar_w,      4, 7)

    # Game Over
    if game_over:
        pyxel.rect(30, 44, 100, 32, 0)
        pyxel.rectb(30, 44, 100, 32, 8)
        pyxel.text(52, 51, "GAME  OVER", 8)
        pyxel.text(40, 62, f"Score: {score}  R=Neu", 7)

    # Gewonnen
    if game_won:
        fc = pyxel.frame_count
        c  = 10 if (fc // 10) % 2 == 0 else 9
        pyxel.rect(20, 38, 120, 44, 0)
        pyxel.rectb(20, 38, 120, 44, c)
        pyxel.text(38, 46, "YOU'RE THE WINNER!", 10)
        pyxel.text(44, 57, f"Score: {score}", 7)
        pyxel.text(44, 67, f"Zeit:  {int(TIME_LIMIT - time_left)}s", 7)
        pyxel.text(44, 75, "R = Neu starten", 13)

# =========================
# RESTART
# =========================
def restart():
    global player_x, player_y, y_velocity, is_jumping, on_platform
    global camera_x, score, time_left, game_over, game_won

    player_x    = 20
    player_y    = float(SCREEN_HEIGHT - PLAYER_SIZE)
    y_velocity  = 0.0
    is_jumping  = False
    on_platform = False
    camera_x    = 0
    score       = 0
    time_left   = float(TIME_LIMIT)
    game_over   = False
    game_won    = False

    pyxel.playm(0, loop=True)

# =========================
# INIT
# =========================
pyxel.init(SCREEN_WIDTH, SCREEN_HEIGHT, title="Platformer", fps=30)
pyxel.load("res.pyxres")
setup_sounds()
pyxel.playm(0, loop=True)
pyxel.run(update, draw)