"""Spieler-Klasse mit Bewegung, Hitbox und Rendering."""
import pyxel
from constants import (
    SCREEN_W, SCREEN_H, TILE, PLAYER_SPEED,
    PLAYER_HITBOX_INSET, PLAYER_HITBOX_SIZE,
    PLAYER_U_BASE, PLAYER_V, SHIELD_U, SHIELD_V,
    DIR_DOWN, DIR_UP, DIR_LEFT, DIR_RIGHT, DIR_GHOST,
    COL_DARK, COL_RED, COL_SKIN, COL_BROWN, COL_WHITE, COL_LBLUE,
    SND_STEP_L, SND_STEP_R, CH_BASS, STEP_INTERVAL,
    rect_overlap
)


class Player:
    """Kapselt Position, Bewegung, Hitbox, Cooldowns und Rendering."""

    def __init__(self, x, y, has_res):
        self.x         = x
        self.y         = y
        self.direction = DIR_DOWN
        self.has_res   = has_res
        self.hit_cooldown = 0
        self.hit_penalty  = 0

        # Schritt-Sound
        self.step_timer = 0
        self.step_foot  = 0   # 0 = links, 1 = rechts (abwechselnd)
        self.is_moving  = False

    @property
    def hitbox(self):
        return (self.x + PLAYER_HITBOX_INSET,
                self.y + PLAYER_HITBOX_INSET,
                PLAYER_HITBOX_SIZE,
                PLAYER_HITBOX_SIZE)

    def collides_with(self, bx, by, bw, bh):
        px, py, pw, ph = self.hitbox
        return rect_overlap(px, py, pw, ph, bx, by, bw, bh)

    def get_input(self):
        dx, dy = 0, 0
        if   pyxel.btn(pyxel.KEY_LEFT):  dx = -PLAYER_SPEED; self.direction = DIR_LEFT
        elif pyxel.btn(pyxel.KEY_RIGHT): dx =  PLAYER_SPEED; self.direction = DIR_RIGHT
        elif pyxel.btn(pyxel.KEY_UP):    dy = -PLAYER_SPEED; self.direction = DIR_UP
        elif pyxel.btn(pyxel.KEY_DOWN):  dy =  PLAYER_SPEED; self.direction = DIR_DOWN
        self.is_moving = (dx != 0 or dy != 0)
        return dx, dy

    def move_free(self, dx, dy):
        self.x = max(0, min(SCREEN_W - TILE, self.x + dx))
        self.y = max(0, min(SCREEN_H - TILE, self.y + dy))

    def move_blocked(self, dx, dy, wall_check_fn):
        moved = False
        if dx != 0:
            nx = max(0, min(SCREEN_W - TILE, self.x + dx))
            if not wall_check_fn(nx, self.y):
                self.x = nx
                moved = True
        if dy != 0:
            ny = max(0, min(SCREEN_H - TILE, self.y + dy))
            if not wall_check_fn(self.x, ny):
                self.y = ny
                moved = True
        # Falls Wand blockiert: kein Schritt-Sound
        if not moved and self.is_moving:
            self.is_moving = False

    def take_hit(self):
        if self.hit_cooldown > 0:
            return False
        self.hit_penalty += 50
        self.hit_cooldown = 90
        return True

    @property
    def is_invincible(self):
        return self.hit_cooldown > 0

    def tick_cooldown(self):
        if self.hit_cooldown > 0:
            self.hit_cooldown -= 1

    def tick_footsteps(self):
        """Spielt Schritt-Sounds ab wenn der Spieler sich bewegt."""
        if self.is_moving:
            self.step_timer += 1
            if self.step_timer >= STEP_INTERVAL:
                self.step_timer = 0
                if self.has_res:
                    if self.step_foot == 0:
                        pyxel.play(CH_BASS, SND_STEP_L)
                    else:
                        pyxel.play(CH_BASS, SND_STEP_R)
                    self.step_foot = 1 - self.step_foot  # Abwechseln
        else:
            self.step_timer = 0

    def draw(self):
        self._draw_sprite(self.x, self.y, self.direction)

    def draw_at(self, x, y, direction):
        self._draw_sprite(x, y, direction)

    def draw_shield(self):
        if not self.is_invincible:
            return
        if 30 > self.hit_cooldown:
            if (self.hit_cooldown // 3) % 2 != 0:
                return
        if self.has_res:
            pyxel.blt(self.x, self.y, 0, SHIELD_U, SHIELD_V, 16, 16, 0)
        else:
            pyxel.circb(self.x + 8, self.y + 8, 9, COL_LBLUE)
            pyxel.circb(self.x + 8, self.y + 8, 10, COL_LBLUE)

    def _draw_sprite(self, x, y, direction):
        if self.has_res:
            pyxel.blt(x, y, 0, PLAYER_U_BASE + direction * 16,
                      PLAYER_V, 16, 16, 0)
        else:
            self._draw_fallback(x, y, direction)

    def _draw_fallback(self, x, y, direction):
        if direction == DIR_GHOST:
            pyxel.circ(x + 8, y + 6, 5, COL_WHITE)
            pyxel.rect(x + 3, y + 6, 10, 7, COL_WHITE)
            for i in range(3):
                pyxel.rect(x + 3 + i * 3, y + 12, 2, 3, COL_DARK)
            pyxel.pset(x + 6, y + 5, COL_DARK)
            pyxel.pset(x + 10, y + 5, COL_DARK)
            return
        pyxel.rect(x + 4, y + 6, 8, 8, COL_RED)
        pyxel.rect(x + 5, y + 1, 6, 6, COL_SKIN)
        if direction == DIR_DOWN:
            pyxel.pset(x + 7, y + 3, COL_DARK)
            pyxel.pset(x + 9, y + 3, COL_DARK)
        elif direction == DIR_UP:
            pyxel.rect(x + 5, y + 1, 6, 6, COL_BROWN)
        elif direction == DIR_LEFT:
            pyxel.pset(x + 6, y + 3, COL_DARK)
        elif direction == DIR_RIGHT:
            pyxel.pset(x + 9, y + 3, COL_DARK)
        pyxel.rect(x + 4, y + 13, 3, 3, COL_DARK)
        pyxel.rect(x + 9, y + 13, 3, 3, COL_DARK)
