class Timers:

    def __init__(self, scheduler: Scheduler, interrupt_controller: InterruptController):
        self.timer_3 = Timer(scheduler, interrupt_controller, Interrupt.TIMER_3, None)
        self.timer_2 = Timer(scheduler, interrupt_controller, Interrupt.TIMER_2, self.timer_3)
        self.timer_1 = Timer(scheduler, interrupt_controller, Interrupt.TIMER_1, self.timer_2)
        self.timer_0 = Timer(scheduler, interrupt_controller, Interrupt.TIMER_0, self.timer_1)

class Timer:

    def __init__(self, scheduler: Scheduler, interrupt_controller: InterruptController, interrupt: Interrupt, next_timer: Timer | None):
        self.WRITE_TIMER_REGISTER_DELAY = 1
        self.START_TIMER_DELAY = 1
        self.FREQUENCIES = [1, 64, 256, 1024]
        self.scheduler = scheduler
        self.interrupt_controller = interrupt_controller
        self.interrupt = interrupt
        self.next_timer = next_timer
        self._counter = 0
        self._reload_value = 0
        self._control_reg = 0
        self._pending_reload_value = 0
        self._pending_control_reg = 0
        self._overflow_event: Event | None = None

    def get_counter(self) -> int:
        if self.get_timer_enabled() and (not self.get_count_up()):
            self._update_counter()
        return self._counter

    def set_counter(self, value: int):
        self._pending_reload_value = value
        self.scheduler.schedule(self._write_reload_value, self.WRITE_TIMER_REGISTER_DELAY, EventTrigger.IMMEDIATELY)

    def get_control_reg(self) -> int:
        return self._control_reg

    def set_control_reg(self, value: int):
        self._pending_control_reg = value
        self.scheduler.schedule(self._write_control_reg, self.WRITE_TIMER_REGISTER_DELAY, EventTrigger.IMMEDIATELY)

    def get_frequency(self) -> int:
        return self.FREQUENCIES[get_bits(self._control_reg, 0, 1)]

    def get_count_up(self) -> bint:
        return get_bit(self._control_reg, 2)

    def set_count_up(self, value: bint):
        set_bit(self._control_reg, 2, value)

    def get_irq_enabled(self) -> bint:
        return get_bit(self._control_reg, 6)

    def get_timer_enabled(self) -> bint:
        return get_bit(self._control_reg, 7)

    def _write_reload_value(self):
        self._reload_value = self._pending_reload_value

    def _write_control_reg(self):
        old_count_up = self.get_count_up()
        old_timer_enabled = self.get_timer_enabled()
        self._control_reg = self._pending_control_reg
        new_count_up = self.get_count_up()
        new_timer_enabled = self.get_timer_enabled()
        if new_timer_enabled and (not old_timer_enabled):
            self._start()
        elif not new_timer_enabled and old_timer_enabled:
            self._stop()
        elif new_count_up and (not old_count_up):
            self._stop()
        elif not new_count_up and old_count_up:
            self._start()

    def _start(self):
        if not self.get_count_up():
            self._counter = self._reload_value
            self._schedule_overflow_event(self.START_TIMER_DELAY)
        elif self._overflow_event:
            self._schedule_overflow_event(0)

    def _stop(self):
        self._update_counter()
        if self._overflow_event:
            self._overflow_event.cancelled = True
            self._overflow_event = None

    def _update_counter(self):
        if self._overflow_event:
            elapsed = self.scheduler.cycles - self._overflow_event.time & 65535
            self._counter = elapsed // self.get_frequency()

    def _schedule_overflow_event(self, start_delay: int):
        delay = (65536 - self._counter) * self.get_frequency() + start_delay
        if self._overflow_event:
            self._overflow_event.cancelled = True
            self._overflow_event = None
        self._overflow_event = self.scheduler.schedule(self._overflow, delay, EventTrigger.IMMEDIATELY)

    def _overflow(self):
        self._counter = self._reload_value
        if not self.get_count_up():
            self._schedule_overflow_event(0)
        elif self._overflow_event:
            self._overflow_event.cancelled = True
            self._overflow_event = None
        if self.get_irq_enabled():
            self.interrupt_controller.signal(self.interrupt)
        if self.next_timer and self.next_timer.get_count_up() and self.next_timer.get_timer_enabled():
            self.next_timer._counter += 1
            if self.next_timer._counter == 65536:
                self.next_timer._overflow()