import unittest

from h8536.emulator import MemoryMap, P9DDR, P9DR
from h8536.emulator.peripherals import P9Bus


def p9_start(bus: P9Bus) -> None:
    bus.write_ddr(0x93)
    bus.write_dr(0x80)
    bus.write_dr(0x82)
    bus.write_dr(0x02)
    bus.write_dr(0x00)


def p9_stop(bus: P9Bus) -> None:
    bus.write_ddr(0x93)
    bus.write_dr(0x00)
    bus.write_dr(0x02)
    bus.write_dr(0x82)
    bus.write_dr(0x80)


def p9_write_byte(bus: P9Bus, value: int) -> bool:
    bus.write_ddr(0x93)
    for bit_index in range(7, -1, -1):
        bit = (value >> bit_index) & 1
        low = 0x80 if bit else 0x00
        bus.write_dr(low)
        bus.write_dr(low | 0x02)
        bus.write_dr(low)
    bus.write_ddr(0x13)
    bus.write_dr(bus.dr_latch | 0x02)
    ack_low = not bool(bus.read_dr() & 0x80)
    bus.write_dr(bus.dr_latch & ~0x02)
    bus.write_ddr(0x93)
    return ack_low


def p9_read_byte(bus: P9Bus, *, master_ack: bool) -> int:
    value = 0
    bus.write_ddr(0x13)
    for _ in range(8):
        bus.write_dr(bus.dr_latch | 0x02)
        value = (value << 1) | (1 if bus.read_dr() & 0x80 else 0)
        bus.write_dr(bus.dr_latch & ~0x02)
    bus.write_ddr(0x93)
    if master_ack:
        bus.write_dr(bus.dr_latch & ~0x80)
    else:
        bus.write_dr(bus.dr_latch | 0x80)
    bus.write_dr(bus.dr_latch | 0x02)
    bus.write_dr(bus.dr_latch & ~0x02)
    return value


class P9BusTest(unittest.TestCase):
    def test_bit7_input_uses_queued_then_default_low_response(self):
        memory = MemoryMap(b"\x00" * 4)
        memory.write8(P9DDR, 0x13)
        memory.write8(P9DR, 0x80)
        memory.p9_bus.queue_input_bits([1])

        self.assertEqual(memory.read8(P9DDR), 0x13)
        self.assertEqual(memory.read8(P9DR) & 0x80, 0x80)
        self.assertEqual(memory.read8(P9DR) & 0x80, 0x00)
        self.assertEqual(memory.registers[P9DR - 0xFE80], 0x80)

    def test_bit7_output_reads_latch(self):
        memory = MemoryMap(b"\x00" * 4)
        memory.write8(P9DDR, 0x93)
        memory.write8(P9DR, 0x80)

        self.assertEqual(memory.read8(P9DDR), 0x93)
        self.assertEqual(memory.read8(P9DR) & 0x80, 0x80)
        self.assertEqual(memory.registers[P9DR - 0xFE80], 0x80)

    def test_strobe_rising_edges_capture_output_bits_and_byte_candidates(self):
        bus = P9Bus()
        bus.write_ddr(0x93)
        for bit in (1, 0, 1, 0, 0, 1, 0, 1):
            bus.write_dr(0x80 if bit else 0x00)
            bus.write_dr((0x80 if bit else 0x00) | 0x02)
            bus.write_dr(0x80 if bit else 0x00)

        self.assertEqual(bus.transmitted_bits, [1, 0, 1, 0, 0, 1, 0, 1])
        self.assertEqual(bus.byte_candidates, [0xA5])
        self.assertEqual([event.edge for event in bus.strobe_edges[:2]], ["rising", "falling"])
        self.assertIn("tx_byte ddr=93 dr=82 value=A5", bus.trace_lines())

    def test_wrapper_results_are_queued_then_default_timeout(self):
        bus = P9Bus()
        bus.queue_wrapper_results([True], source="panel-script")

        self.assertEqual(bus.consume_wrapper_result(), (True, "panel-script", 0))
        self.assertEqual(bus.consume_wrapper_result(), (False, "default_timeout", 0))
        self.assertIn("wrapper_result ddr=00 dr=00 value=01 success=1 source=panel-script queued=0", bus.trace_lines())
        self.assertIn("wrapper_result ddr=00 dr=00 value=00 success=0 source=default_timeout queued=0", bus.trace_lines())

    def test_x24164_bit_banged_write_acknowledges_and_stores_data(self):
        bus = P9Bus()

        p9_start(bus)
        self.assertTrue(p9_write_byte(bus, 0xA0))
        self.assertTrue(p9_write_byte(bus, 0x12))
        self.assertTrue(p9_write_byte(bus, 0x34))
        p9_stop(bus)

        device = bus.x24164_bus.devices[0]
        self.assertEqual(device.read(0x12), 0x34)
        self.assertTrue(any("x24164_write_data" in line and "addr=012" in line for line in bus.trace_lines()))

    def test_x24164_bit_banged_random_read_uses_p91_p97_lines(self):
        bus = P9Bus()
        bus.x24164_bus.devices[0].write(0x12, 0xAB)

        p9_start(bus)
        self.assertTrue(p9_write_byte(bus, 0xA0))
        self.assertTrue(p9_write_byte(bus, 0x12))
        p9_start(bus)
        self.assertTrue(p9_write_byte(bus, 0xA1))
        self.assertEqual(p9_read_byte(bus, master_ack=False), 0xAB)
        p9_stop(bus)

        self.assertTrue(any("x24164_prepare_read" in line and "value=AB" in line for line in bus.trace_lines()))

    def test_x24164_fast_word_mapping_matches_rom_address_banks(self):
        bus = P9Bus()

        self.assertTrue(bus.fast_write_word(0x0012, 0x3456))
        self.assertTrue(bus.fast_write_word(0x0812, 0xABCD))

        self.assertEqual(bus.fast_read_word(0x0012), (True, 0x3456))
        self.assertEqual(bus.fast_read_word(0x0812), (True, 0xABCD))
        self.assertEqual(bus.x24164_bus.devices[0].read(0x12), 0x34)
        self.assertEqual(bus.x24164_bus.devices[1].read(0x12), 0xAB)


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