# Urwid main loop code
#    Copyright (C) 2004-2012  Ian Ward
#    Copyright (C) 2008 Walter Mundt
#    Copyright (C) 2009 Andrew Psaltis
#
#    This library is free software; you can redistribute it and/or
#    modify it under the terms of the GNU Lesser General Public
#    License as published by the Free Software Foundation; either
#    version 2.1 of the License, or (at your option) any later version.
#
#    This library is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#    Lesser General Public License for more details.
#
#    You should have received a copy of the GNU Lesser General Public
#    License along with this library; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Urwid web site: https://urwid.org/

"""Abstract shared code for urwid EventLoop implementation."""

from __future__ import annotations

import abc
import signal
import typing
from collections.abc import Callable

if typing.TYPE_CHECKING:
    from types import FrameType

__all__ = ("ExitMainLoop", "EventLoop")


class ExitMainLoop(Exception):
    """
    When this exception is raised within a main loop the main loop
    will exit cleanly.
    """
    pass


class EventLoop(abc.ABC):
    """
    Abstract class representing an event loop to be used by :class:`MainLoop`.
    """

    @abc.abstractmethod
    def alarm(self, seconds: float | int, callback: Callable[[], typing.Any]) -> typing.Any:
        """
        Call callback() a given time from now.  No parameters are
        passed to callback.

        This method has no default implementation.

        Returns a handle that may be passed to remove_alarm()

        seconds -- floating point time to wait before calling callback
        callback -- function to call from event loop
        """

    @abc.abstractmethod
    def enter_idle(self, callback):
        """
        Add a callback for entering idle.

        This method has no default implementation.

        Returns a handle that may be passed to remove_idle()
        """

    @abc.abstractmethod
    def remove_alarm(self, handle) -> bool:
        """
        Remove an alarm.

        This method has no default implementation.

        Returns True if the alarm exists, False otherwise
        """

    @abc.abstractmethod
    def remove_enter_idle(self, handle) -> bool:
        """
        Remove an idle callback.

        This method has no default implementation.

        Returns True if the handle was removed.
        """

    @abc.abstractmethod
    def remove_watch_file(self, handle) -> bool:
        """
        Remove an input file.

        This method has no default implementation.

        Returns True if the input file exists, False otherwise
        """

    @abc.abstractmethod
    def run(self) -> None:
        """
        Start the event loop.  Exit the loop when any callback raises
        an exception.  If ExitMainLoop is raised, exit cleanly.

        This method has no default implementation.
        """

    @abc.abstractmethod
    def watch_file(self, fd: int, callback: Callable[[], typing.Any]):
        """
        Call callback() when fd has some data to read.  No parameters
        are passed to callback.

        This method has no default implementation.

        Returns a handle that may be passed to remove_watch_file()

        fd -- file descriptor to watch for input
        callback -- function to call when input is available
        """

    def set_signal_handler(
        self,
        signum: int,
        handler: Callable[[int, FrameType | None], typing.Any] | int | signal.Handlers,
    ) -> Callable[[int, FrameType | None], typing.Any] | int | signal.Handlers | None:
        """
        Sets the signal handler for signal signum.

        The default implementation of :meth:`set_signal_handler`
        is simply a proxy function that calls :func:`signal.signal()`
        and returns the resulting value.

        signum -- signal number
        handler -- function (taking signum as its single argument),
        or `signal.SIG_IGN`, or `signal.SIG_DFL`
        """
        return signal.signal(signum, handler)
