from typing import Any, Callable, Dict, Optional, Tuple, Union

from loguru import logger
from Xlib import X
from Xlib.display import Display
from Xlib.protocol.event import KeyPress

from s3wm_core.key_combination import KeyCombination

keycode_mapping: Dict[Tuple[int, int], KeyCombination] = {}


def kill_wm(wm: Any) -> None:
    """Close WM process.

    :param wm: an S3WM instance. (Used Any to avoid circular deps)
    """
    wm.display.close()
    exit(0)  # noqa: WPS421


def init_keymap(display: Display) -> None:
    """
    Sends requests to the X Server to listen for specific key events.

    When the key combination is pressed the application will get the event about it.

    :param display: Used to manipulate keysym to keycode transitions.
    """
    from s3wm_core.wm_config import combinations  # noqa: WPS433

    for button in {1, 3}:  # noqa: WPS335
        display.screen().root.grab_button(
            button=button,
            modifiers=X.Mod1Mask,
            owner_events=True,
            event_mask=X.ButtonPressMask,
            pointer_mode=X.GrabModeAsync,
            keyboard_mode=X.GrabModeAsync,
            confine_to=X.NONE,
            cursor=X.NONE,
        )

    # Registering all keybindings to X11.
    for combination in combinations:
        # Getting code for key on current keyboard.
        codes = {code for code, index in display.keysym_to_keycodes(combination.key)}

        for code in codes:
            logger.debug(combination)
            display.screen().root.grab_key(
                code,
                combination.modifiers,
                1,
                X.GrabModeAsync,
                X.GrabModeAsync,
            )
            keycode_mapping[(code, combination.modifiers)] = combination


def get_key_action(  # noqa: WPS234
    key_event: KeyPress,
) -> Optional[Union[Callable[..., Any], str]]:
    """
    Function to get the action defined in `combinations` by keypress event.

    :param key_event: event generated by X11.
    :return: Action associated with keypress event.
    """
    key = key_event.detail
    modifiers = key_event.state
    combination = keycode_mapping.get((key, modifiers))
    if combination:
        return combination.action
    return None
