import json
import tempfile
import unittest
from pathlib import Path

from h8536.render import format_listing, write_json
from h8536.rom import Rom
from h8536.vectors import read_dtc_vectors_min


def _manual_sci1_rxi_rom() -> Rom:
    data = bytearray([0xFF] * 0xFB88)
    data[0x00A2:0x00A4] = bytes([0xFB, 0x80])
    data[0xFB80:0xFB88] = bytes(
        [
            0x20,
            0x00,  # DTMR: byte transfer, destination increments
            0xFE,
            0xDD,  # DTSR: SCI1_RDR
            0xFC,
            0x00,  # DTDR: receive buffer
            0x00,
            0x80,  # DTCR: 128 transfers
        ],
    )
    return Rom(bytes(data))


class DtcDecodeTest(unittest.TestCase):
    def test_decodes_manual_sci1_receive_register_information(self):
        vectors = read_dtc_vectors_min(_manual_sci1_rxi_rom())
        entry = vectors[0x00A2]
        info = entry["register_info"]

        self.assertTrue(info["valid"])
        self.assertEqual(entry["source"], "sci1_rxi")
        self.assertEqual(info["dtmr"], 0x2000)
        self.assertEqual(info["mode"]["size"], "byte")
        self.assertFalse(info["mode"]["source_increment"])
        self.assertTrue(info["mode"]["destination_increment"])
        self.assertEqual(info["source"]["address"], 0xFEDD)
        self.assertEqual(info["source"]["name"], "SCI1_RDR")
        self.assertEqual(info["destination"]["address"], 0xFC00)
        self.assertEqual(info["destination"]["region"], "on_chip_ram")
        self.assertEqual(info["count"]["transfers"], 128)
        self.assertEqual(info["count"]["bytes"], 128)

    def test_invalid_register_information_pointer_is_reported_conservatively(self):
        data = bytearray([0xFF] * 0x0200)
        data[0x00A2:0x00A4] = bytes([0xFB, 0x80])

        vectors = read_dtc_vectors_min(Rom(bytes(data)))
        info = vectors[0x00A2]["register_info"]

        self.assertFalse(info["valid"])
        self.assertIn("outside ROM image", info["error"])

    def test_zero_count_represents_65536_transfers(self):
        data = bytearray([0xFF] * 0x0200)
        data[0x00C0:0x00C2] = bytes([0x01, 0x00])
        data[0x0100:0x0108] = bytes([0xC0, 0x00, 0x01, 0x20, 0xFE, 0x80, 0x00, 0x00])

        info = read_dtc_vectors_min(Rom(bytes(data)))[0x00C0]["register_info"]

        self.assertEqual(info["mode"]["size"], "word")
        self.assertEqual(info["mode"]["source_increment_step"], 2)
        self.assertEqual(info["count"]["transfers"], 65536)
        self.assertEqual(info["count"]["bytes"], 131072)
        self.assertTrue(info["count"]["zero_means_65536"])

    def test_listing_and_json_include_decoded_register_information(self):
        rom = _manual_sci1_rxi_rom()
        dtc_vectors = read_dtc_vectors_min(rom)

        listing = format_listing(
            Path("rom.bin"),
            rom,
            {},
            {},
            {},
            "min",
            traced=True,
            dtc_vectors=dtc_vectors,
        )

        self.assertIn("; DTC Register Information", listing)
        self.assertIn("sci1_rxi", listing)
        self.assertIn("byte x128", listing)
        self.assertIn("SCI1_RDR (H'FEDD) -> H'FC00", listing)

        with tempfile.TemporaryDirectory() as tmpdir:
            path = Path(tmpdir) / "out.json"
            write_json(path, {}, {}, {}, dtc_vectors=dtc_vectors)
            payload = json.loads(path.read_text(encoding="utf-8"))

        json_info = payload["dtc_vectors"][0]["register_info"]
        self.assertEqual(json_info["mode"]["size"], "byte")
        self.assertEqual(json_info["source"]["name"], "SCI1_RDR")
        self.assertEqual(json_info["count"]["transfers"], 128)


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