import KeyTable from './keysym.js';
import keysyms from './keysymdef.js';
import vkeys from './vkeys.js';
import fixedkeys from './fixedkeys.js';
import DOMKeyTable from './domkeytable.js';
import * as browser from '../util/browser.js';

// Get 'KeyboardEvent.code', handling legacy browsers
export function getKeycode(evt) {
  // Are we getting proper key identifiers?
  // (unfortunately Firefox and Chrome are crappy here and gives
  // us an empty string on some platforms, rather than leaving it
  // undefined)
  if (evt.code) {
    // Mozilla isn't fully in sync with the spec yet
    switch (evt.code) {
      case 'OSLeft':
        return 'MetaLeft';
      case 'OSRight':
        return 'MetaRight';
    }

    return evt.code;
  }

  // The de-facto standard is to use Windows Virtual-Key codes
  // in the 'keyCode' field for non-printable characters
  if (evt.keyCode in vkeys) {
    let code = vkeys[evt.keyCode];

    // macOS has messed up this code for some reason
    if (browser.isMac() && code === 'ContextMenu') {
      code = 'MetaRight';
    }

    // The keyCode doesn't distinguish between left and right
    // for the standard modifiers
    if (evt.location === 2) {
      switch (code) {
        case 'ShiftLeft':
          return 'ShiftRight';
        case 'ControlLeft':
          return 'ControlRight';
        case 'AltLeft':
          return 'AltRight';
      }
    }

    // Nor a bunch of the numpad keys
    if (evt.location === 3) {
      switch (code) {
        case 'Delete':
          return 'NumpadDecimal';
        case 'Insert':
          return 'Numpad0';
        case 'End':
          return 'Numpad1';
        case 'ArrowDown':
          return 'Numpad2';
        case 'PageDown':
          return 'Numpad3';
        case 'ArrowLeft':
          return 'Numpad4';
        case 'ArrowRight':
          return 'Numpad6';
        case 'Home':
          return 'Numpad7';
        case 'ArrowUp':
          return 'Numpad8';
        case 'PageUp':
          return 'Numpad9';
        case 'Enter':
          return 'NumpadEnter';
      }
    }

    return code;
  }

  return 'Unidentified';
}

// Get 'KeyboardEvent.key', handling legacy browsers
export function getKey(evt) {
  // Are we getting a proper key value?
  if (evt.key !== undefined) {
    // Mozilla isn't fully in sync with the spec yet
    switch (evt.key) {
      case 'OS':
        return 'Meta';
      case 'LaunchMyComputer':
        return 'LaunchApplication1';
      case 'LaunchCalculator':
        return 'LaunchApplication2';
    }

    // iOS leaks some OS names
    switch (evt.key) {
      case 'UIKeyInputUpArrow':
        return 'ArrowUp';
      case 'UIKeyInputDownArrow':
        return 'ArrowDown';
      case 'UIKeyInputLeftArrow':
        return 'ArrowLeft';
      case 'UIKeyInputRightArrow':
        return 'ArrowRight';
      case 'UIKeyInputEscape':
        return 'Escape';
    }

    // Broken behaviour in Chrome
    if (evt.key === '\x00' && evt.code === 'NumpadDecimal') {
      return 'Delete';
    }

    return evt.key;
  }

  // Try to deduce it based on the physical key
  const code = getKeycode(evt);
  if (code in fixedkeys) {
    return fixedkeys[code];
  }

  // If that failed, then see if we have a printable character
  if (evt.charCode) {
    return String.fromCharCode(evt.charCode);
  }

  // At this point we have nothing left to go on
  return 'Unidentified';
}

// Get the most reliable keysym value we can get from a key event
export function getKeysym(evt) {
  const key = getKey(evt);

  if (key === 'Unidentified') {
    return null;
  }

  // First look up special keys
  if (key in DOMKeyTable) {
    let location = evt.location;

    // Safari screws up location for the right cmd key
    if (key === 'Meta' && location === 0) {
      location = 2;
    }

    // And for Clear
    if (key === 'Clear' && location === 3) {
      let code = getKeycode(evt);
      if (code === 'NumLock') {
        location = 0;
      }
    }

    if (location === undefined || location > 3) {
      location = 0;
    }

    // The original Meta key now gets confused with the Windows key
    // https://bugs.chromium.org/p/chromium/issues/detail?id=1020141
    // https://bugzilla.mozilla.org/show_bug.cgi?id=1232918
    if (key === 'Meta') {
      let code = getKeycode(evt);
      if (code === 'AltLeft') {
        return KeyTable.XK_Meta_L;
      } else if (code === 'AltRight') {
        return KeyTable.XK_Meta_R;
      }
    }

    // macOS has Clear instead of NumLock, but the remote system is
    // probably not macOS, so lying here is probably best...
    if (key === 'Clear') {
      let code = getKeycode(evt);
      if (code === 'NumLock') {
        return KeyTable.XK_Num_Lock;
      }
    }

    // Windows sends alternating symbols for some keys when using a
    // Japanese layout. We have no way of synchronising with the IM
    // running on the remote system, so we send some combined keysym
    // instead and hope for the best.
    if (browser.isWindows()) {
      switch (key) {
        case 'Zenkaku':
        case 'Hankaku':
          return KeyTable.XK_Zenkaku_Hankaku;
        case 'Romaji':
        case 'KanaMode':
          return KeyTable.XK_Romaji;
      }
    }

    return DOMKeyTable[key][location];
  }

  // Now we need to look at the Unicode symbol instead

  // Special key? (FIXME: Should have been caught earlier)
  if (key.length !== 1) {
    return null;
  }

  const codepoint = key.charCodeAt();
  if (codepoint) {
    return keysyms.lookup(codepoint);
  }

  return null;
}
