# Source code for moderngl_window.utils.scheduler

    import sched
    import time
    from typing import Any, Callable

    from moderngl_window.timers.base import BaseTimer

    class Scheduler:
        def __init__(self, timer: BaseTimer):
            """Create a Scheduler object to handle events.

            Args:
                timer (BaseTimer): timer to use, subclass of BaseTimer.

            Raises:
                ValueError: timer is not a valid argument.
            """
            if not isinstance(timer, BaseTimer):
                raise ValueError(
                    "timer, {}, has to be a instance of BaseTimer or a callable!".format(timer)
                )

            self._events: dict[int, sched.Event] = dict()
            self._event_id = 0

            self._scheduler = sched.scheduler(lambda: timer.time, time.sleep)

        def run_once(
            self,
            action: Callable[[Any], Any],
            delay: float,
            *,
            priority: int = 1,
            arguments: tuple[Any, ...] = (),
            kwargs: dict[Any, Any] = dict(),
        ) -> int:
            """Schedule a function for execution after a delay.

            Args:
                action (callable):
                    function to be called.
                delay (float):
                    delay in seconds.
                priority (int, optional):
                    priority for this event, lower is more important. Defaults to 1.
                arguments (tuple, optional):
                    arguments for the action. Defaults to ().
                kwargs (dict, optional):
                    keyword arguments for the action. Defaults to dict().

            Returns:
                int: event id that can be canceled.
            """
            event = self._scheduler.enter(delay, priority, action, arguments, kwargs)
            self._events[self._event_id] = event
            self._event_id += 1
            return self._event_id - 1

        def run_at(
            self,
            action: Callable[[Any], Any],
            time: float,
            *,
            priority: int = 1,
            arguments: tuple[Any, ...] = (),
            kwargs: dict[Any, Any] = dict(),
        ) -> int:
            """Schedule a function to be executed at a certain time.

            Args:
                action (callable):
                    function to be called.
                time (float):
                    epoch time at which the function should be called.
                priority (int, optional):
                    priority for this event, lower is more important. Defaults to 1.
                arguments (tuple, optional):
                    arguments for the action. Defaults to ().
                kwargs (dict, optional):
                    keyword arguments for the action. Defaults to dict().

            Returns:
                int: event id that can be canceled.
            """
            event = self._scheduler.enterabs(time, priority, action, arguments, kwargs)
            self._events[self._event_id] = event
            self._event_id += 1
            return self._event_id - 1

        def run_every(
            self,
            action: Callable[[Any], Any],
            delay: float,
            *,
            priority: int = 1,
            initial_delay: float = 0.0,
            arguments: tuple[Any, ...] = (),
            kwargs: dict[Any, Any] = dict(),
        ) -> int:
            """Schedule a recurring function to be called every `delay` seconds after a initial delay.

            Args:
                action (callable):
                    function to be called.
                delay (float):
                    delay in seconds.
                priority (int, optional):
                    priority for this event, lower is more important. Defaults to 1.
                initial_delay (float, optional):
                    initial delay in seconds before executing for the first time.
                Defaults to 0. arguments (tuple, optional):
                    arguments for the action. Defaults to ().
                kwargs (dict, optional):
                    keyword arguments for the action. Defaults to dict().

            Returns:
                int: event id that can be canceled.
            """
            recurring_event = self._recurring_event_factory(
                action, arguments, kwargs, (delay, priority), self._event_id
            )
            event = self._scheduler.enter(initial_delay, priority, recurring_event)
            self._events[self._event_id] = event
            self._event_id += 1
            return self._event_id - 1

        def _recurring_event_factory(
            self,
            function: Callable[[Any], Any],
            arguments: tuple[Any, ...],
            kwargs: dict[Any, Any],
            scheduling_info: tuple[Any, Any],
            id: int,
        ) -> Callable[[], None]:
            """Factory for creating recurring events that will reschedule themselves.

            Args:
                function (callable): function to be called.
                arguments (tuple): arguments for the function.
                kwargs (dict): keyword arguments for the function.
                scheduling_info (tuple): tuple of information for scheduling the task.
                id (int): event id this event should be assigned to.
            """

            def _f() -> None:
                function(*arguments, **kwargs)
                event = self._scheduler.enter(*scheduling_info, _f)
                self._events[id] = event

            return _f

        def execute(self) -> None:
            """Run the scheduler without blocking and execute any expired events."""
            self._scheduler.run(blocking=False)

        def cancel(self, event_id: int, delay: float = 0) -> None:
            """Cancel a previously scheduled event.

            Args:
                event_id (int): event to be canceled
                delay (float, optional): delay before canceling the event. Defaults to 0.
            """
            if delay == 0:
                self._cancel(event_id)
            else:
                self.run_once(self._cancel, delay, priority=0, arguments=(event_id,))

        def _cancel(self, event_id: int) -> None:
            if event_id not in self._events:
                raise ValueError("Recurring event with id {} does not exist".format(event_id))
            event = self._events.pop(event_id)
            self._scheduler.cancel(event)