from collections.abc import Callable
from contextvars import Context
from threading import Event, Thread
from typing import Any

type Number = float | int
type Callback = Callable[..., Any]


class Ticker(Thread):
    """Run a function at a regular interval or manually."""

    def __init__(
        self,
        tick: Callback | None = None,
        /,
        *,
        interval: Number | None = None,
        name: str | None = None,
        daemon: bool | None = None,
        context: Context | None = None,
    ) -> None:
        super().__init__(name=name, daemon=daemon, context=context)
        self.interval = interval
        self._tick = tick
        self._next = Event()
        self._done = Event()

    def tick(self) -> None:
        if self._tick:
            self._tick()

    def next(self) -> None:
        self._next.set()

    def run(self) -> None:
        while not self._done.is_set():
            self._next.wait(timeout=self.interval)
            self._next.clear()
            if self._done.is_set():
                break
            self.tick()

    def stop(self) -> None:
        self._done.set()
        self._next.set()
