from __future__ import annotations

from typing import TypedDict

from .dtc import DtcRegisterInfo, decode_dtc_register_info
from .rom import Rom
from .tables import VECTOR_NAMES_MIN


class DtcVectorEntry(TypedDict):
    vector_address: int
    source: str
    register_info_address: int
    target: int
    register_info: DtcRegisterInfo


DTC_VECTOR_NAMES_MIN: dict[int, str] = {
    0x00C0: "irq0",
    0x00C2: "interval_timer",
    0x00C8: "irq1",
    0x00D0: "irq2",
    0x00D2: "irq3",
    0x00D8: "irq4",
    0x00DA: "irq5",
    0x00E0: "frt1_ici",
    0x00E2: "frt1_ocia",
    0x00E4: "frt1_ocib",
    0x00E8: "frt2_ici",
    0x00EA: "frt2_ocia",
    0x00EC: "frt2_ocib",
    0x00F0: "frt3_ici",
    0x00F2: "frt3_ocia",
    0x00F4: "frt3_ocib",
    0x00F8: "tmr_cmia",
    0x00FA: "tmr_cmib",
    0x00A2: "sci1_rxi",
    0x00A4: "sci1_txi",
    0x00AA: "sci2_rxi",
    0x00AC: "sci2_txi",
    0x00B0: "ad_adi",
}

DTC_VECTOR_NAMES_MAX: dict[int, str] = {
    min_addr * 2: name for min_addr, name in DTC_VECTOR_NAMES_MIN.items()
}


def read_vectors_min(rom: Rom) -> dict[int, tuple[str, int]]:
    vectors: dict[int, tuple[str, int]] = {}
    for addr in range(0x0000, 0x009A, 2):
        if not rom.contains(addr, 2):
            break
        target = rom.u16(addr)
        if target in (0x0000, 0xFFFF):
            continue
        name = VECTOR_NAMES_MIN.get(addr, f"vector_{addr:04X}")
        vectors[addr] = (name, target)
    return vectors


def read_vectors_max(rom: Rom) -> dict[int, tuple[str, int]]:
    vectors: dict[int, tuple[str, int]] = {}
    for addr in range(0x0000, 0x0134, 4):
        if not rom.contains(addr, 4):
            break
        page = rom.u8(addr + 1)
        pc = rom.u16(addr + 2)
        if pc in (0x0000, 0xFFFF) or page != 0:
            continue
        name = VECTOR_NAMES_MIN.get(addr // 2, f"vector_{addr:04X}")
        vectors[addr] = (name, pc)
    return vectors


def _dtc_entry(rom: Rom, vector_address: int, source: str, target: int) -> DtcVectorEntry:
    return {
        "vector_address": vector_address,
        "source": source,
        "register_info_address": target,
        "target": target,
        "register_info": decode_dtc_register_info(rom, target),
    }


def read_dtc_vectors_min(rom: Rom) -> dict[int, DtcVectorEntry]:
    vectors: dict[int, DtcVectorEntry] = {}
    for addr, source in sorted(DTC_VECTOR_NAMES_MIN.items()):
        if not rom.contains(addr, 2):
            continue
        target = rom.u16(addr)
        if target in (0x0000, 0xFFFF):
            continue
        vectors[addr] = _dtc_entry(rom, addr, source, target)
    return vectors


def read_dtc_vectors_max(rom: Rom) -> dict[int, DtcVectorEntry]:
    vectors: dict[int, DtcVectorEntry] = {}
    for addr, source in sorted(DTC_VECTOR_NAMES_MAX.items()):
        if not rom.contains(addr, 4):
            continue
        target = rom.u16(addr + 2)
        if target in (0x0000, 0xFFFF):
            continue
        vectors[addr] = _dtc_entry(rom, addr, source, target)
    return vectors
