# SPDX-License-Identifier: Apache-2.0
"""The ERM disc (M1) on pin 18 through a PN2222A (wiring.md §5): plays display.HAPTIC patterns.

Each pattern alternates on/off milliseconds, starting with on. The motor is switched on and off, not PWM: it runs
from SW-RAIL (the cell's voltage or 4.4 V on USB) within its 2–5 V rating, and on/off is all the patterns need.
A new pattern replaces one still playing, so the newest state is the one felt. Nothing blocks: `tick(now)` from
the main loop sets the pin. How patterns feel on the real disc is backlog B-50.
"""
from __future__ import annotations

from dataclasses import dataclass, field

from .display import HAPTIC


def schedule(name: str, t: float) -> list[tuple[float, bool]]:
    """(time, on) switch points for one pattern starting at t, ending with the motor off."""
    out, on = [], True
    for ms in HAPTIC[name]:
        out.append((t, on))
        t += ms / 1000
        on = not on
    out.append((t, False))
    return out


@dataclass
class Haptic:
    motor: object                                   # hw.Output
    playing: str | None = None
    _steps: list = field(default_factory=list)

    def play(self, name: str | None, now: float) -> None:
        if not name:
            return
        if name not in HAPTIC:
            raise KeyError(f"no haptic pattern {name!r}")
        self.playing, self._steps = name, schedule(name, now)
        self.tick(now)

    def tick(self, now: float) -> None:
        state = None
        while self._steps and self._steps[0][0] <= now:
            state = self._steps.pop(0)[1]
        if state is not None:
            self.motor.write(state)
        if not self._steps:
            self.playing = None

    def stop(self) -> None:
        self._steps, self.playing = [], None
        self.motor.write(False)
