/*
 * Copyright 2020 IBM Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import * as React from 'react';
import { dots as spinnerFrames } from 'cli-spinners';
import { inBrowser, doCancel, i18n } from '@kui-shell/core';
import onPaste from './OnPaste';
import onKeyDown from './OnKeyDown';
import onKeyPress from './OnKeyPress';
import KuiContext from '../../../Client/context';
import { onKeyUp } from './ActiveISearch';
import { isActive, isProcessing, isFinished, hasCommand, isEmpty, isOops, hasUUID, hasValue } from './BlockModel';
import DropDown from '../../../spi/DropDown';
import Icons from '../../../spi/Icons';
const strings = i18n('plugin-client-common');
const strings2 = i18n('plugin-client-common', 'screenshot');
export class InputProvider extends React.PureComponent {
    /** rendered to the left of the input element */
    // eslint-disable-next-line @typescript-eslint/no-empty-function
    status() { }
    /** the "xxx" part of "xxx >" of the prompt */
    promptLeft() {
        return (!this.props.noPromptContext && (React.createElement(KuiContext.Consumer, null, config => !config.noPromptContext && this.props.model && React.createElement("span", { className: "repl-context" }, this.props.model.cwd))));
    }
    /** the ">" part of "xxx >" of the prompt */
    promptRight() {
        // &#x2771; "heavy right-pointing angle bracket ornament"
        // another option: &#x276f; "heavy right-pointing angle quotation mark ornament"
        return (React.createElement(KuiContext.Consumer, null, config => React.createElement("span", { className: "repl-prompt-righty" }, config.prompt || '/')));
    }
    isearchPrompt() {
        return React.createElement("div", { className: "repl-prompt" }, this.state.isearch.render());
    }
    normalPrompt() {
        return (React.createElement("div", { className: "repl-prompt" },
            this.promptLeft(),
            this.promptRight()));
    }
    /** the "xxx >" prompt part of the input section */
    prompt() {
        if (this.state && this.state.isearch && this.state.prompt) {
            try {
                return this.isearchPrompt();
            }
            catch (err) {
                console.error('error rendering i-search', err);
                return this.normalPrompt();
            }
        }
        else if (!this.props.isPinned) {
            return this.normalPrompt();
        }
    }
    render() {
        return (React.createElement("div", { className: 'repl-input' + (this.state && this.state.isearch ? ' kui--isearch-active' : '') },
            React.createElement("div", { className: "kui--input-and-context" },
                this.prompt(),
                this.props.children,
                this.input(),
                this.status()),
            this.state && this.state.tabCompletion && this.state.tabCompletion.render()));
    }
}
export default class Input extends InputProvider {
    constructor(props) {
        super(props);
        this.state = {
            execUUID: hasUUID(props.model) && props.model.execUUID,
            prompt: undefined,
            spinner: undefined
        };
    }
    /** @return the value of the prompt */
    value() {
        return this.state.prompt && this.state.prompt.value;
    }
    /** Owner wants us to focus on the current prompt */
    doFocus() {
        if (this.state.prompt) {
            this.state.prompt.focus();
        }
    }
    static newSpinner(spinnerDom) {
        let frame = 0;
        return setInterval(function () {
            frame = frame + 1 === spinnerFrames.frames.length ? 0 : frame + 1;
            spinnerDom.innerText = spinnerFrames.frames[frame];
        }, spinnerFrames.interval);
    }
    static updateSpinner(props, state) {
        const spinner = isProcessing(props.model)
            ? state.spinner || (state.spinnerDom && Input.newSpinner(state.spinnerDom))
            : undefined;
        if (!spinner && state.spinner) {
            clearInterval(state.spinner);
        }
        return spinner;
    }
    static getDerivedStateFromProps(props, state) {
        const spinner = Input.updateSpinner(props, state);
        if (hasUUID(props.model)) {
            return {
                spinner,
                execUUID: props.model.execUUID
            };
        }
        else if (state.prompt && isActive(props.model) && state.execUUID !== undefined && !state.isearch) {
            // e.g. terminal has been cleared; we need to excise the current
            // <input/> because react aggressively caches these
            return {
                spinner,
                prompt: undefined,
                execUUID: undefined
            };
        }
        return state;
    }
    /** the element that represents the command being/having been/going to be executed */
    input() {
        if (this.props.isPinned) {
            return;
        }
        const active = isActive(this.props.model);
        if (active) {
            setTimeout(() => this.state.prompt.focus());
            const kp = active && !this.state.isearch ? onKeyPress.bind(this) : undefined;
            const kd = active && !this.state.isearch ? onKeyDown.bind(this) : undefined;
            const ku = active ? onKeyUp.bind(this) : undefined;
            const op = active && !this.state.isearch ? evt => onPaste(evt.nativeEvent, this.props.tab, this.state.prompt) : undefined;
            return (React.createElement("input", { type: "text", autoFocus: true, autoCorrect: "off", autoComplete: "off", spellCheck: "false", autoCapitalize: "off", className: 'repl-input-element' + (this.state.isearch ? ' repl-input-hidden' : ''), "aria-label": "Command Input", tabIndex: 1, placeholder: this.props.promptPlaceholder, onBlur: this.props.onInputBlur, onFocus: this.props.onInputFocus, onMouseDown: this.props.onInputMouseDown, onMouseMove: this.props.onInputMouseMove, onChange: this.props.onInputChange, onClick: this.props.onInputClick, onKeyPress: evt => {
                    if (kp)
                        kp(evt);
                    this.props.onInputKeyPress && this.props.onInputKeyPress(evt);
                }, onKeyDown: evt => {
                    if (kd)
                        kd(evt);
                    this.props.onInputKeyDown && this.props.onInputKeyDown(evt);
                }, onKeyUp: evt => {
                    if (ku)
                        ku(evt);
                    this.props.onInputKeyUp && this.props.onInputKeyUp(evt);
                }, onPaste: op, ref: c => {
                    if (c && !this.state.prompt) {
                        c.value = hasValue(this.props.model) ? this.props.model.value : '';
                        this.setState({ prompt: c });
                    }
                } }));
        }
        else {
            const value = this.value() ||
                (hasValue(this.props.model)
                    ? this.props.model.value
                    : hasCommand(this.props.model)
                        ? this.props.model.command
                        : '');
            if (isProcessing(this.props.model)) {
                // for processing blocks, we still need an input, albeit
                // readOnly, to handle ctrl+C
                return (React.createElement("span", { className: "flex-layout flex-fill" },
                    React.createElement("input", { className: "repl-input-element", readOnly: true, value: value, onKeyDown: evt => {
                            if (evt.key === 'c' && evt.ctrlKey) {
                                doCancel(this.props.tab, this.props._block);
                            }
                        }, ref: c => c && c.focus() })));
            }
            else {
                // for "done" blocks, render the value as a plain div
                return React.createElement("div", { className: "repl-input-element" }, value);
            }
        }
    }
    /** render the time the block started processing */
    timestamp() {
        if (!this.props.isPinned &&
            !isEmpty(this.props.model) &&
            (isProcessing(this.props.model) || isFinished(this.props.model))) {
            return (this.props.model.startTime && (React.createElement("span", { className: "kui--repl-block-timestamp kui--repl-block-right-element" }, this.props.model.startTime.toLocaleTimeString())));
        }
    }
    /** spinner for processing blocks */
    spinner() {
        if (isProcessing(this.props.model)) {
            return (React.createElement("span", { className: "kui--repl-block-spinner", ref: spinnerDom => {
                    this.setState({ spinnerDom });
                } }));
        }
    }
    /** error icon for error blocks */
    errorIcon() {
        if (isOops(this.props.model)) {
            return React.createElement(Icons, { className: "kui--repl-block-error-icon", icon: "Error", "data-mode": "error" });
        }
    }
    removeAction() {
        return !this.props.willRemove || this.props.isPinned
            ? []
            : [
                {
                    label: strings('Remove'),
                    handler: () => this.props.willRemove()
                }
            ];
    }
    screenshotAction() {
        return !this.props.willScreenshot || inBrowser()
            ? []
            : [
                {
                    label: strings2('Screenshot'),
                    handler: () => this.props.willScreenshot()
                }
            ];
    }
    unPinnedAction() {
        return !this.props.isPinned
            ? []
            : [
                {
                    label: strings2('Show as table in terminal'),
                    handler: () => this.props.unPin()
                }
            ];
    }
    /** DropDown menu for completed blocks */
    dropdown() {
        if (!isActive(this.props.model)) {
            const actions = this.screenshotAction()
                .concat(this.unPinnedAction())
                .concat(this.removeAction());
            return (React.createElement(DropDown, { actions: actions, className: "kui--repl-block-right-element kui--toolbar-button-with-icon", onClose: this.props.willLoseFocus }));
        }
    }
    /** Close button. Only for pinned blocks for now. */
    close() {
        return (this.props.willRemove &&
            this.props.isPinned && (React.createElement("a", { href: "#", onClick: this.props.willRemove, className: "kui--repl-block-right-element kui--toolbar-button-with-icon kui--pinned-close-button", title: strings('Close watcher') },
            React.createElement(Icons, { icon: "WindowClose" }))));
    }
    /**
     * Status elements associated with the block; even though these
     * pertain to the Output part of a Block, these are currently placed
     * in the Input area.
     *
     */
    status() {
        return (React.createElement("span", { className: "repl-prompt-right-elements" },
            this.spinner(),
            this.errorIcon(),
            this.timestamp(),
            this.dropdown(),
            this.close()));
    }
}
//# sourceMappingURL=Input.js.map