"use strict";
/*---------------------------------------------------------------------------------------------
 * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
 * See LICENSE.md in the project root for license terms and full copyright notice.
 *--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module Popup
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.KeyinPalettePanel = exports.clearKeyinPaletteHistory = void 0;
// cSpell:ignore focusvalue uiadmin
const React = require("react");
const ts_key_enum_1 = require("ts-key-enum");
require("./KeyinPalettePanel.scss");
const core_react_1 = require("@itwin/core-react");
const core_frontend_1 = require("@itwin/core-frontend");
const UiFramework_1 = require("../UiFramework");
const KeyinPaletteTools_1 = require("../tools/KeyinPaletteTools");
const useUiStateStorage_1 = require("../uistate/useUiStateStorage");
const itwinui_react_1 = require("@itwin/itwinui-react");
const matchesWords_1 = require("../utils/matchesWords");
const KEYIN_PALETTE_NAMESPACE = "KeyinPalettePanel";
const KEYIN_HISTORY_KEY = "historyArray";
/** @internal */
function clearKeyinPaletteHistory() {
    const uiSettingsStorage = UiFramework_1.UiFramework.getUiStateStorage();
    // istanbul ignore else
    if (uiSettingsStorage) {
        void uiSettingsStorage.deleteSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY);
    }
}
exports.clearKeyinPaletteHistory = clearKeyinPaletteHistory;
/**
 * @internal
 */
function KeyinPalettePanel({ keyins, onKeyinExecuted, historyLength: allowedHistoryLength = 6, }) {
    const [currentKeyin, setCurrentKeyin] = React.useState("");
    const placeholderLabel = React.useRef(UiFramework_1.UiFramework.translate("keyinbrowser.placeholder"));
    const inputRef = React.useRef(null);
    const keyinSeparator = "--#--";
    const [historyKeyins, setHistoryKeyins] = React.useState([]);
    const uiSettingsStorage = (0, useUiStateStorage_1.useUiStateStorageHandler)();
    React.useEffect(() => {
        async function fetchState() {
            const settingsResult = await uiSettingsStorage.getSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY);
            // istanbul ignore else
            if (core_react_1.UiStateStorageStatus.Success === settingsResult.status) {
                const filteredHistory = settingsResult.setting.filter((keyin) => {
                    const result = core_frontend_1.IModelApp.tools.parseKeyin(keyin);
                    return result.ok;
                });
                setHistoryKeyins(filteredHistory);
            }
            else {
                setHistoryKeyins([]);
            }
        }
        void fetchState();
    }, [uiSettingsStorage]);
    // istanbul ignore next
    const storeHistoryKeyins = React.useCallback(async (value) => {
        const result = await uiSettingsStorage.saveSetting(KEYIN_PALETTE_NAMESPACE, KEYIN_HISTORY_KEY, value);
        if (result.status !== core_react_1.UiStateStorageStatus.Success) {
            const briefMessage = UiFramework_1.UiFramework.translate("keyinbrowser.couldNotSaveHistory");
            const errorDetails = new core_frontend_1.NotifyMessageDetails(core_frontend_1.OutputMessagePriority.Error, briefMessage);
            core_frontend_1.IModelApp.notifications.outputMessage(errorDetails);
        }
    }, [uiSettingsStorage]);
    const allKeyins = React.useMemo(() => {
        const availableKeyins = [];
        historyKeyins.forEach((value) => {
            availableKeyins.push({ value, isHistory: true });
        });
        availableKeyins.push(...keyins.sort((a, b) => a.value.toLowerCase().localeCompare(b.value.toLowerCase())));
        return availableKeyins;
    }, [historyKeyins, keyins]);
    const submitKeyin = React.useCallback(async (value) => {
        let detailedMessage;
        let message;
        try {
            switch (await core_frontend_1.IModelApp.tools.parseAndRun(value)) {
                case core_frontend_1.ParseAndRunResult.ToolNotFound:
                    message = `UiFramework.translate("keyinbrowser.couldNotFindTool")} ${value}`;
                    break;
                // istanbul ignore next
                case core_frontend_1.ParseAndRunResult.BadArgumentCount:
                    message = UiFramework_1.UiFramework.translate("keyinbrowser.incorrectArgs");
                    break;
                // istanbul ignore next
                case core_frontend_1.ParseAndRunResult.FailedToRun:
                    message = UiFramework_1.UiFramework.translate("keyinbrowser.failedToRun");
                    break;
            }
        }
        catch (ex) {
            // istanbul ignore next
            {
                message = UiFramework_1.UiFramework.translate("keyinbrowser.exceptionOccurred");
                detailedMessage = `${UiFramework_1.UiFramework.translate("keyinbrowser.exceptionOccurred")}: ${ex}`;
            }
        }
        // istanbul ignore else
        if (undefined !== message) {
            const errorDetails = new core_frontend_1.NotifyMessageDetails(core_frontend_1.OutputMessagePriority.Error, message, detailedMessage, core_frontend_1.OutputMessageType.Sticky);
            core_frontend_1.IModelApp.notifications.outputMessage(errorDetails);
        }
        else {
            // istanbul ignore next
            if (value.length < 400 &&
                value !== KeyinPaletteTools_1.ClearKeyinPaletteHistoryTool.keyin &&
                value !== KeyinPaletteTools_1.ClearKeyinPaletteHistoryTool.englishKeyin) {
                const newHistoryEntries = [value];
                for (const entry of historyKeyins) {
                    if (entry !== value) {
                        newHistoryEntries.push(entry);
                        if (newHistoryEntries.length >= allowedHistoryLength)
                            break;
                    }
                }
                await storeHistoryKeyins(newHistoryEntries);
            }
            // close the popup by processing the supplied onKeyinExecuted function
            if (onKeyinExecuted)
                onKeyinExecuted(value);
        }
    }, [storeHistoryKeyins, onKeyinExecuted, historyKeyins, allowedHistoryLength]);
    const selectKeyin = React.useCallback(() => {
        // istanbul ignore else
        if (inputRef.current) {
            inputRef.current.focus();
            inputRef.current.setSelectionRange(inputRef.current.value.length, inputRef.current.value.length);
        }
    }, [inputRef]);
    const updateKeyin = React.useCallback((value) => {
        setCurrentKeyin(value);
        selectKeyin();
    }, [selectKeyin]);
    const getKeyinFromListboxValue = (value) => {
        // istanbul ignore else
        if (value) {
            const indexSeparator = value.search(keyinSeparator);
            // istanbul ignore else
            if (indexSeparator)
                return value.substring(0, indexSeparator);
        }
        // istanbul ignore next
        return undefined;
    };
    const handleKeypressOnKeyinsList = React.useCallback(async (event) => {
        var _a, _b;
        const key = event.key;
        // built into listbox a "Space" key will trigger the selection of a entry so if an Enter key is pressed
        // assume user wants to execute the entry.
        // istanbul ignore else
        if (key === ts_key_enum_1.Key.Enter) {
            event.preventDefault();
            // istanbul ignore next
            const keyinToSend = getKeyinFromListboxValue((_b = (_a = event.currentTarget) === null || _a === void 0 ? void 0 : _a.dataset) === null || _b === void 0 ? void 0 : _b.focusvalue);
            // istanbul ignore else
            if (keyinToSend) {
                if (event.ctrlKey)
                    updateKeyin(keyinToSend);
                else
                    await submitKeyin(keyinToSend);
            }
        }
    }, [submitKeyin, updateKeyin]);
    const onListboxValueChange = React.useCallback(async (value, isControlOrCommandPressed) => {
        const keyin = getKeyinFromListboxValue(value);
        // istanbul ignore else
        if (keyin) {
            if (isControlOrCommandPressed)
                updateKeyin(keyin);
            else
                await submitKeyin(keyin);
        }
    }, [submitKeyin, updateKeyin]);
    const onInputValueChange = React.useCallback((event) => {
        setCurrentKeyin(event.target.value);
    }, []);
    const filteredKeyins = React.useMemo(() => {
        const filteredHistory = [];
        if (undefined === currentKeyin || 0 === currentKeyin.length) {
            return allKeyins;
        }
        else {
            const newKeyinSet = [];
            allKeyins.forEach((value) => {
                if (value.value.length >= currentKeyin.length) {
                    // Force contiguous searches if key-in is over 60 characters long for performance.
                    const matches = (0, matchesWords_1.matchesWords)(currentKeyin, value.value, currentKeyin.length > 60);
                    if (matches && matches.length) {
                        // istanbul ignore else
                        if (value.isHistory) {
                            filteredHistory.push(value);
                            newKeyinSet.push({ ...value, matches });
                        }
                        else {
                            // only add entry if no match in filtered history
                            // istanbul ignore else
                            if (-1 ===
                                filteredHistory.findIndex((historyEntry) => historyEntry.value === value.value))
                                newKeyinSet.push({ ...value, matches });
                        }
                    }
                }
            });
            return newKeyinSet;
        }
    }, [allKeyins, currentKeyin]);
    const onInputValueKeyDown = React.useCallback(async (event) => {
        if (ts_key_enum_1.Key.Enter === event.key) {
            event.preventDefault();
            event.stopPropagation();
            // istanbul ignore else
            if (1 === filteredKeyins.length) {
                if (event.ctrlKey)
                    updateKeyin(filteredKeyins[0].value);
                else
                    await submitKeyin(filteredKeyins[0].value);
            }
            else {
                if (currentKeyin)
                    await submitKeyin(currentKeyin);
            }
        }
        else if (ts_key_enum_1.Key.Tab === event.key && 1 === filteredKeyins.length) {
            event.preventDefault();
            event.stopPropagation();
            updateKeyin(filteredKeyins[0].value);
        }
        else {
            if (event.key === ts_key_enum_1.Key.ArrowDown && filteredKeyins.length > 0) {
                event.preventDefault();
                event.stopPropagation();
                // istanbul ignore else
                if (inputRef.current) {
                    const nextElement = inputRef.current
                        .nextElementSibling;
                    nextElement && nextElement.focus();
                }
            }
        }
    }, [filteredKeyins, submitKeyin, currentKeyin, updateKeyin]);
    const lastHistoryIndex = filteredKeyins.findIndex((entry) => true !== entry.isHistory) - 1;
    return (React.createElement("div", { className: "uifw-command-palette-panel" },
        React.createElement(itwinui_react_1.Input, { ref: inputRef, type: "text", onKeyDown: onInputValueKeyDown, className: "uifw-command-palette-input", "data-testid": "command-palette-input", onChange: onInputValueChange, placeholder: placeholderLabel.current, value: currentKeyin, size: "small" }),
        filteredKeyins.length > 0 && (React.createElement(core_react_1.Listbox, { id: "uifw-command-sources", className: "map-manager-source-list", onKeyDown: handleKeypressOnKeyinsList, onListboxValueChange: onListboxValueChange }, filteredKeyins.map((entry, index) => {
            const value = `${entry.value}${keyinSeparator}${entry.isHistory ? "history" : "registry"}`;
            const itemClass = `uifw-command-palette-value-entry${index === lastHistoryIndex ? " uifw-history-bottom-border" : ""}`;
            return (React.createElement(core_react_1.ListboxItem, { key: `${entry.value}-${index}`, className: itemClass, value: value },
                React.createElement(core_react_1.FilteredText, { value: entry.value, matches: entry.matches })));
        })))));
}
exports.KeyinPalettePanel = KeyinPalettePanel;
//# sourceMappingURL=KeyinPalettePanel.js.map