# Source code for evdev.events

    """
    This module provides the :class:`InputEvent` class, which closely
    resembles the ``input_event`` struct defined in ``linux/input.h``:

    .. code-block:: c

        struct input_event {
            struct timeval time;
            __u16 type;
            __u16 code;
            __s32 value;
        };

    This module also defines several :class:`InputEvent` sub-classes that
    know more about the different types of events (key, abs, rel etc). The
    :data:`event_factory` dictionary maps event types to these classes.

    Assuming you use the :func:`evdev.util.categorize()` function to
    categorize events according to their type, adding or replacing a class
    for a specific event type becomes a matter of modifying
    :data:`event_factory`.

    All classes in this module have reasonable ``str()`` and ``repr()``
    methods::

        >>> print(event)
        event at 1337197425.477827, code 04, type 04, val 458792
        >>> print(repr(event))
        InputEvent(1337197425L, 477827L, 4, 4, 458792L)

        >>> print(key_event)
        key event at 1337197425.477835, 28 (KEY_ENTER), up
        >>> print(repr(key_event))
        KeyEvent(InputEvent(1337197425L, 477835L, 1, 28, 0L))
    """

    # event type descriptions have been taken mot-a-mot from:
    # http://www.kernel.org/doc/Documentation/input/event-codes.txt

    # pylint: disable=no-name-in-module
    from typing import Final
    from .ecodes import ABS, EV_ABS, EV_KEY, EV_REL, EV_SYN, KEY, REL, SYN, keys

    class InputEvent:
        """A generic input event."""

        __slots__ = "sec", "usec", "type", "code", "value"

        def __init__(self, sec, usec, type, code, value):
            #: Time in seconds since epoch at which event occurred.
            self.sec: int = sec

            #: Microsecond portion of the timestamp.
            self.usec: int = usec

            #: Event type - one of ``ecodes.EV_*``.
            self.type: int = type

            #: Event code related to the event type.
            self.code: int = code

            #: Event value related to the event type.
            self.value: int = value

        def timestamp(self) -> float:
            """Return event timestamp as a float."""
            return self.sec + (self.usec / 1000000.0)

        def __str__(self):
            msg = "event at {:f}, code {:02d}, type {:02d}, val {:02d}"
            return msg.format(self.timestamp(), self.code, self.type, self.value)

        def __repr__(self):
            msg = "{}({!r}, {!r}, {!r}, {!r}, {!r})"
            return msg.format(self.__class__.__name__, self.sec, self.usec, self.type, self.code, self.value)

    class KeyEvent:
        """An event generated by a keyboard, button or other key-like devices."""

        key_up: Final[int] = 0x0
        key_down: Final[int] = 0x1
        key_hold: Final[int] = 0x2

        __slots__ = "scancode", "keycode", "keystate", "event"

        def __init__(self, event: InputEvent, allow_unknown: bool = False):
            """
            The ``allow_unknown`` argument determines what to do in the event of an event code
            for which a key code cannot be found. If ``False`` a ``KeyError`` will be raised.
            If ``True`` the keycode will be set to the hex value of the event code.
            """

            self.scancode: int = event.code

            if event.value == 0:
                self.keystate = KeyEvent.key_up
            elif event.value == 2:
                self.keystate = KeyEvent.key_hold
            elif event.value == 1:
                self.keystate = KeyEvent.key_down

            try:
                self.keycode = keys[event.code]
            except KeyError:
                if allow_unknown:
                    self.keycode = "0x{:02X}".format(event.code)
                else:
                    raise

            #: Reference to an :class:`InputEvent` instance.
            self.event: InputEvent = event

        def __str__(self):
            try:
                ks = ("up", "down", "hold")[self.keystate]
            except IndexError:
                ks = "unknown"

            msg = "key event at {:f}, {} ({}), {}"
            return msg.format(self.event.timestamp(), self.scancode, self.keycode, ks)

        def __repr__(self):
            return "{}({!r})".format(self.__class__.__name__, self.event)

    class RelEvent:
        """A relative axis event (e.g moving the mouse 5 units to the left)."""

        __slots__ = "event"

        def __init__(self, event: InputEvent):
            #: Reference to an :class:`InputEvent` instance.
            self.event: InputEvent = event

        def __str__(self):
            msg = "relative axis event at {:f}, {}"
            return msg.format(self.event.timestamp(), REL[self.event.code])

        def __repr__(self):
            return "{}({!r})".format(self.__class__.__name__, self.event)

    class AbsEvent:
        """An absolute axis event (e.g the coordinates of a tap on a touchscreen)."""

        __slots__ = "event"

        def __init__(self, event: InputEvent):
            #: Reference to an :class:`InputEvent` instance.
            self.event: InputEvent = event

        def __str__(self):
            msg = "absolute axis event at {:f}, {}"
            return msg.format(self.event.timestamp(), ABS[self.event.code])

        def __repr__(self):
            return "{}({!r})".format(self.__class__.__name__, self.event)

    class SynEvent:
        """
        A synchronization event. Used as markers to separate events. Events may be
        separated in time or in space, such as with the multitouch protocol.
        """

        __slots__ = "event"

        def __init__(self, event: InputEvent):
            #: Reference to an :class:`InputEvent` instance.
            self.event: InputEvent = event

        def __str__(self):
            msg = "synchronization event at {:f}, {}"
            return msg.format(self.event.timestamp(), SYN[self.event.code])

        def __repr__(self):
            return "{}({!r})".format(self.__class__.__name__, self.event)

    #: A mapping of event types to :class:`InputEvent` sub-classes. Used
    #: by :func:`evdev.util.categorize()`
    event_factory = {
        EV_KEY: KeyEvent,
        EV_REL: RelEvent,
        EV_ABS: AbsEvent,
        EV_SYN: SynEvent,
    }

    __all__ = ("InputEvent", "KeyEvent", "RelEvent", "SynEvent", "AbsEvent", "event_factory")