
// Get 'KeyboardEvent.code', handling legacy browsers
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
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
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;
}
window.getKeycode = getKeycode;
window.getKeysym = getKeysym;
