from typing import Optional, Set

from flut._flut.engine import FlutImmutableObject, FlutRealtimeObject, FlutStaticObject
from flut._flut.runtime import _flut_unpack_optional_field, _flut_unpack_required_field
from flut.dart.core import Duration

from .keyboard_key import (
    LogicalKeyboardKey,
    PhysicalKeyboardKey,
)


class KeyEvent(FlutImmutableObject):

    def __init__(
        self,
        physicalKey: PhysicalKeyboardKey,
        logicalKey: LogicalKeyboardKey,
        timeStamp: Duration,
        character: Optional[str] = None,
        synthesized: bool = False,
    ):
        super().__init__("KeyEvent")
        self.physicalKey = physicalKey
        self.logicalKey = logicalKey
        self.timeStamp = timeStamp
        self.character = character
        self.synthesized = synthesized


class KeyDownEvent(KeyEvent):

    def __init__(
        self,
        physicalKey: PhysicalKeyboardKey,
        logicalKey: LogicalKeyboardKey,
        timeStamp: Duration,
        character: Optional[str] = None,
        synthesized: bool = False,
    ):
        super().__init__(physicalKey, logicalKey, timeStamp, character, synthesized)
        self.physicalKey = physicalKey
        self.logicalKey = logicalKey
        self.timeStamp = timeStamp
        self.character = character
        self.synthesized = synthesized

    @staticmethod
    def _flut_unpack(data: dict) -> "KeyDownEvent":
        return KeyDownEvent(
            physicalKey=_flut_unpack_required_field(data, "physicalKey"),
            logicalKey=_flut_unpack_required_field(data, "logicalKey"),
            timeStamp=_flut_unpack_required_field(data, "timeStamp"),
            character=_flut_unpack_optional_field(data, "character"),
            synthesized=_flut_unpack_required_field(data, "synthesized"),
        )


class KeyUpEvent(KeyEvent):

    def __init__(
        self,
        physicalKey: PhysicalKeyboardKey,
        logicalKey: LogicalKeyboardKey,
        timeStamp: Duration,
        character: Optional[str] = None,
        synthesized: bool = False,
    ):
        super().__init__(physicalKey, logicalKey, timeStamp, character, synthesized)
        self.physicalKey = physicalKey
        self.logicalKey = logicalKey
        self.timeStamp = timeStamp
        self.character = character
        self.synthesized = synthesized

    @staticmethod
    def _flut_unpack(data: dict) -> "KeyUpEvent":
        return KeyUpEvent(
            physicalKey=_flut_unpack_required_field(data, "physicalKey"),
            logicalKey=_flut_unpack_required_field(data, "logicalKey"),
            timeStamp=_flut_unpack_required_field(data, "timeStamp"),
            character=_flut_unpack_optional_field(data, "character"),
            synthesized=_flut_unpack_required_field(data, "synthesized"),
        )


class KeyRepeatEvent(KeyEvent):

    def __init__(
        self,
        physicalKey: PhysicalKeyboardKey,
        logicalKey: LogicalKeyboardKey,
        timeStamp: Duration,
        character: Optional[str] = None,
        synthesized: bool = False,
    ):
        super().__init__(physicalKey, logicalKey, timeStamp, character, synthesized)
        self.physicalKey = physicalKey
        self.logicalKey = logicalKey
        self.timeStamp = timeStamp
        self.character = character
        self.synthesized = synthesized

    @staticmethod
    def _flut_unpack(data: dict) -> "KeyRepeatEvent":
        return KeyRepeatEvent(
            physicalKey=_flut_unpack_required_field(data, "physicalKey"),
            logicalKey=_flut_unpack_required_field(data, "logicalKey"),
            timeStamp=_flut_unpack_required_field(data, "timeStamp"),
            character=_flut_unpack_optional_field(data, "character"),
            synthesized=_flut_unpack_required_field(data, "synthesized"),
        )


class HardwareKeyboard(FlutRealtimeObject, FlutStaticObject):

    def __init__(self, oid: int):
        super().__init__("HardwareKeyboard", oid=oid)

    @staticmethod
    def _flut_unpack(data: dict) -> "HardwareKeyboard":
        return HardwareKeyboard(oid=_flut_unpack_required_field(data, "_flut_oid"))

    @staticmethod
    def instance() -> "HardwareKeyboard":
        return FlutStaticObject._static_call("HardwareKeyboard", "instance")

    @property
    def physicalKeysPressed(self) -> Set[PhysicalKeyboardKey]:
        result = self._get("physicalKeysPressed")
        if result is None:
            return set()
        return {PhysicalKeyboardKey(usage) for usage in result}

    @property
    def logicalKeysPressed(self) -> Set[LogicalKeyboardKey]:
        result = self._get("logicalKeysPressed")
        if result is None:
            return set()
        return {LogicalKeyboardKey(kid) for kid in result}

    def isPhysicalKeyPressed(self, key: PhysicalKeyboardKey) -> bool:
        return bool(self._call("isPhysicalKeyPressed", usbHidUsage=key.usbHidUsage))

    def isLogicalKeyPressed(self, key: LogicalKeyboardKey) -> bool:
        return bool(self._call("isLogicalKeyPressed", keyId=key.keyId))

    @property
    def isShiftPressed(self) -> bool:
        return bool(self._get("isShiftPressed"))

    @property
    def isControlPressed(self) -> bool:
        return bool(self._get("isControlPressed"))

    @property
    def isAltPressed(self) -> bool:
        return bool(self._get("isAltPressed"))

    @property
    def isMetaPressed(self) -> bool:
        return bool(self._get("isMetaPressed"))
