import argparse
import unittest

from h8536.emulator import H8536Emulator, SCI1_RDR, SCI1_SSR, SCI_SSR_ORER, SCI_SSR_RDRF
from h8536.emulator.rx_probe import RunContext, UartTiming, _inject_frame_uart_timed, frame_checksum, frame_checksum_ok, parse_frame


def rom_with_reset(*, reset: int = 0x1000, size: int = 0x1020) -> bytearray:
    rom = bytearray([0xFF] * size)
    rom[0:2] = reset.to_bytes(2, "big")
    rom[reset : reset + 2] = b"\x20\xFE"  # BRA self
    return rom


class EmulatorRxProbeTest(unittest.TestCase):
    def test_parse_frame_accepts_five_bytes_and_appends_checksum(self):
        frame = parse_frame("04 00 00 40 00")

        self.assertEqual(frame, bytes.fromhex("04000040001E"))
        self.assertTrue(frame_checksum_ok(frame))

    def test_parse_frame_accepts_compact_checked_frame(self):
        frame = parse_frame("0780684030C5")

        self.assertEqual(frame, bytes.fromhex("0780684030C5"))
        self.assertEqual(frame_checksum(frame), 0xC5)
        self.assertTrue(frame_checksum_ok(frame))

    def test_parse_frame_rejects_wrong_length(self):
        with self.assertRaises(argparse.ArgumentTypeError):
            parse_frame("04 00 00 40")

    def test_uart_timed_injection_does_not_wait_for_rdrf_consumption(self):
        emulator = H8536Emulator(bytes(rom_with_reset()), clock_hz=10_000_000)
        context = RunContext()

        steps, reason = _inject_frame_uart_timed(
            emulator,
            b"\x11\x22",
            timing=UartTiming(baud=38_400),
            max_steps_per_gap=1000,
            context=context,
        )

        self.assertEqual(reason, "frame_injected_uart_timing")
        self.assertGreater(steps, 0)
        self.assertEqual(emulator.memory.read8(SCI1_RDR), 0x11)
        self.assertEqual(emulator.memory.read8(SCI1_SSR) & (SCI_SSR_RDRF | SCI_SSR_ORER), SCI_SSR_RDRF | SCI_SSR_ORER)


if __name__ == "__main__":
    unittest.main()
