import unittest

from h8536.emulator import (
    HEARTBEAT_FRAME,
    SCI1_SCR,
    SCI1_SSR,
    SCI1_TDR,
    SCI_SCR_TE,
    SCI_SSR_FER,
    SCI_SSR_ORER,
    SCI_SSR_PER,
    SCI_SSR_RDRF,
    SCI_SSR_TDRE,
    SCI1,
    UartTiming,
)


class SciTimingTest(unittest.TestCase):
    def test_ssr_write_one_does_not_set_hardware_flags(self):
        sci = SCI1()
        sci.ssr = 0x00

        sci.write(SCI1_SSR, SCI_SSR_TDRE | SCI_SSR_RDRF | SCI_SSR_ORER | SCI_SSR_FER | SCI_SSR_PER)

        self.assertEqual(sci.read(SCI1_SSR) & 0xF8, 0x00)

    def test_ssr_write_zero_clears_selected_writable_flags(self):
        sci = SCI1()
        sci.ssr = SCI_SSR_TDRE | SCI_SSR_RDRF | SCI_SSR_ORER | SCI_SSR_FER | SCI_SSR_PER | 0x07

        sci.write(SCI1_SSR, sci.ssr & ~SCI_SSR_RDRF)

        self.assertEqual(sci.read(SCI1_SSR) & SCI_SSR_RDRF, 0x00)
        self.assertEqual(
            sci.read(SCI1_SSR) & (SCI_SSR_TDRE | SCI_SSR_ORER | SCI_SSR_FER | SCI_SSR_PER),
            SCI_SSR_TDRE | SCI_SSR_ORER | SCI_SSR_FER | SCI_SSR_PER,
        )

    def test_tdr_write_then_ssr_clear_delays_tdre_until_ticks(self):
        sci = SCI1(tx_ready_ticks=2)
        sci.write(SCI1_SCR, sci.read(SCI1_SCR) | SCI_SCR_TE)
        self.assertTrue(sci.read(SCI1_SSR) & SCI_SSR_TDRE)

        sci.write(SCI1_TDR, 0x42)
        sci.write(SCI1_SSR, sci.read(SCI1_SSR) & ~SCI_SSR_TDRE)
        self.assertFalse(sci.read(SCI1_SSR) & SCI_SSR_TDRE)

        sci.tick()
        self.assertFalse(sci.read(SCI1_SSR) & SCI_SSR_TDRE)

        sci.tick()
        self.assertTrue(sci.read(SCI1_SSR) & SCI_SSR_TDRE)

    def test_transmit_capture_requires_te_enabled(self):
        sci = SCI1()

        sci.write(SCI1_TDR, 0x33)
        self.assertEqual(sci.tx_bytes, [])
        self.assertFalse(sci.tx_events[-1].emitted)

        sci.write(SCI1_SCR, sci.read(SCI1_SCR) | SCI_SCR_TE)
        for byte in HEARTBEAT_FRAME:
            sci.write(SCI1_TDR, byte)

        self.assertEqual(bytes(sci.tx_bytes), HEARTBEAT_FRAME)
        self.assertEqual(sci.tx_frames, [HEARTBEAT_FRAME])
        self.assertTrue(sci.saw_heartbeat())

    def test_inject_rx_sets_overrun_if_rdrf_is_still_full(self):
        sci = SCI1()

        sci.inject_rx(0x11)
        sci.inject_rx(0x22)

        self.assertEqual(sci.read(SCI1_SSR) & (SCI_SSR_RDRF | SCI_SSR_ORER), SCI_SSR_RDRF | SCI_SSR_ORER)
        self.assertEqual(sci.rdr, 0x11)

    def test_uart_8n1_38400_byte_timing(self):
        timing = UartTiming(baud=38_400)

        self.assertEqual(timing.bits_per_character, 10)
        self.assertAlmostEqual(timing.micros_per_character(), 260.416666, places=3)
        self.assertEqual(timing.cycles_per_character(10_000_000), 2604)

    def test_uart_8e1_38400_byte_timing_matches_bench_link(self):
        timing = UartTiming.from_format("8E1", baud=38_400)

        self.assertEqual(timing.bits_per_character, 11)
        self.assertAlmostEqual(timing.micros_per_character(), 286.458333, places=3)
        self.assertEqual(timing.cycles_per_character(10_000_000), 2865)
        self.assertEqual(timing.summary(10_000_000).split()[0], "uart_8E1")

    def test_uart_timing_can_be_derived_from_sci_smr(self):
        timing = UartTiming.from_sci_smr(0x24, baud=38_400)

        self.assertEqual((timing.data_bits, timing.parity, timing.stop_bits), (8, "E", 1))

    def test_tdr_write_can_use_uart_character_time_for_tdre(self):
        sci = SCI1()
        sci.configure_tx_timing(UartTiming.from_format("8E1", baud=38_400), clock_hz=10_000_000)
        sci.write(SCI1_SCR, sci.read(SCI1_SCR) | SCI_SCR_TE)

        sci.write(SCI1_TDR, 0x42)
        sci.write(SCI1_SSR, sci.read(SCI1_SSR) & ~SCI_SSR_TDRE)

        self.assertTrue(sci.tx_busy())
        self.assertFalse(sci.read(SCI1_SSR) & SCI_SSR_TDRE)
        sci.tick(2864)
        self.assertFalse(sci.read(SCI1_SSR) & SCI_SSR_TDRE)
        sci.tick(1)
        self.assertTrue(sci.read(SCI1_SSR) & SCI_SSR_TDRE)
        self.assertFalse(sci.tx_busy())


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