import { useEffect, useMemo } from 'react';

export enum Modifier {
  Ctrl = 'ctrl',
  Shift = 'shift',
  Alt = 'alt',
};

export enum Key {
  Backspace = 8,
  Tab = 9,
  Enter = 13,
  PageUp = 33,
  Space = 32,
  PageDown = 34,
  End = 35,
  Home = 36,
  Left = 37,
  Up = 38,
  Right = 39,
  Down = 40,
  Num0 = 48,
  Num1 = 49,
  Num2 = 50,
  Num3 = 51,
  Num4 = 52,
  Num5 = 53,
  Num6 = 54,
  Num7 = 55,
  Num8 = 56,
  Num9 = 57,
  A = 65,
  B = 66,
  C = 67,
  D = 68,
  E = 69,
  F = 70,
  G = 71,
  H = 72,
  I = 73,
  J = 74,
  K = 75,
  L = 76,
  M = 77,
  N = 78,
  O = 79,
  P = 80,
  Q = 81,
  R = 82,
  S = 83,
  T = 84,
  U = 85,
  V = 86,
  W = 87,
  X = 88,
  Y = 89,
  Z = 90,
  F1 = 112,
  F2 = 113,
  F3 = 114,
  F4 = 115,
  F5 = 116,
  F6 = 117,
  F7 = 118,
  F8 = 119,
  F9 = 120,
  F10 = 121,
  F11 = 122,
  F12 = 123,
  Semicolon = 186,
  Equals = 187,
  Comma = 188,
  Dash = 189,
  Period = 190,
  ForwardSlash = 191,
  Tilde = 192,
  OpenBracket = 219,
  BackSlash = 220,
  CloseBraket = 221,
  SingleQuote = 222,
};

export type KeyInput = [Modifier, Modifier, Modifier, Key] | [Modifier, Modifier, Key] | [Modifier, Key] | [Key]

const listeners: {[key: string]: () => void} = {};

const getEventString = (e: KeyboardEvent) => {
  const modifiers = [
    (e.ctrlKey || e.metaKey) && Modifier.Ctrl,
    e.altKey && Modifier.Alt,
    e.shiftKey && Modifier.Shift
  ].filter(Boolean).sort() as Modifier[];
  const userInput: KeyInput = [...modifiers, e.which as Key] as KeyInput;
  return userInput.join('+');
}

window.addEventListener('keydown', (e) => {
  if (e.target != null && (e.target as HTMLElement).matches('input, textarea, code')) {
    return;
  }
  const inputString = getEventString(e);
  const listener = listeners[inputString];
  if (listener) {
    listener();
    e.preventDefault();
  };
});

window.addEventListener('keyup', (e) => {
  if (e.target != null && (e.target as HTMLElement).matches('input, textarea, code')) {
    return;
  }
  if (listeners.hasOwnProperty(getEventString(e))) {
    e.preventDefault();
  };
});

window.addEventListener('keypress', (e) => {
  if (e.target != null && (e.target as HTMLElement).matches('input, textarea, code')) {
    return;
  }
  if (listeners.hasOwnProperty(getEventString(e))) {
    e.preventDefault();
  };
});

export default (inputKey: KeyInput | Key, callback: () => void) => {
  const inputKeyString = useMemo(
    () => {
      const key: KeyInput = typeof inputKey === 'number' ? [inputKey] : inputKey;
      const modifierPart = key.slice(0, -1) as string[];
      const keyCode = key.slice(-1)[0] as number;
      return [...modifierPart.sort(), keyCode].join('+');
    },
    [inputKey]
  );
  useEffect(
    () => {
      const existingListener = listeners[inputKeyString];
      if (existingListener && existingListener.toString() !== callback.toString()) {
        console.log(existingListener, callback);
        throw new Error(`Duplicate assigment to input ${inputKeyString}`);
      }
      listeners[inputKeyString] = callback;
    },
    [inputKeyString, callback]
  );
}
