"""
game_engine.py - Arcade-engine voor Sprint 7 (v2, METHODE-gebaseerd). NIET AANPASSEN.

In sprint 6 muteerde deze engine ZELF de attributen van je objecten (player.x -= ...).
Nu roept de engine METHODES aan op jouw objecten. Jouw classes bepalen zelf hoe ze
bewegen, schieten, schade oplopen, sneller worden en hun eigen rapport maken. De engine
leest alleen nog attributen om te TEKENEN (x, y, width, height, color) en om botsingen
te bepalen. De engine toont ook een startscherm met uitleg en een game-over-scherm.

--------------------------------------------------------------------------
HET CONTRACT - welke METHODES de engine aanroept:

  game.player  - naast de teken-attributen x, y, width, height, color, health:
      move(direction, width)  direction is -1 (links), 0, of +1 (rechts); de speler
                              verschuift zichzelf EN houdt zich binnen [0, width-breedte]
      update()                wordt elk frame aangeroepen (bijv. om de schietpauze te laten aflopen)
      can_shoot()  -> bool    True als de speler nu mag schieten
      shoot()      -> bullet  geeft een NIEUW bullet-object terug, boven de speler,
                              en start de schietpauze
      take_damage(n)          verlaagt de eigen health met n
      is_dead()    -> bool    True als de speler geen leven meer heeft

  game.enemies - een LIJST; elke enemy heeft de teken-attributen EN:
      update()                laat zichzelf een stap vallen
      off_screen(height) -> bool   True als de vijand onder het scherm is
      respawn(width)          zet zichzelf weer bovenaan, op een nieuwe x
      speed_up()              wordt bij elk nieuw level aangeroepen: de vijand wordt sneller

  game.bullets - een LIJST; elke bullet heeft de teken-attributen EN:
      update()                beweegt zichzelf omhoog
      off_screen() -> bool    True als de kogel boven het scherm verdwenen is

  game         - het spelobject met:
      player, enemies, bullets, score, width, height   (attributen)
      add_score(n)            telt n bij de score op
      level()      -> int     het huidige level (begint op 1)
      record_shot()           de engine meldt: er is een kogel afgevuurd
      record_hit()            de engine meldt: een kogel heeft een vijand geraakt
      summary()    -> str     tekst voor het game-over-scherm (mag uit meerdere regels bestaan)

  y telt naar BENEDEN. Kleuren zijn RGB-tuples. De engine tekent, leest toetsen,
  detecteert botsingen en beheert de kogellijst; jouw METHODES doen de rest.
--------------------------------------------------------------------------
"""

import pygame

BACKGROUND = (15, 15, 22)
WHITE = (240, 240, 240)
GRAY = (150, 150, 160)
CYAN = (0, 200, 255)
YELLOW = (255, 220, 0)
RED = (255, 80, 80)
GREEN = (80, 220, 120)


def _overlap(a, b):
    """True als twee rechthoeken (objecten met x/y/width/height) elkaar raken."""
    return (a.x < b.x + b.width and a.x + a.width > b.x and
            a.y < b.y + b.height and a.y + a.height > b.y)


def _text(screen, font, text, color, y):
    """Tekent een regel horizontaal gecentreerd op hoogte y."""
    image = font.render(text, True, color)
    screen.blit(image, ((screen.get_width() - image.get_width()) // 2, y))


def _wait_screen(screen, clock, fonts, title, title_color, lines, footer, keys, ignore_frames=0):
    """Toont een scherm en wacht op een toets uit keys. Toetsen in de eerste ignore_frames
    frames tellen niet mee, zodat een speler die nog toetsen vasthoudt het scherm niet per
    ongeluk wegdrukt. Geeft False terug als het venster wordt gesloten."""
    big, font, small = fonts
    pygame.event.clear()
    frames = 0
    while True:
        frames += 1
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return False
            if event.type == pygame.KEYDOWN and frames > ignore_frames and event.key in keys:
                return True
        screen.fill(BACKGROUND)
        _text(screen, big, title, title_color, 70)
        y = 160
        for line in lines:
            _text(screen, font, line, WHITE, y)
            y += 30
        _text(screen, small, footer, YELLOW, screen.get_height() - 70)
        pygame.display.flip()
        clock.tick(30)


START_LINES = [
    "Pijltjes links / rechts: bewegen",
    "Spatiebalk (ingedrukt houden): schieten",
    "",
    "Rode blokken vallen naar beneden.",
    "Ontwijk ze: +1 punt voor elk blok dat je mist.",
    "Schiet ze neer: +5 punten.",
    "Raakt een blok jou? Dan verlies je een leven.",
    "",
    "Elke 20 punten ga je een level omhoog.",
    "Hoger level = snellere vijanden.",
]


def run_world(game):
    """Start het spel. Geef je hele Game-object mee."""
    pygame.init()
    screen = pygame.display.set_mode((game.width, game.height))
    pygame.display.set_caption("Sprint 7 - Arcade Dodge (methode-gebaseerd)")
    clock = pygame.time.Clock()
    fonts = (pygame.font.SysFont(None, 56), pygame.font.SysFont(None, 26), pygame.font.SysFont(None, 22))
    big, font, small = fonts

    # Startscherm: uitleg, en wachten tot de speler klaar is.
    if not _wait_screen(screen, clock, fonts, "ARCADE DODGE", CYAN, START_LINES,
                        "Druk op SPATIE om te starten", {pygame.K_SPACE, pygame.K_RETURN}):
        pygame.quit()
        return

    last_level = game.level()
    banner = 0            # aantal frames dat de LEVEL-melding nog zichtbaar is
    window_closed = False
    running = True
    while running:
        # 1) Vensterknop
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
                window_closed = True

        keys = pygame.key.get_pressed()

        # 2) De speler ververst zichzelf; ingedrukte spatie -> de SPELER maakt zelf een kogel
        game.player.update()
        if keys[pygame.K_SPACE] and game.player.can_shoot():
            game.bullets.append(game.player.shoot())
            game.record_shot()

        # 3) Toetsen -> direction; de SPELER beweegt en klemt zichzelf
        direction = 0
        if keys[pygame.K_LEFT]:
            direction -= 1
        if keys[pygame.K_RIGHT]:
            direction += 1
        game.player.move(direction, game.width)

        # 4) Elke vijand valt zichzelf; onderaan -> zichzelf herspawnen
        for enemy in game.enemies:
            enemy.update()
            if enemy.off_screen(game.height):
                enemy.respawn(game.width)
                game.add_score(1)

        # 5) Elke kogel beweegt zichzelf; buiten beeld -> uit de lijst
        for bullet in game.bullets:
            bullet.update()
        game.bullets[:] = [b for b in game.bullets if not b.off_screen()]

        # 6) Botsing kogel x vijand (engine detecteert; objecten reageren)
        for bullet in game.bullets:
            for enemy in game.enemies:
                if _overlap(bullet, enemy):
                    enemy.respawn(game.width)
                    bullet.y = -100          # markeer voor opruimen (volgende frame)
                    game.add_score(5)
                    game.record_hit()

        # 7) Botsing vijand x speler
        for enemy in game.enemies:
            if _overlap(game.player, enemy):
                enemy.respawn(game.width)
                game.player.take_damage(1)
                if game.player.is_dead():
                    running = False

        # 8) Nieuw level? Dan worden alle vijanden sneller.
        level = game.level()
        if level > last_level:
            last_level = level
            banner = 90
            for enemy in game.enemies:
                enemy.speed_up()

        # 9) Tekenen op basis van attributen (net als in sprint 6)
        screen.fill(BACKGROUND)
        p = game.player
        pygame.draw.rect(screen, p.color, (p.x, p.y, p.width, p.height))
        for enemy in game.enemies:
            pygame.draw.rect(screen, enemy.color,
                             (enemy.x, enemy.y, enemy.width, enemy.height))
        for bullet in game.bullets:
            pygame.draw.rect(screen, bullet.color,
                             (bullet.x, bullet.y, bullet.width, bullet.height))

        hud = f"Score: {game.score}    Level: {level}    HP: {game.player.health}"
        screen.blit(font.render(hud, True, WHITE), (10, 10))
        ready = game.player.can_shoot()
        screen.blit(small.render("Kanon: klaar" if ready else "Kanon: laden...", True,
                                 GREEN if ready else GRAY), (10, 38))
        if banner > 0:
            _text(screen, big, f"LEVEL {level}", YELLOW, game.height // 2 - 30)
            banner -= 1
        _text(screen, small, "Pijltjes: bewegen    Spatie: schieten", GRAY, game.height - 18)

        pygame.display.flip()
        clock.tick(60)

    # Game-over-scherm met het rapport van jouw Game-object.
    if not window_closed:
        # SPATIE telt hier niet mee: die is al "schieten" en wordt vaak nog ingedrukt.
        _wait_screen(screen, clock, fonts, "GAME OVER", RED, str(game.summary()).splitlines(),
                     "Druk op ENTER of ESC om af te sluiten",
                     {pygame.K_RETURN, pygame.K_ESCAPE}, ignore_frames=45)

    pygame.quit()
