/*
 * Copyright 2020 The Kubernetes Authors
 *
 * 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.
 */
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import React from 'react';
import { Page } from '@patternfly/react-core/dist/esm/components/Page';
import { Events, isReadOnlyClient, pexecInCurrentTab } from '@kui-shell/core';
import Sidebar from './Sidebar';
import TabModel, { uuidForFirstTab } from './TabModel';
import TabContent from './TabContent';
import TopTabStripe from './TopTabStripe';
import '../../../web/css/static/TabContainer.scss';
import '../../../web/scss/components/Page/_index.scss';
export default class TabContainer extends React.PureComponent {
    constructor(props) {
        super(props);
        /** Has the first tab activated itself? */
        this.isFirstTabReady = false;
        this._onSwitchTab = this.onSwitchTab.bind(this);
        this._onCloseTab = this.onCloseTab.bind(this);
        this._onNewTab = () => this.onNewTab();
        this._onTabReady = this.onTabReady.bind(this);
        this._onSetTabTitle = (uuid, title) => {
            this.setState(({ tabs }) => {
                const tabIdx = tabs.findIndex(_ => _.uuid === uuid);
                if (tabIdx < 0 || tabs[tabIdx].title === title) {
                    return null;
                }
                else {
                    return {
                        tabs: [...tabs.slice(0, tabIdx), tabs[tabIdx].setTitle(title), ...tabs.slice(tabIdx + 1)]
                    };
                }
            });
        };
        this.toggleSidebar = () => this.setState(curState => ({ isSidebarOpen: !curState.isSidebarOpen }));
        this.state = {
            isSidebarOpen: this.props.guidebooksExpanded || false,
            tabs: [this.newTabModel(undefined, undefined, uuidForFirstTab().toString())],
            activeIdx: 0
        };
    }
    componentDidMount() {
        Events.eventBus.on('/tab/new/request', (evt) => {
            // the protocol is: if we are opening multiple tabs in the
            // "foreground", then make sure the *first* of the new tabs is
            // active
            const weWillAssertActiveTab = !evt.background && evt.tabs.length > 1;
            evt.tabs.forEach((spec, idx) => {
                // re: the `&& idx > 0` part: we do want the *first* tab to assert activeness
                const doNotChangeActiveTab = evt.background || (weWillAssertActiveTab && idx > 0);
                this.onNewTab(spec, doNotChangeActiveTab);
            });
            if (weWillAssertActiveTab) {
                // here is where make sure the *first* of the new tabs is
                // active
                this.setState(curState => ({
                    activeIdx: curState.tabs.length - evt.tabs.length
                }));
            }
        });
        Events.eventBus.on('/tab/switch/request', (idx) => {
            this.onSwitchTab(idx);
        });
        Events.eventBus.on('/kui/tab/edit/toggle/index', (idx) => {
            const tab = this.state.tabs[idx];
            if (tab && tab.uuid) {
                Events.eventBus.emitWithTabId('/kui/tab/edit/toggle', tab.uuid);
            }
        });
    }
    /** save tab state such as CWD prior to a tab switch */
    captureState() {
        try {
            this.state.tabs[this.state.activeIdx].state.capture();
        }
        catch (err) {
            console.error(err);
        }
    }
    /** restore tab state after a tab switch */
    restoreState(tabIdx) {
        this.state.tabs[tabIdx].state.restore();
    }
    /**
     * Switch Tab event: update state so that activeIdx=idx
     *
     */
    onSwitchTab(idx) {
        return __awaiter(this, void 0, void 0, function* () {
            // capture current state, restore state of the switched-to tab
            const currentTabState = this.state.tabs[this.state.activeIdx].state;
            const nextTabState = this.state.tabs[idx].state;
            yield currentTabState.switchTo(nextTabState);
            if (idx >= 0 && idx < this.state.tabs.length) {
                this.setState({
                    activeIdx: idx
                });
            }
            setTimeout(() => Events.eventBus.emit('/tab/switch/request/done', { idx, tab: nextTabState }));
        });
    }
    /**
     * Close Tab event
     *
     */
    onCloseTab(idx) {
        return __awaiter(this, void 0, void 0, function* () {
            // execute a command onClose?
            const tabModel = this.state.tabs[idx];
            if (tabModel && tabModel.onClose) {
                try {
                    yield pexecInCurrentTab(tabModel.onClose, undefined, true);
                }
                catch (err) {
                    console.error('Error executing tab onClose handler', err);
                }
            }
            const residualTabs = this.state.tabs.slice(0, idx).concat(this.state.tabs.slice(idx + 1));
            if (residualTabs.length > 0) {
                const activeIdx = idx === 0 ? 0 : idx - 1;
                this.restoreState(activeIdx);
                this.setState({
                    tabs: residualTabs,
                    activeIdx
                });
            }
        });
    }
    listenForTabClose(model) {
        Events.eventBus.onceWithTabId('/tab/close/request', model.uuid, (uuid, tab) => __awaiter(this, void 0, void 0, function* () {
            if (this.state.tabs.length === 1) {
                // then we are closing the last tab, so close the window
                tab.REPL.qexec('window close');
            }
            else {
                this.onCloseTab(this.state.tabs.findIndex(_ => _.uuid === model.uuid));
            }
        }));
    }
    newTabModel(spec = {}, doNotChangeActiveTab = false, uuid) {
        // !this.state means: if this is the very first tab we've ever
        // !created, *and* we were given an initial title (via
        // !this.props.title), then use that
        const model = new TabModel(uuid, spec.statusStripeDecoration, doNotChangeActiveTab, spec.title || this.props.title, undefined, undefined, spec.cmdline, spec.onClose, spec.exec);
        this.listenForTabClose(model);
        return model;
    }
    /**
     * New Tab event
     *
     */
    onNewTab(spec = {}, doNotChangeActiveTab = false) {
        // if we already have a tab with this title, and this isn't a
        // background tab, then switch to it
        if (spec.title) {
            const existingIdx = this.state.tabs.findIndex(_ => _.title === spec.title);
            if (existingIdx >= 0) {
                if (!doNotChangeActiveTab) {
                    this.onSwitchTab(existingIdx);
                }
                return;
            }
        }
        this.captureState();
        const model = this.newTabModel(spec, doNotChangeActiveTab);
        this.setState(curState => ({
            tabs: curState.tabs.concat(model),
            activeIdx: !doNotChangeActiveTab ? curState.tabs.length : curState.activeIdx
        }));
    }
    graft(node, uuid, key) {
        if (React.isValidElement(node)) {
            // ^^^ this check avoids tsc errors
            return React.cloneElement(node, {
                key,
                uuid
            });
        }
        else {
            return node;
        }
    }
    /** Graft the tab `uuid` */
    children(uuid) {
        if (Array.isArray(this.props.children)) {
            return this.props.children.map((child, idx) => this.graft(child, uuid, idx));
        }
        else {
            return this.graft(this.props.children, uuid);
        }
    }
    willUpdateTopTabButtons(uuid, buttons) {
        this.setState(curState => {
            const idx = curState.tabs.findIndex(_ => _.uuid === uuid);
            if (idx >= 0) {
                return {
                    tabs: curState.tabs
                        .slice(0, idx)
                        .concat([curState.tabs[idx].update(buttons)])
                        .concat(curState.tabs.slice(idx + 1))
                };
            }
        });
    }
    onTabReady(tab) {
        // "initial tab" handling
        if (!this.isFirstTabReady) {
            if (this.props.onTabReady) {
                this.props.onTabReady(tab);
            }
            this.isFirstTabReady = true;
        }
    }
    /** Render the row of Tabs along the top */
    topTabStripe() {
        return (React.createElement(TopTabStripe, { tabs: this.state.tabs, onNewTab: this._onNewTab, onCloseTab: this._onCloseTab, onSwitchTab: this._onSwitchTab, activeIdx: this.state.activeIdx, topTabNames: this.props.topTabNames, needsSidebar: this.needsSidebar, isSidebarOpen: this.state.isSidebarOpen, onToggleSidebar: this.toggleSidebar, noTopTabs: this.props.noTopTabs, noNewTabButton: this.props.noNewTabButton, noNewSplitButton: this.props.noNewSplitButton, closeableTabs: this.state.tabs.length > 1 && (this.props.closeableTabs || !isReadOnlyClient()) }));
    }
    /** Render the content of the tabs */
    tabContent() {
        return (React.createElement("div", { className: "tab-container" }, this.state.tabs.map((_, idx) => (React.createElement(TabContent, Object.assign({}, this.props, { tabTitle: _.title, key: _.uuid, uuid: _.uuid, active: idx === this.state.activeIdx, initialCommandLine: _.initialCommandLine, onTabReady: this._onTabReady, willSetTitle: this._onSetTabTitle, state: _.state }), this.children(_.uuid))))));
    }
    get needsSidebar() {
        return !!this.props.guidebooks;
    }
    sidebar() {
        return (React.createElement(Sidebar, { version: this.props.version, isOpen: this.state.isSidebarOpen, toggleOpen: this.toggleSidebar, noTopTabs: this.props.noTopTabs, guidebooks: this.props.guidebooks, productName: this.props.productName, indicateActiveItem: !!this.props.noTopTabs, guidebooksCommand: this.props.guidebooksCommand }));
    }
    render() {
        return (React.createElement(Page, { mainContainerId: "kui--page-main", className: "kui--tab-container-page", header: this.topTabStripe(), sidebar: this.sidebar(), "data-sidebar-open": this.state.isSidebarOpen || undefined }, this.tabContent()));
    }
}
//# sourceMappingURL=TabContainer.js.map