from typing import Optional, override
from flut._flut.engine import FlutImmutableObject
from flut._flut.runtime import _flut_unpack_required_field


class LogicalKeyboardKey(FlutImmutableObject):

    def __init__(self, keyId: int):
        super().__init__("LogicalKeyboardKey")
        self.keyId = keyId

    @staticmethod
    def _flut_unpack(data: dict) -> "LogicalKeyboardKey":
        key_id = _flut_unpack_required_field(data, "keyId")
        if key_id in _knownLogicalKeys:
            return _knownLogicalKeys[key_id]
        return LogicalKeyboardKey(key_id)

    @override
    def _flut_pack(self) -> dict:
        result = self._flut_base_props()
        result["keyId"] = self.keyId
        return result

    @property
    def keyLabel(self) -> str:
        if self.keyId < 0x10000 and self.keyId >= 0x20:
            return chr(self.keyId)
        return _keyLabels.get(self.keyId, "")

    @property
    def debugName(self) -> Optional[str]:
        return _keyLabels.get(self.keyId)

    def __eq__(self, other):
        if isinstance(other, LogicalKeyboardKey):
            return self.keyId == other.keyId
        return False

    def __hash__(self):
        return hash(self.keyId)

    def __repr__(self):
        name = self.debugName or f"0x{self.keyId:08x}"
        return f"LogicalKeyboardKey({name})"

    space = None
    exclamation = None
    quote = None
    numberSign = None
    dollar = None
    percent = None
    ampersand = None
    quoteSingle = None
    parenthesisLeft = None
    parenthesisRight = None
    asterisk = None
    add = None
    comma = None
    minus = None
    period = None
    slash = None

    digit0 = None
    digit1 = None
    digit2 = None
    digit3 = None
    digit4 = None
    digit5 = None
    digit6 = None
    digit7 = None
    digit8 = None
    digit9 = None

    keyA = None
    keyB = None
    keyC = None
    keyD = None
    keyE = None
    keyF = None
    keyG = None
    keyH = None
    keyI = None
    keyJ = None
    keyK = None
    keyL = None
    keyM = None
    keyN = None
    keyO = None
    keyP = None
    keyQ = None
    keyR = None
    keyS = None
    keyT = None
    keyU = None
    keyV = None
    keyW = None
    keyX = None
    keyY = None
    keyZ = None

    backspace = None
    tab = None
    enter = None
    escape = None
    delete = None

    arrowDown = None
    arrowLeft = None
    arrowRight = None
    arrowUp = None

    end = None
    home = None
    pageDown = None
    pageUp = None
    insert = None

    altLeft = None
    altRight = None
    controlLeft = None
    controlRight = None
    metaLeft = None
    metaRight = None
    shiftLeft = None
    shiftRight = None

    f1 = None
    f2 = None
    f3 = None
    f4 = None
    f5 = None
    f6 = None
    f7 = None
    f8 = None
    f9 = None
    f10 = None
    f11 = None
    f12 = None

    capsLock = None
    numLock = None
    scrollLock = None


_unprintablePlane = 0x00100000000

LogicalKeyboardKey.space = LogicalKeyboardKey(0x00000000020)
LogicalKeyboardKey.exclamation = LogicalKeyboardKey(0x00000000021)
LogicalKeyboardKey.quote = LogicalKeyboardKey(0x00000000022)
LogicalKeyboardKey.numberSign = LogicalKeyboardKey(0x00000000023)
LogicalKeyboardKey.dollar = LogicalKeyboardKey(0x00000000024)
LogicalKeyboardKey.percent = LogicalKeyboardKey(0x00000000025)
LogicalKeyboardKey.ampersand = LogicalKeyboardKey(0x00000000026)
LogicalKeyboardKey.quoteSingle = LogicalKeyboardKey(0x00000000027)
LogicalKeyboardKey.parenthesisLeft = LogicalKeyboardKey(0x00000000028)
LogicalKeyboardKey.parenthesisRight = LogicalKeyboardKey(0x00000000029)
LogicalKeyboardKey.asterisk = LogicalKeyboardKey(0x0000000002A)
LogicalKeyboardKey.add = LogicalKeyboardKey(0x0000000002B)
LogicalKeyboardKey.comma = LogicalKeyboardKey(0x0000000002C)
LogicalKeyboardKey.minus = LogicalKeyboardKey(0x0000000002D)
LogicalKeyboardKey.period = LogicalKeyboardKey(0x0000000002E)
LogicalKeyboardKey.slash = LogicalKeyboardKey(0x0000000002F)

LogicalKeyboardKey.digit0 = LogicalKeyboardKey(0x00000000030)
LogicalKeyboardKey.digit1 = LogicalKeyboardKey(0x00000000031)
LogicalKeyboardKey.digit2 = LogicalKeyboardKey(0x00000000032)
LogicalKeyboardKey.digit3 = LogicalKeyboardKey(0x00000000033)
LogicalKeyboardKey.digit4 = LogicalKeyboardKey(0x00000000034)
LogicalKeyboardKey.digit5 = LogicalKeyboardKey(0x00000000035)
LogicalKeyboardKey.digit6 = LogicalKeyboardKey(0x00000000036)
LogicalKeyboardKey.digit7 = LogicalKeyboardKey(0x00000000037)
LogicalKeyboardKey.digit8 = LogicalKeyboardKey(0x00000000038)
LogicalKeyboardKey.digit9 = LogicalKeyboardKey(0x00000000039)

LogicalKeyboardKey.keyA = LogicalKeyboardKey(0x00000000061)
LogicalKeyboardKey.keyB = LogicalKeyboardKey(0x00000000062)
LogicalKeyboardKey.keyC = LogicalKeyboardKey(0x00000000063)
LogicalKeyboardKey.keyD = LogicalKeyboardKey(0x00000000064)
LogicalKeyboardKey.keyE = LogicalKeyboardKey(0x00000000065)
LogicalKeyboardKey.keyF = LogicalKeyboardKey(0x00000000066)
LogicalKeyboardKey.keyG = LogicalKeyboardKey(0x00000000067)
LogicalKeyboardKey.keyH = LogicalKeyboardKey(0x00000000068)
LogicalKeyboardKey.keyI = LogicalKeyboardKey(0x00000000069)
LogicalKeyboardKey.keyJ = LogicalKeyboardKey(0x0000000006A)
LogicalKeyboardKey.keyK = LogicalKeyboardKey(0x0000000006B)
LogicalKeyboardKey.keyL = LogicalKeyboardKey(0x0000000006C)
LogicalKeyboardKey.keyM = LogicalKeyboardKey(0x0000000006D)
LogicalKeyboardKey.keyN = LogicalKeyboardKey(0x0000000006E)
LogicalKeyboardKey.keyO = LogicalKeyboardKey(0x0000000006F)
LogicalKeyboardKey.keyP = LogicalKeyboardKey(0x00000000070)
LogicalKeyboardKey.keyQ = LogicalKeyboardKey(0x00000000071)
LogicalKeyboardKey.keyR = LogicalKeyboardKey(0x00000000072)
LogicalKeyboardKey.keyS = LogicalKeyboardKey(0x00000000073)
LogicalKeyboardKey.keyT = LogicalKeyboardKey(0x00000000074)
LogicalKeyboardKey.keyU = LogicalKeyboardKey(0x00000000075)
LogicalKeyboardKey.keyV = LogicalKeyboardKey(0x00000000076)
LogicalKeyboardKey.keyW = LogicalKeyboardKey(0x00000000077)
LogicalKeyboardKey.keyX = LogicalKeyboardKey(0x00000000078)
LogicalKeyboardKey.keyY = LogicalKeyboardKey(0x00000000079)
LogicalKeyboardKey.keyZ = LogicalKeyboardKey(0x0000000007A)

LogicalKeyboardKey.backspace = LogicalKeyboardKey(0x00100000008)
LogicalKeyboardKey.tab = LogicalKeyboardKey(0x00100000009)
LogicalKeyboardKey.enter = LogicalKeyboardKey(0x0010000000D)
LogicalKeyboardKey.escape = LogicalKeyboardKey(0x0010000001B)
LogicalKeyboardKey.delete = LogicalKeyboardKey(0x0010000007F)

LogicalKeyboardKey.arrowDown = LogicalKeyboardKey(0x00100000301)
LogicalKeyboardKey.arrowLeft = LogicalKeyboardKey(0x00100000302)
LogicalKeyboardKey.arrowRight = LogicalKeyboardKey(0x00100000303)
LogicalKeyboardKey.arrowUp = LogicalKeyboardKey(0x00100000304)

LogicalKeyboardKey.end = LogicalKeyboardKey(0x00100000305)
LogicalKeyboardKey.home = LogicalKeyboardKey(0x00100000306)
LogicalKeyboardKey.pageDown = LogicalKeyboardKey(0x00100000307)
LogicalKeyboardKey.pageUp = LogicalKeyboardKey(0x00100000308)
LogicalKeyboardKey.insert = LogicalKeyboardKey(0x00100000407)

LogicalKeyboardKey.altLeft = LogicalKeyboardKey(0x00200000102)
LogicalKeyboardKey.altRight = LogicalKeyboardKey(0x00200000103)
LogicalKeyboardKey.controlLeft = LogicalKeyboardKey(0x00200000100)
LogicalKeyboardKey.controlRight = LogicalKeyboardKey(0x00200000101)
LogicalKeyboardKey.metaLeft = LogicalKeyboardKey(0x00200000106)
LogicalKeyboardKey.metaRight = LogicalKeyboardKey(0x00200000107)
LogicalKeyboardKey.shiftLeft = LogicalKeyboardKey(0x00200000104)
LogicalKeyboardKey.shiftRight = LogicalKeyboardKey(0x00200000105)

LogicalKeyboardKey.f1 = LogicalKeyboardKey(0x00100000801)
LogicalKeyboardKey.f2 = LogicalKeyboardKey(0x00100000802)
LogicalKeyboardKey.f3 = LogicalKeyboardKey(0x00100000803)
LogicalKeyboardKey.f4 = LogicalKeyboardKey(0x00100000804)
LogicalKeyboardKey.f5 = LogicalKeyboardKey(0x00100000805)
LogicalKeyboardKey.f6 = LogicalKeyboardKey(0x00100000806)
LogicalKeyboardKey.f7 = LogicalKeyboardKey(0x00100000807)
LogicalKeyboardKey.f8 = LogicalKeyboardKey(0x00100000808)
LogicalKeyboardKey.f9 = LogicalKeyboardKey(0x00100000809)
LogicalKeyboardKey.f10 = LogicalKeyboardKey(0x0010000080A)
LogicalKeyboardKey.f11 = LogicalKeyboardKey(0x0010000080B)
LogicalKeyboardKey.f12 = LogicalKeyboardKey(0x0010000080C)

LogicalKeyboardKey.capsLock = LogicalKeyboardKey(0x00100000104)
LogicalKeyboardKey.numLock = LogicalKeyboardKey(0x0010000010A)
LogicalKeyboardKey.scrollLock = LogicalKeyboardKey(0x0010000010C)

_keyLabels = {
    0x00100000008: "Backspace",
    0x00100000009: "Tab",
    0x0010000000D: "Enter",
    0x0010000001B: "Escape",
    0x0010000007F: "Delete",
    0x00100000301: "Arrow Down",
    0x00100000302: "Arrow Left",
    0x00100000303: "Arrow Right",
    0x00100000304: "Arrow Up",
    0x00100000305: "End",
    0x00100000306: "Home",
    0x00100000307: "Page Down",
    0x00100000308: "Page Up",
    0x00100000407: "Insert",
    0x00200000102: "Alt Left",
    0x00200000103: "Alt Right",
    0x00200000100: "Control Left",
    0x00200000101: "Control Right",
    0x00200000106: "Meta Left",
    0x00200000107: "Meta Right",
    0x00200000104: "Shift Left",
    0x00200000105: "Shift Right",
    0x00100000801: "F1",
    0x00100000802: "F2",
    0x00100000803: "F3",
    0x00100000804: "F4",
    0x00100000805: "F5",
    0x00100000806: "F6",
    0x00100000807: "F7",
    0x00100000808: "F8",
    0x00100000809: "F9",
    0x0010000080A: "F10",
    0x0010000080B: "F11",
    0x0010000080C: "F12",
    0x00100000104: "Caps Lock",
    0x0010000010A: "Num Lock",
    0x0010000010C: "Scroll Lock",
}

_knownLogicalKeys = {
    key.keyId: key
    for key in [
        LogicalKeyboardKey.space,
        LogicalKeyboardKey.enter,
        LogicalKeyboardKey.escape,
        LogicalKeyboardKey.backspace,
        LogicalKeyboardKey.tab,
        LogicalKeyboardKey.delete,
        LogicalKeyboardKey.arrowDown,
        LogicalKeyboardKey.arrowLeft,
        LogicalKeyboardKey.arrowRight,
        LogicalKeyboardKey.arrowUp,
        LogicalKeyboardKey.f1,
        LogicalKeyboardKey.f2,
        LogicalKeyboardKey.f3,
        LogicalKeyboardKey.f4,
        LogicalKeyboardKey.f5,
        LogicalKeyboardKey.f6,
        LogicalKeyboardKey.f7,
        LogicalKeyboardKey.f8,
        LogicalKeyboardKey.f9,
        LogicalKeyboardKey.f10,
        LogicalKeyboardKey.f11,
        LogicalKeyboardKey.f12,
    ]
}


class PhysicalKeyboardKey(FlutImmutableObject):

    def __init__(self, usbHidUsage: int):
        super().__init__("PhysicalKeyboardKey")
        self.usbHidUsage = usbHidUsage

    @staticmethod
    def _flut_unpack(data: dict) -> "PhysicalKeyboardKey":
        usage = _flut_unpack_required_field(data, "usbHidUsage")
        if usage in _knownPhysicalKeys:
            return _knownPhysicalKeys[usage]
        return PhysicalKeyboardKey(usage)

    @override
    def _flut_pack(self) -> dict:
        result = self._flut_base_props()
        result["usbHidUsage"] = self.usbHidUsage
        return result

    @property
    def debugName(self) -> Optional[str]:
        return _physicalKeyLabels.get(self.usbHidUsage)

    def __eq__(self, other):
        if isinstance(other, PhysicalKeyboardKey):
            return self.usbHidUsage == other.usbHidUsage
        return False

    def __hash__(self):
        return hash(self.usbHidUsage)

    def __repr__(self):
        name = self.debugName or f"0x{self.usbHidUsage:08x}"
        return f"PhysicalKeyboardKey({name})"

    keyA = None
    keyB = None
    keyC = None
    keyD = None
    keyE = None
    keyF = None
    keyG = None
    keyH = None
    keyI = None
    keyJ = None
    keyK = None
    keyL = None
    keyM = None
    keyN = None
    keyO = None
    keyP = None
    keyQ = None
    keyR = None
    keyS = None
    keyT = None
    keyU = None
    keyV = None
    keyW = None
    keyX = None
    keyY = None
    keyZ = None

    digit1 = None
    digit2 = None
    digit3 = None
    digit4 = None
    digit5 = None
    digit6 = None
    digit7 = None
    digit8 = None
    digit9 = None
    digit0 = None

    enter = None
    escape = None
    backspace = None
    tab = None
    space = None

    arrowUp = None
    arrowDown = None
    arrowLeft = None
    arrowRight = None


PhysicalKeyboardKey.keyA = PhysicalKeyboardKey(0x00070004)
PhysicalKeyboardKey.keyB = PhysicalKeyboardKey(0x00070005)
PhysicalKeyboardKey.keyC = PhysicalKeyboardKey(0x00070006)
PhysicalKeyboardKey.keyD = PhysicalKeyboardKey(0x00070007)
PhysicalKeyboardKey.keyE = PhysicalKeyboardKey(0x00070008)
PhysicalKeyboardKey.keyF = PhysicalKeyboardKey(0x00070009)
PhysicalKeyboardKey.keyG = PhysicalKeyboardKey(0x0007000A)
PhysicalKeyboardKey.keyH = PhysicalKeyboardKey(0x0007000B)
PhysicalKeyboardKey.keyI = PhysicalKeyboardKey(0x0007000C)
PhysicalKeyboardKey.keyJ = PhysicalKeyboardKey(0x0007000D)
PhysicalKeyboardKey.keyK = PhysicalKeyboardKey(0x0007000E)
PhysicalKeyboardKey.keyL = PhysicalKeyboardKey(0x0007000F)
PhysicalKeyboardKey.keyM = PhysicalKeyboardKey(0x00070010)
PhysicalKeyboardKey.keyN = PhysicalKeyboardKey(0x00070011)
PhysicalKeyboardKey.keyO = PhysicalKeyboardKey(0x00070012)
PhysicalKeyboardKey.keyP = PhysicalKeyboardKey(0x00070013)
PhysicalKeyboardKey.keyQ = PhysicalKeyboardKey(0x00070014)
PhysicalKeyboardKey.keyR = PhysicalKeyboardKey(0x00070015)
PhysicalKeyboardKey.keyS = PhysicalKeyboardKey(0x00070016)
PhysicalKeyboardKey.keyT = PhysicalKeyboardKey(0x00070017)
PhysicalKeyboardKey.keyU = PhysicalKeyboardKey(0x00070018)
PhysicalKeyboardKey.keyV = PhysicalKeyboardKey(0x00070019)
PhysicalKeyboardKey.keyW = PhysicalKeyboardKey(0x0007001A)
PhysicalKeyboardKey.keyX = PhysicalKeyboardKey(0x0007001B)
PhysicalKeyboardKey.keyY = PhysicalKeyboardKey(0x0007001C)
PhysicalKeyboardKey.keyZ = PhysicalKeyboardKey(0x0007001D)

PhysicalKeyboardKey.digit1 = PhysicalKeyboardKey(0x0007001E)
PhysicalKeyboardKey.digit2 = PhysicalKeyboardKey(0x0007001F)
PhysicalKeyboardKey.digit3 = PhysicalKeyboardKey(0x00070020)
PhysicalKeyboardKey.digit4 = PhysicalKeyboardKey(0x00070021)
PhysicalKeyboardKey.digit5 = PhysicalKeyboardKey(0x00070022)
PhysicalKeyboardKey.digit6 = PhysicalKeyboardKey(0x00070023)
PhysicalKeyboardKey.digit7 = PhysicalKeyboardKey(0x00070024)
PhysicalKeyboardKey.digit8 = PhysicalKeyboardKey(0x00070025)
PhysicalKeyboardKey.digit9 = PhysicalKeyboardKey(0x00070026)
PhysicalKeyboardKey.digit0 = PhysicalKeyboardKey(0x00070027)

PhysicalKeyboardKey.enter = PhysicalKeyboardKey(0x00070028)
PhysicalKeyboardKey.escape = PhysicalKeyboardKey(0x00070029)
PhysicalKeyboardKey.backspace = PhysicalKeyboardKey(0x0007002A)
PhysicalKeyboardKey.tab = PhysicalKeyboardKey(0x0007002B)
PhysicalKeyboardKey.space = PhysicalKeyboardKey(0x0007002C)

PhysicalKeyboardKey.arrowRight = PhysicalKeyboardKey(0x0007004F)
PhysicalKeyboardKey.arrowLeft = PhysicalKeyboardKey(0x00070050)
PhysicalKeyboardKey.arrowDown = PhysicalKeyboardKey(0x00070051)
PhysicalKeyboardKey.arrowUp = PhysicalKeyboardKey(0x00070052)

_physicalKeyLabels = {
    0x00070004: "Key A",
    0x00070005: "Key B",
    0x00070006: "Key C",
    0x00070007: "Key D",
    0x00070008: "Key E",
    0x00070009: "Key F",
    0x0007000A: "Key G",
    0x0007000B: "Key H",
    0x0007000C: "Key I",
    0x0007000D: "Key J",
    0x0007000E: "Key K",
    0x0007000F: "Key L",
    0x00070010: "Key M",
    0x00070011: "Key N",
    0x00070012: "Key O",
    0x00070013: "Key P",
    0x00070014: "Key Q",
    0x00070015: "Key R",
    0x00070016: "Key S",
    0x00070017: "Key T",
    0x00070018: "Key U",
    0x00070019: "Key V",
    0x0007001A: "Key W",
    0x0007001B: "Key X",
    0x0007001C: "Key Y",
    0x0007001D: "Key Z",
    0x0007001E: "Digit 1",
    0x0007001F: "Digit 2",
    0x00070020: "Digit 3",
    0x00070021: "Digit 4",
    0x00070022: "Digit 5",
    0x00070023: "Digit 6",
    0x00070024: "Digit 7",
    0x00070025: "Digit 8",
    0x00070026: "Digit 9",
    0x00070027: "Digit 0",
    0x00070028: "Enter",
    0x00070029: "Escape",
    0x0007002A: "Backspace",
    0x0007002B: "Tab",
    0x0007002C: "Space",
    0x0007004F: "Arrow Right",
    0x00070050: "Arrow Left",
    0x00070051: "Arrow Down",
    0x00070052: "Arrow Up",
}

_knownPhysicalKeys = {
    key.usbHidUsage: key
    for key in [
        PhysicalKeyboardKey.keyA,
        PhysicalKeyboardKey.keyB,
        PhysicalKeyboardKey.keyC,
        PhysicalKeyboardKey.keyD,
        PhysicalKeyboardKey.keyE,
        PhysicalKeyboardKey.keyF,
        PhysicalKeyboardKey.keyG,
        PhysicalKeyboardKey.keyH,
        PhysicalKeyboardKey.keyI,
        PhysicalKeyboardKey.keyJ,
        PhysicalKeyboardKey.keyK,
        PhysicalKeyboardKey.keyL,
        PhysicalKeyboardKey.keyM,
        PhysicalKeyboardKey.keyN,
        PhysicalKeyboardKey.keyO,
        PhysicalKeyboardKey.keyP,
        PhysicalKeyboardKey.keyQ,
        PhysicalKeyboardKey.keyR,
        PhysicalKeyboardKey.keyS,
        PhysicalKeyboardKey.keyT,
        PhysicalKeyboardKey.keyU,
        PhysicalKeyboardKey.keyV,
        PhysicalKeyboardKey.keyW,
        PhysicalKeyboardKey.keyX,
        PhysicalKeyboardKey.keyY,
        PhysicalKeyboardKey.keyZ,
        PhysicalKeyboardKey.enter,
        PhysicalKeyboardKey.escape,
        PhysicalKeyboardKey.backspace,
        PhysicalKeyboardKey.tab,
        PhysicalKeyboardKey.space,
        PhysicalKeyboardKey.arrowUp,
        PhysicalKeyboardKey.arrowDown,
        PhysicalKeyboardKey.arrowLeft,
        PhysicalKeyboardKey.arrowRight,
    ]
}
