"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.keyToIcon = exports.isAltPressed = exports.isShiftPressed = exports.isKeyRepeat = exports.isKeyPressed = exports.makeKeyupListener = exports.listenKeyup = exports.makeKeypressListener = exports.makeKeydownListener = exports.listenKeypress = exports.listenKeydown = exports.listenShortcut = exports.makeShortcutListener = exports.isSupportedKeyboardEvent = exports.getKey = exports.sameSetArray = void 0;
const f_1 = require("../../../system/f/comparisson/Equals/f");
const f_2 = require("../../../system/f/object/Keys/f");
const id_1 = require("../../../util/id");
const addGlobalBlurListener_1 = require("../../addGlobalBlurListener");
const keyCode_1 = require("./keyCode");
function sameSetArray(a, b) {
    const _ = (a, b) => {
        return a.localeCompare(b);
    };
    return (0, f_1.default)(a.sort(_), b.sort(_));
}
exports.sameSetArray = sameSetArray;
function getKey(keyCode) {
    return keyCode_1.keyCodeToKey[keyCode];
}
exports.getKey = getKey;
function isSupportedKeyboardEvent(event) {
    const { key, metaKey } = event;
    // if (metaKey) {
    //   return false
    // }
    if (key === 'imeprocess') {
        return false;
    }
    if (key === 'CapsLock') {
        return false;
    }
    return true;
}
exports.isSupportedKeyboardEvent = isSupportedKeyboardEvent;
class KeyboardController {
    constructor($system, $element) {
        this._ctrl = false;
        this._meta = false;
        this._shift = false;
        this._alt = false;
        this._repeat = false;
        this._pressed = [];
        this._listen = () => {
            this.$element.addEventListener('keydown', this._onKeydown);
            this.$element.addEventListener('keyup', this._onKeyup);
            this.$element.addEventListener('focusin', this._onFocusIn);
            this.$element.addEventListener('focusout', this._onFocusOut);
        };
        this._unlisten = () => {
            // console.log('KeyboardController', '_unlisten')
            this.$element.removeEventListener('keydown', this._onKeydown);
            this.$element.removeEventListener('keyup', this._onKeyup);
            this.$element.removeEventListener('focusin', this._onFocusIn);
            this.$element.removeEventListener('focusout', this._onFocusOut);
        };
        this._reset = () => {
            const { $system } = this;
            const { input: $input } = $system;
            const { keyboard: $keyboard } = $input;
            const { pressed: $pressed } = $keyboard;
            // AD HOC
            this._repeat = false;
            // for (const keyCode of pressed) {
            //   this._keydown(keyCode)
            // }
            this._pressed = [...$pressed];
        };
        this._onFocusIn = () => {
            // console.log('KeyboardController', '_onFocusIn')
            this._reset();
        };
        this._onFocusOut = () => {
            // console.log('KeyboardController', '_onFocusOut')
            // this._flush()
        };
        this._flush = () => {
            ;
            [...this._pressed].forEach((p) => this._keyup(p));
        };
        this._keydown = (keyCode) => {
            const index = this._pressed.indexOf(keyCode);
            // const previous = this.getCurrentCombo()
            if (index === -1) {
                this._pressed.push(keyCode);
            }
            // const current = this.getCurrentCombo()
            // console.log('_keydown', `${previous} -> ${current}`)
            const filtered = this._filterShortcuts('keydown');
            for (const shortcut of filtered) {
                if (this._repeat && !shortcut.multiple) {
                    continue;
                }
                const key = keyCode_1.keyCodeToKey[keyCode];
                shortcut.keydown(key, {
                    key,
                    keyCode,
                    ctrlKey: this._ctrl,
                    shiftKey: this._shift,
                    altKey: this._alt,
                    metaKey: this._meta,
                    repeat: this._repeat,
                });
            }
            // if (sameSetArray(current, ['meta', 'alt', 'i'])) {
            //   this._flush()
            // }
            return filtered;
        };
        this._keyup = (keyCode) => {
            const index = this._pressed.indexOf(keyCode);
            if (index > -1) {
                const filtered = this._filterShortcuts('keyup');
                // const previous = this.getCurrentCombo()
                this._remove(keyCode);
                // const current = this.getCurrentCombo()
                // console.log('_keyup', `${previous} -> ${current}`)
                const key = keyCode_1.keyCodeToKey[keyCode];
                for (const shortcut of filtered) {
                    shortcut.keyup(key, {
                        key,
                        keyCode,
                        ctrlKey: this._ctrl,
                        shiftKey: this._shift,
                        altKey: this._alt,
                        metaKey: this._meta,
                        repeat: this._repeat,
                    });
                }
            }
        };
        this._onKeydown = (event) => {
            const {} = this.$system;
            const { keyCode, key, ctrlKey, shiftKey, metaKey, altKey, repeat } = event;
            if (metaKey) {
                // event.preventDefault()
                return;
            }
            if (!isSupportedKeyboardEvent(event)) {
                event.preventDefault();
                return;
            }
            this._ctrl = ctrlKey;
            this._meta = metaKey;
            this._shift = shiftKey;
            this._alt = altKey;
            this._repeat = repeat;
            const shortcuts = this._keydown(keyCode);
            for (const shortcut of shortcuts) {
                if (shortcut && shortcut.preventDefault) {
                    event.preventDefault();
                }
            }
        };
        this._onKeyup = (event) => {
            // console.log('KeyboardController', '_onKeyup')
            if (!isSupportedKeyboardEvent(event)) {
                return;
            }
            const { keyCode, key, ctrlKey, shiftKey, metaKey, altKey } = event;
            this._ctrl = ctrlKey;
            this._meta = metaKey;
            this._shift = shiftKey;
            this._alt = altKey;
            this._keyup(keyCode);
            // if (this._pressed.length > 0) {
            //   this._keydown(this._pressed[this._pressed.length - 1])
            // }
            // meta/alt key prevents other keys from firing up
            // if (key === 'meta') {
            //   this._flush()
            // }
        };
        this.$system = $system;
        this.$element = $element;
        this._ctrl = false;
        this._meta = false;
        this._shift = false;
        this._alt = false;
        this._pressed = [];
        this._shortcuts = {};
        const { root: $root } = $system;
        (0, addGlobalBlurListener_1.addGlobalBlurListener)($root, () => {
            this._flush();
        });
        this._reset();
    }
    isCtrlPressed() {
        return this._ctrl;
    }
    isAltPressed() {
        return this._alt;
    }
    isShiftPressed() {
        return this._shift;
    }
    _removeEmptySpace(shortcut) {
        if (shortcut.combo) {
            if (Array.isArray(shortcut.combo)) {
                shortcut.combo = shortcut.combo.map((c) => c.replace(/ /g, ''));
            }
            else {
                shortcut.combo = shortcut.combo.replace(/ /g, '');
            }
        }
        return shortcut;
    }
    addShortcutGroup(shortcutGroup) {
        const id = (0, id_1.randomIdNotIn)(this._shortcuts);
        this._shortcuts[id] = shortcutGroup.map(this._removeEmptySpace);
        if ((0, f_2.keys)(this._shortcuts).length === 1) {
            this._listen();
        }
        return id;
    }
    removeShortcutGroup(id) {
        delete this._shortcuts[id];
        if ((0, f_2.keys)(this._shortcuts).length === 0) {
            this._unlisten();
        }
    }
    isKeyPressed(key) {
        const keyCode = keyCode_1.keyToKeyCode[key];
        return !!keyCode && this._pressed.indexOf(keyCode) > -1;
    }
    _filterShortcuts(type) {
        const filtered = [];
        for (const id in this._shortcuts) {
            const shortcutGroup = this._shortcuts[id];
            const currentCombo = this.getCurrentCombo();
            const currentComboStr = currentCombo.join('+');
            for (const shortcut of shortcutGroup) {
                let match = false;
                if ((type === 'keydown' && shortcut.keydown) ||
                    (type === 'keyup' && shortcut.keyup)) {
                    let { combo } = shortcut;
                    const { strict } = shortcut;
                    combo = Array.isArray(combo) ? combo : [combo];
                    for (const c of combo) {
                        if (c === currentComboStr ||
                            (strict === false && currentCombo.includes(c))) {
                            match = true;
                            break;
                        }
                    }
                }
                if (match) {
                    filtered.push(shortcut);
                }
            }
        }
        return filtered;
    }
    _remove(keyCode) {
        const index = this._pressed.indexOf(keyCode);
        if (index > -1) {
            this._pressed.splice(index, 1);
        }
    }
    _isSystemCombo(event) {
        const { key, ctrlKey, metaKey } = event;
        if ((metaKey || ctrlKey) && ['c', 'v', 'x', 'z'].includes(key)) {
            return true;
        }
        return false;
    }
    getCurrentCombo() {
        return this._pressed.map((c) => {
            const key = keyCode_1.keyCodeToKey[c];
            return key;
        });
    }
    _comboToKeyCodes(combo) {
        return combo.split('+').map((key) => keyCode_1.keyToKeyCode[key]);
    }
}
KeyboardController.keys = keyCode_1.keyCodeToKey;
exports.default = KeyboardController;
function makeShortcutListener(shortcuts, _global = false) {
    return (component) => {
        return listenShortcut(component, shortcuts, _global);
    };
}
exports.makeShortcutListener = makeShortcutListener;
function listenShortcut(component, shortcuts, _global = false) {
    const { $element, $system } = component;
    const $keyboard = new KeyboardController($system, $element);
    const shortcutGroupId = $keyboard.addShortcutGroup(shortcuts);
    return () => {
        $keyboard.removeShortcutGroup(shortcutGroupId);
    };
}
exports.listenShortcut = listenShortcut;
function listenKeydown(component, onKeydown) {
    const { $element } = component;
    const keydownListener = (_event) => {
        const { key, keyCode, ctrlKey, shiftKey, altKey, metaKey, repeat } = _event;
        onKeydown &&
            onKeydown({ key, keyCode, ctrlKey, shiftKey, altKey, metaKey, repeat }, _event);
    };
    $element.addEventListener('keydown', keydownListener);
    return () => {
        $element.removeEventListener('keydown', keydownListener);
    };
}
exports.listenKeydown = listenKeydown;
function listenKeypress(component, onKeydown) {
    const { $element } = component;
    const keypressListener = (_event) => {
        const { key, keyCode, ctrlKey, shiftKey, altKey, metaKey, repeat } = _event;
        onKeydown &&
            onKeydown({ key, keyCode, ctrlKey, shiftKey, altKey, metaKey, repeat }, _event);
    };
    $element.addEventListener('keypress', keypressListener);
    return () => {
        $element.removeEventListener('keypress', keypressListener);
    };
}
exports.listenKeypress = listenKeypress;
function makeKeydownListener(listener) {
    return (component) => {
        return listenKeydown(component, listener);
    };
}
exports.makeKeydownListener = makeKeydownListener;
function makeKeypressListener(listener) {
    return (component) => {
        return listenKeypress(component, listener);
    };
}
exports.makeKeypressListener = makeKeypressListener;
function listenKeyup(component, onKeyup) {
    const { $element } = component;
    const keyupListener = (_event) => {
        const { key, keyCode, ctrlKey, shiftKey, altKey, metaKey, repeat } = _event;
        onKeyup &&
            onKeyup({ key, keyCode, ctrlKey, shiftKey, altKey, metaKey, repeat }, _event);
    };
    $element.addEventListener('keyup', keyupListener);
    return () => {
        $element.removeEventListener('keyup', keyupListener);
    };
}
exports.listenKeyup = listenKeyup;
function makeKeyupListener(listener) {
    return (component) => {
        return listenKeyup(component, listener);
    };
}
exports.makeKeyupListener = makeKeyupListener;
const codeToKey = {
    Backquote: ['`', '~', '`', '`'],
    Digit1: ['1', '!', '¡', '⁄'],
    Digit2: ['2', '@', '™', '€'],
    Digit3: ['3', '#', '£', '‹'],
    Digit4: ['4', '$', '¢', '›'],
    Digit5: ['5', '%', '∞', 'ﬁ'],
    Digit6: ['6', '^', '§', 'ﬂ'],
    Digit7: ['7', '&', '¶', '‡'],
    Digit8: ['8', '*', '•', '°'],
    Digit9: ['9', '(', 'ª', '·'],
    Digit0: ['0', ')', 'º', '‚'],
    Minus: ['-', '_', '–', '—'],
    Equal: ['=', '+', '≠', '±'],
    Backspace: ['Backspace', 'Backspace', 'Backspace', 'Backspace'],
    Tab: ['Tab', 'Tab', 'Tab', 'Tab'],
    KeyQ: ['q', 'Q', 'œ', 'Œ'],
    KeyW: ['w', 'W', '∑', '„'],
    KeyE: ['e', 'E', '´', '´'],
    KeyR: ['r', 'R', '®', '‰'],
    KeyT: ['t', 'T', '†', 'ˇ'],
    KeyY: ['y', 'Y', '¥', 'Á'],
    KeyU: ['u', 'U', '¨', '¨'],
    KeyI: ['i', 'I', 'ˆ', 'ˆ'],
    KeyO: ['o', 'O', 'ø', 'Ø'],
    KeyP: ['p', 'P', 'π', '∏'],
    BracketLeft: ['[', '{', '“', '”'],
    BracketRight: [']', '}', '‘', '’'],
    Backlash: ['\\', '|', '«', '»'],
    CapsLock: ['CapsLock', 'CapsLock', 'CapsLock', 'CapsLock'],
    KeyA: ['a', 'A', 'å', 'Å'],
    KeyS: ['s', 'S', 'ß', 'Í'],
    KeyD: ['d', 'D', '∂', 'Î'],
    KeyF: ['f', 'F', 'ƒ', 'Ï'],
    KeyG: ['g', 'G', '©', '˝'],
    KeyH: ['h', 'H', '˙', 'Ó'],
    KeyJ: ['j', 'J', '∆', 'Ô'],
    KeyK: ['k', 'K', '˚', ''],
    KeyL: ['l', 'L', '¬', 'Ò'],
    Semicolon: [';', ':', '…', 'Ú'],
    Quote: ["'", '"', 'æ', 'Æ'],
    Enter: ['Enter', 'Enter', 'Enter', 'Enter'],
    ShiftLeft: ['Shift', 'Shift', 'Shift', 'Shift'],
    KeyZ: ['z', 'Z', 'Ω', '¸'],
    KeyX: ['x', 'X', '≈', '˛'],
    KeyC: ['c', 'C', 'ç', 'Ç'],
    KeyV: ['v', 'V', '√', '◊'],
    KeyB: ['b', 'B', '∫', 'ı'],
    KeyN: ['n', 'N', '˜', '˜'],
    KeyM: ['m', 'M', 'µ', 'Â'],
    Comma: [',', '<', '≤', '¯'],
    Slash: ['/', '?', '÷', '¿'],
    Period: ['.', '>', '≥', '˘'],
    ShiftRight: ['Shift', 'Shift', 'Shift', 'Shift'],
    ControlLeft: ['Control', 'Control', 'Control', 'Control'],
    AltLeft: ['Alt', 'Alt', 'Alt', 'Alt'],
    MetaLeft: ['Meta', 'Meta', 'Meta', 'Meta'],
    Space: [' ', ' ', ' ', ' '],
    MetaRight: ['Meta', 'Meta', 'Meta', 'Meta'],
    AltRight: ['Alt', 'Alt', 'Alt', 'Alt'],
    ArrowLeft: ['ArrowLeft', 'ArrowLeft', 'ArrowLeft', 'ArrowLeft'],
    ArrowUp: ['ArrowUp', 'ArrowUp', 'ArrowUp', 'ArrowUp'],
    ArrowDown: ['ArrowDown', 'ArrowDown', 'ArrowDown', 'ArrowDown'],
    ArrowRight: ['ArrowRight', 'ArrowRight', 'ArrowRight', 'ArrowRight'],
};
function isKeyPressed(system, key) {
    const { input } = system;
    const { keyboard } = input;
    const { pressed } = keyboard;
    const keyCode = keyCode_1.keyToKeyCode[key];
    return pressed.indexOf(keyCode) > -1;
}
exports.isKeyPressed = isKeyPressed;
function isKeyRepeat($system) {
    const { input: $input } = $system;
    const { keyboard: $keyboard } = $input;
    const { repeat: $repeat } = $keyboard;
    return $repeat;
}
exports.isKeyRepeat = isKeyRepeat;
function isShiftPressed($system) {
    return isKeyPressed($system, 'Shift');
}
exports.isShiftPressed = isShiftPressed;
function isAltPressed($system) {
    return isKeyPressed($system, 'Alt');
}
exports.isAltPressed = isAltPressed;
exports.keyToIcon = {
    Enter: 'enter',
    Shift: 'shift',
    Backspace: 'backspace',
    ArrowDown: 'arrow-down',
    ArrowLeft: 'arrow-left',
    ArrowRight: 'arrow-right',
    ArrowUp: 'arrow-up',
    AltLeft: 'option',
    AltRight: 'option',
    CtrlRight: 'chevron-up',
    CtrlLeft: 'chevron-up',
    MetaLeft: 'cmd',
};
//# sourceMappingURL=index.js.map