import pyxel
import random

SCREEN_WIDTH = 512
SCREEN_HEIGHT = 512
GAME_SPEED = 1.0
FLOOR_Y = SCREEN_HEIGHT - 80
PLAYER_Y = FLOOR_Y

MAX_FALLING_STONES = 4
MAX_ENEMIES = 4
MAX_WAGONS = 3
MAX_HOLES = 3

PLAYER_W = 28
PLAYER_H = 40

# Beruehrung Rechtecke
def rect_collide(x1, y1, w1, h1, x2, y2, w2, h2):
    return (
        x1 < x2 + w2 and
        x2 < x1 + w1 and
        y1 < y2 + h2 and
        y2 < y1 + h1
    )


class Player:
    def __init__(self):
        self.reset()

    def reset(self):
        self.x = SCREEN_WIDTH // 2 - 14
        self.y = PLAYER_Y
        self.vy = 0
        self.on_ground = True
        self.alive = True
        self.dig_count = 0
        self.on_rope = False
        self.rope_x = 0

    def update(self):
        if pyxel.btnp(pyxel.KEY_G):
            self.dig_count += 1

        if not self.alive:
            return 0

        scroll = 0

        if pyxel.btn(pyxel.KEY_RIGHT):
            scroll = 1.5 * GAME_SPEED
        if pyxel.btn(pyxel.KEY_LEFT):
            scroll = -1.2 * GAME_SPEED

        self.x = SCREEN_WIDTH // 2 - 14

        if self.on_rope:
            self.vy = 0
            self.on_ground = False
            self.x = self.rope_x - PLAYER_W // 2

            if pyxel.btnp(pyxel.KEY_SPACE):
                self.on_rope = False
                self.vy = -3.2

            return scroll

        if self.on_ground and pyxel.btnp(pyxel.KEY_SPACE):
            self.vy = -4.8
            self.on_ground = False

        if not self.on_ground:
            self.vy += 0.25
            self.y += self.vy

            if self.y >= PLAYER_Y:
                self.y = PLAYER_Y
                self.vy = 0
                self.on_ground = True

        return scroll

    def draw(self):
        if self.on_rope:
            pyxel.blt(self.x, self.y - 28, 0, 0, 16, 32, 32, 0)
            pyxel.line(self.x + 8, self.y - 20, self.x + 8, self.y - 4, 7)
        else:
            pyxel.blt(self.x, self.y - 28, 0, 0, 16, 32, 32, 0)


class Diamond:
    def __init__(self):
        self.active = False

    def spawn(self):
        self.active = True
        self.x = SCREEN_WIDTH + random.randint(20, 80)
        self.y = random.randint(PLAYER_Y - 40, PLAYER_Y - 12)

    def update(self, speed):
        if not self.active:
            return
        self.x -= speed
        if self.x < -20:
            self.active = False

    def draw(self):
        if self.active:
            pyxel.tri(self.x, self.y - 8, self.x - 8, self.y, self.x + 8, self.y, 10)
            pyxel.tri(self.x, self.y + 8, self.x - 8, self.y, self.x + 8, self.y, 10)


class FallingStone:
    def __init__(self):
        self.active = False

    def spawn(self):
        self.active = True
        self.size = random.randint(12, 20)
        self.x = random.randint(0, SCREEN_WIDTH - self.size)
        self.y = -10
        self.vy = random.uniform(0.8, 1.6)

    def update(self):
        if not self.active:
            return
        self.y += self.vy
        if self.y > SCREEN_HEIGHT:
            self.active = False

    def draw(self):
        if self.active:
            pyxel.rect(self.x, self.y, self.size, self.size, 13)


class Enemy:
    def __init__(self):
        self.active = False

    def spawn(self):
        self.active = True
        self.x = SCREEN_WIDTH + random.randint(0, 60)
        self.y = PLAYER_Y
        self.base_speed = 0.6

    def update(self, scroll, level_factor):
        if not self.active:
            return
        self.x -= self.base_speed * level_factor + scroll
        if self.x < -30:
            self.active = False

    def draw(self):
        if self.active:
            pyxel.rect(self.x, self.y - 12, 24, 12, 13)


class Wagon:
    def __init__(self):
        self.active = False

    def spawn(self):
        self.active = True
        self.x = SCREEN_WIDTH + random.randint(40, 120)
        self.y = PLAYER_Y - 4
        self.speed = 1.2

    def update(self, scroll):
        if not self.active:
            return
        self.x -= self.speed + scroll
        if self.x < -24:
            self.active = False

    def draw(self):
        if self.active:
            pyxel.rect(self.x, self.y, 18, 6, 5)
            pyxel.rect(self.x, self.y - 4, 4, 4, 8)
            pyxel.rect(self.x + 4, self.y - 4, 4, 4, 8)
            pyxel.rect(self.x + 8, self.y - 4, 4, 4, 8)
            pyxel.rect(self.x + 12, self.y - 4, 4, 4, 8)
            pyxel.circ(self.x + 4, self.y + 7, 2, 0)
            pyxel.circ(self.x + 13, self.y + 7, 2, 0)


class Hole:
    def __init__(self):
        self.active = False

    def spawn(self):
        self.active = True
        self.width = random.randint(90, 140)
        self.x = SCREEN_WIDTH + random.randint(80, 180)

    def update(self, scroll):
        if not self.active:
            return
        self.x -= 1.0 + scroll
        if self.x + self.width < 0:
            self.active = False

    def draw(self):
        if self.active:
            pyxel.rect(self.x, FLOOR_Y, self.width, SCREEN_HEIGHT - FLOOR_Y, 0)


class Rope:
    def __init__(self):
        self.active = False
        self.swing = 0
    def spawn(self, hole_x, hole_w):
        self.active = True
        self.x = hole_x + hole_w // 2
        self.top_y = 120
        self.bottom_y = FLOOR_Y - 10
        self.swing = random.randint (0,100)
    def update(self, scroll):
        if not self.active:
            return
        self.x -= 1.0 + scroll
        if self.x < -20:
            self.active = False

    def draw(self):
        if self.active:
            pyxel.line(self.x, self.top_y, self.x, self.bottom_y, 6)
            pyxel.circ(self.x, self.top_y, 3, 7)


class Confetti:
    def __init__(self):
        self.x = random.randint(0, SCREEN_WIDTH)
        self.y = -10
        self.vy = random.uniform(1, 2)
        self.color = random.randint(7, 15)

    def update(self):
        self.y += self.vy

    def draw(self):
        pyxel.rect(self.x, self.y, 2, 2, self.color)


class Game:
    def __init__(self):
        pyxel.init(SCREEN_WIDTH, SCREEN_HEIGHT, title="Mine Runner", fps=60)
        pyxel.load("res.pyxres")
        pyxel.playm(0, loop=True)

        self.player = Player()
        self.diamond = Diamond()
        self.stones = [FallingStone() for _ in range(MAX_FALLING_STONES)]
        self.enemies = [Enemy() for _ in range(MAX_ENEMIES)]
        self.wagons = [Wagon() for _ in range(MAX_WAGONS)]
        self.holes = [Hole() for _ in range(MAX_HOLES)]
        self.ropes = [Rope() for _ in range(MAX_HOLES)]

        self.world_offset = 0
        self.tile = 24

        self.ceiling = [random.randint(20, 60) for _ in range(200)]
        self.wall = [random.randint(50, 100) for _ in range(200)]

        self.next_diamond = 60
        self.next_stone = 80
        self.next_enemy = 120
        self.next_wagon = 90
        self.next_hole = 160

        self.level = 1
        self.diamonds = 0

        self.game_over = False
        self.win = False
        self.level_transition = False
        self.started = False

        self.confetti = []

        self.stars = []
        for _ in range(60):
            self.stars.append([
                random.randint(0, SCREEN_WIDTH),
                random.randint(0, SCREEN_HEIGHT),
                random.randint(1, 2)
            ])

        pyxel.run(self.update, self.draw)

    def reset(self):
        self.player.reset()
        self.diamond = Diamond()
        self.stones = [FallingStone() for _ in range(MAX_FALLING_STONES)]
        self.enemies = [Enemy() for _ in range(MAX_ENEMIES)]
        self.wagons = [Wagon() for _ in range(MAX_WAGONS)]
        self.holes = [Hole() for _ in range(MAX_HOLES)]
        self.ropes = [Rope() for _ in range(MAX_HOLES)]

        self.world_offset = 0
        self.next_diamond = 60
        self.next_stone = 80
        self.next_enemy = 120
        self.next_wagon = 90
        self.next_hole = 160

        self.level = 1
        self.diamonds = 0

        self.game_over = False
        self.win = False
        self.level_transition = False
        self.started = False

        self.confetti = []

    def update(self):
        for star in self.stars:
            star[0] -= 0.2
            if star[0] < 0:
                star[0] = SCREEN_WIDTH
                star[1] = random.randint(0, SCREEN_HEIGHT)

        if not self.started:
            if pyxel.btnp(pyxel.KEY_SPACE):
                self.started = True
            return

        if self.game_over:
            if pyxel.btnp(pyxel.KEY_R):
                self.reset()
            return

        if self.win:
            if len(self.confetti) < 200:
                self.confetti.append(Confetti())
            for c in self.confetti:
                c.update()
            return

        if self.level_transition:
            if pyxel.btnp(pyxel.KEY_SPACE):
                self.level = 2
                self.diamonds = 0
                self.level_transition = False
            return

        scroll = self.player.update()
        self.world_offset += scroll

        level_factor = 1.5 if self.level == 1 else 2.0

        if not self.diamond.active:
            self.next_diamond -= 1
            if self.next_diamond <= 0:
                self.diamond.spawn()
                self.next_diamond = random.randint(80, 120)

        self.diamond.update(scroll)

        self.next_stone -= 1
        if self.next_stone <= 0:
            for s in self.stones:
                if not s.active:
                    s.spawn()
                    break
            self.next_stone = random.randint(80, 140)

        for s in self.stones:
            s.update()

        self.next_enemy -= 1
        if self.next_enemy <= 0:
            for e in self.enemies:
                if not e.active:
                    e.spawn()
                    break
            self.next_enemy = random.randint(110, 170) if self.level == 1 else random.randint(80, 130)

        for e in self.enemies:
            e.update(scroll, level_factor)

        if self.level == 2:
            self.next_wagon -= 2
        else:
            self.next_wagon -= 1

        if self.next_wagon <= 0:
            for w in self.wagons:
                if not w.active:
                    w.spawn()
                    break
            self.next_wagon = random.randint(50, 100) if self.level == 2 else random.randint(90, 140)

        for w in self.wagons:
            w.update(scroll)

        self.next_hole -= 1
        if self.next_hole <= 0:
            for i in range(len(self.holes)):
                if not self.holes[i].active and not self.ropes[i].active:
                    self.holes[i].spawn()
                    self.ropes[i].spawn(self.holes[i].x, self.holes[i].width)
                    break
            self.next_hole = random.randint(180, 280) if self.level == 1 else random.randint(130, 220)

        for h in self.holes:
            h.update(scroll)

        for r in self.ropes:
            r.update(scroll)

        self.check_rope_attach()
        self.check_hole_fall()
        self.check_collisions()

    def check_rope_attach(self):
        p = self.player

        if p.on_rope:
            attached = False
            for r in self.ropes:
                if r.active and abs((p.x + PLAYER_W // 2) - r.x) < 8:
                    p.rope_x = r.x
                    p.y = min(max(p.y, r.top_y + 18), r.bottom_y + 18)
                    attached = True
                    break
            if not attached:
                p.on_rope = False
            return

        if p.on_ground:
            return

        px_center = p.x + PLAYER_W // 2
        py_top = p.y - PLAYER_H

        for r in self.ropes:
            if not r.active:
                continue

            if (
                abs(px_center - r.x) < 10 and
                py_top < r.bottom_y and
                p.y > r.top_y + 10
            ):
                p.on_rope = True
                p.rope_x = r.x
                p.vy = 0
                return

    def check_hole_fall(self):
        p = self.player

        if p.on_rope:
            return

        if not p.on_ground:
            return

        px_left = p.x + 2
        px_right = p.x + PLAYER_W - 2

        for h in self.holes:
            if h.active:
                if px_left > h.x and px_right < h.x + h.width:
                    self.game_over = True
                    self.player.alive = False
                    pyxel.play(2, 2)
                    return

    def check_collisions(self):
        p = self.player

        if self.player.dig_count >= 20:
            self.diamonds += 1
            self.player.dig_count = 0

        if self.diamond.active and rect_collide(
            p.x, p.y - 20, 16, 24,
            self.diamond.x - 6, self.diamond.y - 8, 12, 16
        ):
            self.diamond.active = False
            self.diamonds += 1
            pyxel.play(1, 1)

            if self.level == 1 and self.diamonds >= 3:
                self.level_transition = True

        if self.level == 2 and self.diamonds >= 5:
            self.win = True
            pyxel.play(3, 3)

        for s in self.stones:
            if s.active and rect_collide(p.x, p.y - 20, 16, 24, s.x, s.y, s.size, s.size):
                self.game_over = True
                self.player.alive = False
                pyxel.play(2, 2)


        for w in self.wagons:
            if w.active and rect_collide(p.x, p.y - 20, 16, 24, w.x, w.y - 4, 18, 12):
                self.game_over = True
                self.player.alive = False
                pyxel.play(2, 2)

    def draw(self):
        pyxel.cls(0)

        for x, y, s in self.stars:
            if self.level == 1:
                pyxel.circ(x, y, s, 7)
            else:
                pyxel.circ(x, y, s, 8)

        if self.win:
            pyxel.text(170, 120, "DU HAST GEWONNEN!", 10)
            for c in self.confetti:
                c.draw()
            return

        if not self.started:
            pyxel.text(205, 150, "MINE RUNNER", 7)
            pyxel.text(190, 190, "SPACE zum Starten", 6)
            pyxel.text(145, 225, "Sammle Diamanten & ueberlebe!", 5)
            pyxel.text(150, 255, "Du bist ein Minenarbeiter.", 7)
            pyxel.text(150, 275, "Druecke G zum Graben.", 7)
            pyxel.text(120, 295, "Springe auf Seile, um Loecher zu ueberqueren.", 7)
            return

        if self.level_transition:
            pyxel.text(220, 180, "BRAVO!", 7)
            pyxel.text(165, 210, "Sammle jetzt 5 Diamanten", 7)
            pyxel.text(175, 240, "SPACE zum Fortfahren", 6)
            return

        start = int(self.world_offset // self.tile)
        offset_x = int(self.world_offset % self.tile)

        for i in range(-2, SCREEN_WIDTH // self.tile + 3):
            idx = (start + i) % len(self.ceiling)
            x = i * self.tile - offset_x

            color = 13 if self.level == 1 else 12

            pyxel.rect(x, 0, self.tile, self.ceiling[idx], color)
            h = self.wall[idx]
            pyxel.rect(x, FLOOR_Y - h, self.tile, h, color)

        pyxel.rect(0, FLOOR_Y, SCREEN_WIDTH, SCREEN_HEIGHT - FLOOR_Y, 10)

        for h in self.holes:
            h.draw()

        for r in self.ropes:
            r.draw()

        self.diamond.draw()
        for s in self.stones:
            s.draw()
        for e in self.enemies:
            e.draw()
        for w in self.wagons:
            w.draw()
        self.player.draw()

        pyxel.text(10, 10, f"Level: {self.level}", 7)
        pyxel.text(10, 24, f"Diamanten: {self.diamonds}", 7)
        pyxel.text(10, 38, f"Graben: {self.player.dig_count}/20", 7)

        if self.player.on_rope:
            pyxel.text(10, 52, "Am Seil!", 11)

        if self.game_over:
            pyxel.text(210, 170, "GAME OVER", 8)
            pyxel.text(205, 190, "R = Neustart", 7)


Game()