"""Animierte Augen: Blinzeln und Blick folgt / wandert."""

from __future__ import annotations

import math
import random
import time

from PyQt6.QtCore import Qt, QRectF
from PyQt6.QtGui import QBrush, QColor, QPainter, QTransform

from jam.motion3d import Motion3D

EYE_LAYOUT = (
    (0.36, 0.30),
    (0.64, 0.30),
)


def _layout(
    w: int,
    h: int,
    *,
    scale: float,
    motion: Motion3D,
    moving: bool,
    dancing: bool,
) -> tuple[float, float, float, float, float, float]:
    cx, cy = w / 2.0, h * 0.72
    side = max(64.0, min(w, h) * 0.82)
    depth_scale = motion.depth_scale()
    hop = motion.hop_offset(moving=moving, dancing=dancing, size_scale=scale)
    pw = max(16.0, side * depth_scale)
    ph = max(16.0, side * (0.9 + motion.depth * 0.12) * motion.squash)
    ox = cx + motion.shadow_offset() * pw * 0.08 - pw / 2
    oy = cy - hop - ph / 2
    tilt = motion.pitch * 0.85
    skew = motion.skew()
    sway = motion.sway * 0.7
    return ox, oy, pw, ph, tilt, skew, sway


def face_rect(
    w: int,
    h: int,
    *,
    scale: float,
    bob: float,
    angle_y: float,
    face_left: bool,
    moving: bool,
    dancing: bool,
    motion: Motion3D | None = None,
) -> tuple[QRectF, float, float, float]:
    if motion is None:
        motion = Motion3D(yaw=angle_y, bob=bob)
        motion.tick(
            vx=1.0 if not face_left else -1.0,
            vy=0.0,
            bob_phase=bob,
            moving=moving,
            dancing=dancing,
            face_left=face_left,
        )
    ox, oy, pw, ph, tilt, skew, sway = _layout(
        w, h, scale=scale, motion=motion, moving=moving, dancing=dancing
    )
    return QRectF(ox, oy, pw, ph), tilt, skew, sway


def face_paint_transform(
    w: int,
    h: int,
    *,
    scale: float,
    bob: float,
    angle_y: float,
    face_left: bool,
    moving: bool,
    dancing: bool,
    motion: Motion3D | None = None,
) -> tuple[QTransform, QRectF]:
    if motion is None:
        motion = Motion3D(yaw=angle_y, bob=bob)
    ox, oy, pw, ph, tilt, skew, sway = _layout(
        w, h, scale=scale, motion=motion, moving=moving, dancing=dancing
    )
    cx, cy = w / 2.0, h * 0.72
    hop = motion.hop_offset(moving=moving, dancing=dancing, size_scale=scale)
    t = QTransform()
    t.translate(cx + sway * pw * 0.08, cy - hop)
    t.rotate(motion.roll * 0.9)
    t.rotate(tilt)
    t.shear(skew, 0.0)
    t.translate(-pw / 2, -ph / 2)
    return t, QRectF(0, 0, pw, ph)


class EyeAnimator:
    def __init__(self) -> None:
        self._pupil_x = 0.0
        self._pupil_y = 0.0
        self._target_x = 0.0
        self._target_y = 0.0
        self._blink = 1.0
        self._blink_phase = 0.0
        self._next_blink = time.time() + random.uniform(2.0, 4.0)
        self._wander_at = time.time() + 1.5
        self._look_at: tuple[float, float] | None = None

    def set_look_at(self, look: tuple[float, float] | None) -> None:
        self._look_at = look

    def tick(self, *, moving: bool = False) -> None:
        now = time.time()

        if self._blink_phase > 0:
            self._blink_phase = max(0.0, self._blink_phase - 0.22)
            t = self._blink_phase
            if t > 0.55:
                self._blink = 1.0 - (t - 0.55) / 0.45 * 0.95
            else:
                self._blink = t / 0.55 * 0.95
        else:
            self._blink = min(1.0, self._blink + 0.25)
            if now >= self._next_blink:
                self._blink_phase = 1.0
                self._next_blink = now + random.uniform(2.2, 5.0)

        if self._look_at is not None:
            self._target_x, self._target_y = self._look_at
        elif now >= self._wander_at:
            self._wander_at = now + random.uniform(1.2, 2.8)
            self._target_x = random.uniform(-0.55, 0.55)
            self._target_y = random.uniform(-0.2, 0.4)

        speed = 0.14 if moving else 0.08
        self._pupil_x += (self._target_x - self._pupil_x) * speed
        self._pupil_y += (self._target_y - self._pupil_y) * speed

    def draw(
        self,
        p: QPainter,
        body: QRectF,
        *,
        face_left: bool = False,
    ) -> None:
        if body.width() < 8:
            return

        bw, bh = body.width(), body.height()
        eye_r = bw * 0.105
        pupil_r = eye_r * 0.42
        lid_h = eye_r * 2.2 * (1.0 - self._blink)

        px_off = self._pupil_x * eye_r * 0.38
        py_off = self._pupil_y * eye_r * 0.32
        if face_left:
            px_off *= -1

        for ex, ey in EYE_LAYOUT:
            cx = body.x() + bw * ex
            cy = body.y() + bh * ey

            p.setPen(Qt.PenStyle.NoPen)
            p.setBrush(QBrush(QColor(255, 255, 255, 245)))
            p.drawEllipse(int(cx - eye_r), int(cy - eye_r), int(eye_r * 2), int(eye_r * 2))

            if self._blink < 0.92:
                p.setBrush(QBrush(QColor(55, 20, 85, 250)))
                lid_top = int(cy - eye_r)
                p.drawRect(
                    int(cx - eye_r * 1.05),
                    lid_top,
                    int(eye_r * 2.1),
                    int(lid_h),
                )

            if self._blink > 0.08:
                p.setBrush(QBrush(QColor(20, 10, 35)))
                p.drawEllipse(
                    int(cx - pupil_r + px_off),
                    int(cy - pupil_r + py_off),
                    int(pupil_r * 2),
                    int(pupil_r * 2),
                )
                p.setBrush(QBrush(QColor(255, 255, 255, 230)))
                glint = pupil_r * 0.35
                p.drawEllipse(
                    int(cx - pupil_r * 0.3 + px_off),
                    int(cy - pupil_r * 0.5 + py_off),
                    int(glint),
                    int(glint),
                )
