from __future__ import annotations

import contextlib
import typing
from abc import ABCMeta, abstractmethod

from libqtile import hook
from libqtile.command.base import CommandObject, expose_command
from libqtile.config import Screen

if typing.TYPE_CHECKING:
    from typing import Any

    from libqtile import config
    from libqtile.backend.base import Internal
    from libqtile.backend.base.idle_inhibit import IdleInhibitorManager
    from libqtile.backend.base.idle_notify import IdleNotifier
    from libqtile.command.base import ItemT
    from libqtile.core.manager import Qtile
    from libqtile.group import _Group


class Core(CommandObject, metaclass=ABCMeta):
    painter: Any
    supports_restarting: bool = True
    qtile: Qtile
    idle_inhibitor_manager: IdleInhibitorManager[Any]
    idle_notifier: IdleNotifier

    @property
    @abstractmethod
    def name(self) -> str:
        """The name of the backend"""

    def _items(self, name: str) -> ItemT:
        return None

    def _select(self, name, sel):
        return None

    @abstractmethod
    def finalize(self):
        """Destructor/Clean up resources"""

    @property
    @abstractmethod
    def display_name(self) -> str:
        pass

    @abstractmethod
    def setup_listener(self) -> None:
        """Setup a listener for the given qtile instance"""

    @abstractmethod
    def remove_listener(self) -> None:
        """Setup a listener for the given qtile instance"""

    def update_desktops(self, groups: list[_Group], index: int) -> None:
        """Set the current desktops of the window manager"""

    @abstractmethod
    def get_output_info(self) -> list[config.Output]:
        """Get the output information"""

    @abstractmethod
    def grab_key(self, key: config.Key | config.KeyChord) -> tuple[int, int]:
        """Configure the backend to grab the key event"""

    @abstractmethod
    def ungrab_key(self, key: config.Key | config.KeyChord) -> tuple[int, int]:
        """Release the given key event"""

    @abstractmethod
    def ungrab_keys(self) -> None:
        """Release the grabbed key events"""

    @abstractmethod
    def grab_button(self, mouse: config.Mouse) -> int:
        """Configure the backend to grab the mouse event"""

    def ungrab_buttons(self) -> None:
        """Release the grabbed button events"""

    def grab_pointer(self) -> None:
        """Configure the backend to grab mouse events"""

    def ungrab_pointer(self) -> None:
        """Release grabbed pointer events"""

    def on_config_load(self, initial: bool) -> None:
        """
        Respond to config loading. `initial` will be `True` if Qtile just started.
        """

    def warp_pointer(self, x: int, y: int) -> None:
        """Warp the pointer to the given coordinates relative."""

    @contextlib.contextmanager
    def masked(self):
        """A context manager to suppress window events while operating on many windows."""
        yield

    def create_internal(self, x: int, y: int, width: int, height: int) -> Internal:
        """Create an internal window controlled by Qtile."""
        raise NotImplementedError  # Only error when called, not when instantiating class

    def flush(self) -> None:
        """If needed, flush the backend's event queue."""

    def simulate_keypress(self, modifiers: list[str], key: str) -> None:
        """Simulate a keypress with given modifiers"""

    def keysym_from_name(self, name: str) -> int:
        """Get the keysym for a key from its name"""
        raise NotImplementedError

    def get_mouse_position(self) -> tuple[int, int]:
        """Get mouse coordinates."""
        raise NotImplementedError

    @expose_command()
    def info(self) -> dict[str, Any]:
        """Get basic information about the running backend."""
        return {"backend": self.name, "display_name": self.display_name}

    def check_screen_fullscreen_background(self, screen: Screen | None = None) -> None:
        """Toggles fullscreen background if any window on the screen is fullscreen."""
        # Wayland only

    def update_backend_log_level(self) -> None:
        """Update the backend log level based on Qtile's log level."""
        # Wayland only

    @property
    def inhibited(self) -> bool:
        if not hasattr(self, "_inhibited"):
            self._inhibited = False

        return self._inhibited

    @inhibited.setter
    def inhibited(self, value: bool):
        if value != self.inhibited:
            self._inhibited = value
            hook.fire("idle_inhibitor_change", value)

    @expose_command()
    def set_idle_inhibitor(self) -> None:
        """Create a global idle inhibitor."""
        self.idle_inhibitor_manager.add_global_inhibitor()

    @expose_command()
    def remove_idle_inhibitor(self) -> None:
        """Remove global idle inhibitor."""
        self.idle_inhibitor_manager.remove_global_inhibitor()

    @expose_command()
    def get_idle_inhibitors(self, active_only: bool = False) -> list[str]:
        """Return list of inhibitors."""
        return [
            f"{inhibitor!r}"
            for inhibitor in self.idle_inhibitor_manager.inhibitors
            if not active_only or (active_only and inhibitor.check())
        ]
