from __future__ import annotations

from dataclasses import dataclass

from .constants import FRT_TCR_OCIEA, FRT_TCSR_CCLRA, FRT_TCSR_OCFA
from .memory import MemoryMap


FRT_INTERNAL_PRESCALERS = {
    0b00: 4,
    0b01: 8,
    0b10: 32,
}


@dataclass(frozen=True)
class FrtRegisters:
    name: str
    tcr: int
    tcsr: int
    frc_h: int
    ocra_h: int


@dataclass
class FrtOciaScheduler:
    registers: FrtRegisters
    cycle_accumulator: int = 0
    last_config: tuple[int, int] | None = None
    compare_matches: int = 0

    def tick(self, memory: MemoryMap, cycle_delta: int) -> None:
        cycle_delta = max(0, cycle_delta)
        tcr = memory.register8(self.registers.tcr)
        prescaler = frt_internal_prescaler(tcr)
        if prescaler is None:
            self.cycle_accumulator = 0
            self.last_config = None
            return

        config = self._config(memory)
        if config != self.last_config:
            self.cycle_accumulator = 0
            self.last_config = config

        self.cycle_accumulator += cycle_delta
        ticks = self.cycle_accumulator // prescaler
        self.cycle_accumulator %= prescaler
        if ticks <= 0:
            return

        frc = memory.register16(self.registers.frc_h)
        ocra = memory.register16(self.registers.ocra_h)
        tcsr = memory.register8(self.registers.tcsr)
        clear_on_a = bool(tcsr & FRT_TCSR_CCLRA)
        matched = False

        for _ in range(ticks):
            frc = (frc + 1) & 0xFFFF
            if frc == ocra:
                matched = True
                self.compare_matches += 1
                if clear_on_a:
                    frc = 0

        memory.set_register16(self.registers.frc_h, frc)
        if matched:
            memory.set_register8(self.registers.tcsr, tcsr | FRT_TCSR_OCFA)

    def period_cycles(self, memory: MemoryMap) -> int | None:
        tcr = memory.register8(self.registers.tcr)
        prescaler = frt_internal_prescaler(tcr)
        if prescaler is None:
            return None
        ocra = memory.register16(self.registers.ocra_h)
        return frt_ocia_period_cycles(tcr, ocra)

    def pending(self, memory: MemoryMap) -> bool:
        return bool(memory.register8(self.registers.tcr) & FRT_TCR_OCIEA and memory.register8(self.registers.tcsr) & FRT_TCSR_OCFA)

    def period_ms(self, memory: MemoryMap, clock_hz: int) -> float | None:
        period = self.period_cycles(memory)
        if period is None or clock_hz <= 0:
            return None
        return 1000.0 * period / clock_hz

    def _config(self, memory: MemoryMap) -> tuple[int, int]:
        return memory.register8(self.registers.tcr) & 0x03, memory.register16(self.registers.ocra_h)


def frt_internal_prescaler(tcr: int) -> int | None:
    return FRT_INTERNAL_PRESCALERS.get(tcr & 0x03)


def frt_ocia_period_cycles(tcr: int, ocra: int) -> int | None:
    prescaler = frt_internal_prescaler(tcr)
    if prescaler is None:
        return None
    compare_ticks = (ocra & 0xFFFF) or 0x10000
    return compare_ticks * prescaler


__all__ = [
    "FRT_INTERNAL_PRESCALERS",
    "FrtOciaScheduler",
    "FrtRegisters",
    "frt_internal_prescaler",
    "frt_ocia_period_cycles",
]
