import unittest

from h8536.cycles import annotate_cycles
from h8536.model import Instruction
from h8536.timing import cycle_bounds, summarize_timing


class TimingSummaryTest(unittest.TestCase):
    def test_cycle_bounds_use_branch_min_and_max(self):
        self.assertEqual(cycle_bounds({"not_taken": 3, "taken": 8}), (3, 8))
        self.assertEqual(cycle_bounds({"false": 3, "count_minus_1": 4, "taken": 8}), (3, 8))

    def test_summarizes_backward_scb_loop_candidate(self):
        instructions = {
            0x0100: Instruction(0x0100, b"\x58\x02\x00", "MOV:I.W", "#H'0200, R0"),
            0x0103: Instruction(0x0103, b"\x01\xB8\xFD", "SCB/F", "R0, loc_0103", kind="branch", targets=[0x0103]),
            0x0106: Instruction(0x0106, b"\x19", "RTS", kind="return", fallthrough=False),
        }
        annotate_cycles(instructions, "min")

        summary = summarize_timing(instructions, {0x0103: "loc_0103"})

        self.assertEqual(summary["loops"][0]["start"], 0x0103)
        self.assertEqual(summary["loops"][0]["kind"], "counter_delay_loop")
        self.assertEqual(summary["loops"][0]["cycles_min"], 3)
        self.assertEqual(summary["loops"][0]["cycles_max"], 9)

    def test_summarizes_straight_line_block_to_branch(self):
        instructions = {
            0x0200: Instruction(0x0200, b"\x58\x00\x01", "MOV:I.W", "#H'0001, R0"),
            0x0203: Instruction(0x0203, b"\x26\x02", "BNE", "loc_0207", kind="branch", targets=[0x0207]),
            0x0205: Instruction(0x0205, b"\x19", "RTS", kind="return", fallthrough=False),
            0x0207: Instruction(0x0207, b"\x19", "RTS", kind="return", fallthrough=False),
        }
        annotate_cycles(instructions, "min")

        summary = summarize_timing(instructions, {0x0207: "loc_0207"})

        first = summary["blocks"][0]
        self.assertEqual(first["start"], 0x0200)
        self.assertEqual(first["end"], 0x0203)
        self.assertEqual(first["cycles_min"], 6)
        self.assertEqual(first["cycles_max"], 11)


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