// Key codes and modifiers correspond to definitions in the
// [Linux USB HID gadget driver](https://www.kernel.org/doc/Documentation/usb/gadget_hid.txt)
// [Universal Serial Bus HID Usage Tables: Section 10](https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf)
// These are all the key codes (not scan codes) that an 85/101/102 keyboard might have on it
export const keys = {
  Again: 0x79,
  AlternateErase: 0x9d,
  AltGr: 0xe6,        // aka AltRight
  AltLeft: 0xe2,
  AltRight: 0xe6,
  Application: 0x65,
  ArrowDown: 0x51,
  ArrowLeft: 0x50,
  ArrowRight: 0x4f,
  ArrowUp: 0x52,
  Attention: 0x9a,
  Backquote: 0x35,    // aka Grave
  Backslash: 0x31,
  Backspace: 0x2a,
  BracketLeft: 0x2f,  // aka LeftBrace
  BracketRight: 0x30, // aka RightBrace
  Cancel: 0x9b,
  CapsLock: 0x39,
  Clear: 0x9c,
  ClearAgain: 0xa2,
  Comma: 0x36,
  Compose: 0xe3,
  ContextMenu: 0x65,
  ControlLeft: 0xe0,
  ControlRight: 0xe4,
  Copy: 0x7c,
  CrSel: 0xa3,
  CurrencySubunit: 0xb5,
  CurrencyUnit: 0xb4,
  Cut: 0x7b,
  DecimalSeparator: 0xb3,
  Delete: 0x4c,
  Digit0: 0x27,
  Digit1: 0x1e,
  Digit2: 0x1f,
  Digit3: 0x20,
  Digit4: 0x21,
  Digit5: 0x22,
  Digit6: 0x23,
  Digit7: 0x24,
  Digit8: 0x25,
  Digit9: 0x26,
  End: 0x4d,
  Enter: 0x28,
  Equal: 0x2e,
  Escape: 0x29,
  Execute: 0x74,
  ExSel: 0xa4,
  F1: 0x3a,
  F2: 0x3b,
  F3: 0x3c,
  F4: 0x3d,
  F5: 0x3e,
  F6: 0x3f,
  F7: 0x40,
  F8: 0x41,
  F9: 0x42,
  F10: 0x43,
  F11: 0x44,
  F12: 0x45,
  F13: 0x68,
  F14: 0x69,
  F15: 0x6a,
  F16: 0x6b,
  F17: 0x6c,
  F18: 0x6d,
  F19: 0x6e,
  F20: 0x6f,
  F21: 0x70,
  F22: 0x71,
  F23: 0x72,
  F24: 0x73,
  Find: 0x7e,
  Grave: 0x35,
  HashTilde: 0x32,  // non-US # and ~
  Help: 0x75,
  Home: 0x4a,
  Insert: 0x49,
  International7: 0x8d,
  International8: 0x8e,
  International9: 0x8f,
  IntlBackslash: 0x64, // non-US \ and |
  KeyA: 0x04,
  KeyB: 0x05,
  KeyC: 0x06,
  KeyD: 0x07,
  KeyE: 0x08,
  KeyF: 0x09,
  KeyG: 0x0a,
  KeyH: 0x0b,
  KeyI: 0x0c,
  KeyJ: 0x0d,
  KeyK: 0x0e,
  KeyL: 0x0f,
  KeyM: 0x10,
  KeyN: 0x11,
  KeyO: 0x12,
  KeyP: 0x13,
  KeyQ: 0x14,
  KeyR: 0x15,
  KeyS: 0x16,
  KeyT: 0x17,
  KeyU: 0x18,
  KeyV: 0x19,
  KeyW: 0x1a,
  KeyX: 0x1b,
  KeyY: 0x1c,
  KeyZ: 0x1d,
  KeyRO: 0x87,
  KatakanaHiragana: 0x88,
  Yen: 0x89,
  Henkan: 0x8a,
  Muhenkan: 0x8b,
  KPJPComma: 0x8c,
  Hangeul: 0x90,
  Hanja: 0x91,
  Katakana: 0x92,
  Hiragana: 0x93,
  ZenkakuHankaku: 0x94,
  LockingCapsLock: 0x82,
  LockingNumLock: 0x83,
  LockingScrollLock: 0x84,
  Lang6: 0x95,
  Lang7: 0x96,
  Lang8: 0x97,
  Lang9: 0x98,
  Menu: 0x76,
  MetaLeft: 0xe3,
  MetaRight: 0xe7,
  Minus: 0x2d,
  Mute: 0x7f,
  NumLock: 0x53,  // and Clear
  Numpad0: 0x62,  // and Insert
  Numpad00: 0xb0,
  Numpad000: 0xb1,
  Numpad1: 0x59,  // and End
  Numpad2: 0x5a,  // and Down Arrow
  Numpad3: 0x5b,  // and Page Down
  Numpad4: 0x5c,  // and Left Arrow
  Numpad5: 0x5d,
  Numpad6: 0x5e,  // and Right Arrow
  Numpad7: 0x5f,  // and Home
  Numpad8: 0x60,  // and Up Arrow
  Numpad9: 0x61,  // and Page Up
  NumpadAdd: 0x57,
  NumpadAnd: 0xc7,
  NumpadAt: 0xce,
  NumpadBackspace: 0xbb,
  NumpadBinary: 0xda,
  NumpadCircumflex: 0xc3,
  NumpadClear: 0xd8,
  NumpadClearEntry: 0xd9,
  NumpadColon: 0xcb,
  NumpadComma: 0x85,
  NumpadDecimal: 0x63, // and Delete
  NumpadDecimalBase: 0xdc,
  NumpadDelete: 0x63,
  NumpadDivide: 0x54,
  NumpadDownArrow: 0x5a,
  NumpadEnd: 0x59,
  NumpadEnter: 0x58,
  NumpadEqual: 0x67,
  NumpadExclamation: 0xcf,
  NumpadGreaterThan: 0xc6,
  NumpadHexadecimal: 0xdd,
  NumpadHome: 0x5f,
  NumpadKeyA: 0xbc,
  NumpadKeyB: 0xbd,
  NumpadKeyC: 0xbe,
  NumpadKeyD: 0xbf,
  NumpadKeyE: 0xc0,
  NumpadKeyF: 0xc1,
  NumpadLeftArrow: 0x5c,
  NumpadLeftBrace: 0xb8,
  NumpadLeftParen: 0xb6,
  NumpadLessThan: 0xc5,
  NumpadLogicalAnd: 0xc8,
  NumpadLogicalOr: 0xca,
  NumpadMemoryAdd: 0xd3,
  NumpadMemoryClear: 0xd2,
  NumpadMemoryDivide: 0xd6,
  NumpadMemoryMultiply: 0xd5,
  NumpadMemoryRecall: 0xd1,
  NumpadMemoryStore: 0xd0,
  NumpadMemorySubtract: 0xd4,
  NumpadMultiply: 0x55,
  NumpadOctal: 0xdb,
  NumpadOctathorpe: 0xcc,
  NumpadOr: 0xc9,
  NumpadPageDown: 0x5b,
  NumpadPageUp: 0x61,
  NumpadPercent: 0xc4,
  NumpadPlusMinus: 0xd7,
  NumpadRightArrow: 0x5e,
  NumpadRightBrace: 0xb9,
  NumpadRightParen: 0xb7,
  NumpadSpace: 0xcd,
  NumpadSubtract: 0x56,
  NumpadTab: 0xba,
  NumpadUpArrow: 0x60,
  NumpadXOR: 0xc2,
  Octothorpe: 0x32, // non-US # and ~
  Operation: 0xa1,
  Out: 0xa0,
  PageDown: 0x4e,
  PageUp: 0x4b,
  Paste: 0x7d,
  Pause: 0x48,
  Period: 0x37, // aka Dot
  Power: 0x66,
  PrintScreen: 0x46,
  Prior: 0x9d,
  Quote: 0x34, // aka Single Quote or Apostrophe
  Return: 0x9e,
  ScrollLock: 0x47,
  Select: 0x77,
  Semicolon: 0x33,
  Separator: 0x9f,
  ShiftLeft: 0xe1,
  ShiftRight: 0xe5,
  Slash: 0x38,
  Space: 0x2c,
  Stop: 0x78,
  SystemRequest: 0x9a,  // aka Attention
  Tab: 0x2b,
  ThousandsSeparator: 0xb2,
  Tilde: 0x35,
  Undo: 0x7a,
  VolumeDown: 0x81,
  VolumeUp: 0x80,
} as Record<string, number>;

export const deadKeys = {
  Acute: 0x00b4,
  Breve: 0x02d8,
  Caron: 0x02c7,
  Cedilla: 0x00b8,
  Circumflex: 0x005e, // or 0x02c6?
  Comma: 0x002c,
  Dot: 0x00b7,
  DoubleAcute: 0x02dd,
  Grave: 0x0060,
  Kreis: 0x00b0,
  Ogonek: 0x02db,
  Ring: 0x02da,
  Slash: 0x02f8,
  Tilde: 0x007e,
  Umlaut: 0x00a8,
} as Record<string, number>

export const modifiers = {
  ControlLeft: 0x01,
  ControlRight: 0x10,
  ShiftLeft: 0x02,
  ShiftRight: 0x20,
  AltLeft: 0x04,
  AltRight: 0x40,
  MetaLeft: 0x08,
  MetaRight: 0x80,
  AltGr: 0x40,
} as Record<string, number>;

export const hidKeyToModifierMask = {
  0xe0: modifiers.ControlLeft,
  0xe1: modifiers.ShiftLeft,
  0xe2: modifiers.AltLeft,
  0xe3: modifiers.MetaLeft,
  0xe4: modifiers.ControlRight,
  0xe5: modifiers.ShiftRight,
  0xe6: modifiers.AltRight, // can also be AltGr
  0xe7: modifiers.MetaRight,
} as Record<number, number>;

export const latchingKeys = ["CapsLock", "ScrollLock", "NumLock", "Meta", "Compose", "Kana"];

export function decodeModifiers(modifier: number) {
  return {
    isShiftActive: (modifier & (modifiers.ShiftLeft | modifiers.ShiftRight)) !== 0,
    isControlActive: (modifier & (modifiers.ControlLeft | modifiers.ControlRight)) !== 0,
    isAltActive: (modifier & (modifiers.AltLeft | modifiers.AltRight)) !== 0,
    isMetaActive: (modifier & (modifiers.MetaLeft | modifiers.MetaRight)) !== 0,
    isAltGrActive: (modifier & modifiers.AltGr) !== 0,
  };
}