/*
 * 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.
 */
import Debug from 'debug';
import React from 'react';
import { Tabs, Tab, TabTitleText } from '@patternfly/react-core/dist/esm/components/Tabs';
import { eventChannelUnsafe } from '@kui-shell/core/mdist/api/Events';
import { isButton, isResourceByReference, isResourceWithMetadata } from '@kui-shell/core/mdist/api/Response';
import { addRelevantModes, badgeRegistrar } from '@kui-shell/core/mdist/api/Sidecar';
import Badge from './Badge';
import KuiContext from '../../Client/context';
import ToolbarContainer from './ToolbarContainer';
import Toolbar from './Toolbar';
import BaseSidecar from './BaseSidecarV2';
import '../../../../web/css/static/ToolbarButton.scss';
import '../../../../web/scss/components/Sidecar/PatternFly.scss';
/** Lazily load KuiContent; see https://github.com/IBM/kui/issues/3746 */
const KuiContent = React.lazy(() => import('../../Content/KuiContent'));
const debug = Debug('plugin-sidecar/components/TopNavSidecar');
export function getStateFromMMR(tab, response) {
    let allModes = response.modes.slice(0);
    // consult the view registrar for registered view modes
    // relevant to this resource
    const cmd = '';
    if (isResourceWithMetadata(response)) {
        addRelevantModes(tab, allModes, cmd, { resource: response });
    }
    else if (isResourceByReference(response)) {
        addRelevantModes(tab, allModes, cmd, response);
    }
    // obey the `order` constraints of the modes
    allModes = allModes.sort((a, b) => {
        return (a.order || 0) - (b.order || 0);
    });
    // defaultMode: if the response specified one, then look for it;
    // otherwise, use the mode that considers itself default;
    // otherwise use the first
    const tabs = allModes.filter(_ => !isButton(_));
    const defaultModeFromResponse = response.defaultMode ? tabs.findIndex(_ => _.mode === response.defaultMode) : -1;
    const defaultModeFromModel = defaultModeFromResponse === -1 ? tabs.findIndex(_ => _.defaultMode) : defaultModeFromResponse;
    const defaultMode = defaultModeFromModel === -1 ? 0 : defaultModeFromModel;
    // re: as any: yay tsc, there are several open issue for this;
    // it's related to isButton using generics
    const buttonsFromRegistrar = allModes.filter(isButton);
    const buttonsFromResponse = response.buttons || [];
    const buttons = buttonsFromResponse.concat(buttonsFromRegistrar);
    // toolbarText: if the default mode specified one, then use it;
    // otherwise, use the one specified by response
    const toolbarText = (tabs[defaultMode] && tabs[defaultMode].toolbarText) || response.toolbarText;
    return {
        currentTabIndex: defaultMode,
        defaultMode,
        tabs,
        toolbarText,
        viewButtons: buttons.filter(_ => !(_.kind === 'drilldown' && _.showRelatedResource)),
        drilldownButtons: buttons.filter(_ => _.kind === 'drilldown' && _.showRelatedResource)
    };
}
/**
 *
 * TopNavSidecar
 * -----------------------
 * | <TitleBar/>         |
 * -----------------------
 * | nameHash?           |
 * | name                |
 * |---------------------|
 * | Tab | Tab |  ...    | <Tab/> from here down  - this is header()
 * |---------------------|                  |
 * | <Toolbar/>          |              <ToolbarContainer/> from here down
 * |---------------------|                  |                        |
 * | <KuiContent/>       |                  |                        |
 * |                     |               to here                  to here
 * -----------------------
 * | <Toolbar/>          | this is footer()
 * -----------------------
 */
export default class TopNavSidecar extends BaseSidecar {
    constructor(props) {
        super(props);
        this._didUpdateToolbar = this.didUpdateToolbar.bind(this);
        this._onSelect = this.onSelect.bind(this);
        this._bodyContentMemo = [];
        this._style = { flexDirection: 'column' };
        this.state = TopNavSidecar.getDerivedStateFromProps(props);
    }
    /** @return a `HistoryEntry` for the given `Response` */
    static getDerivedStateFromProps(props, state) {
        const { tab, response } = props;
        if (!state || state.response !== response) {
            const args = {
                argsForMode: response.argsForMode,
                argvNoOptions: props.argvNoOptions,
                parsedOptions: props.parsedOptions
            };
            return Object.assign(state || {}, { response, toolbarText: response.toolbarText, args }, getStateFromMMR(tab, response));
        }
        else {
            return state;
        }
    }
    headerBodyStyle() {
        return { 'flex-direction': 'column' };
    }
    /** return the pretty name or unadulterated name from the response */
    prettyName() {
        return (this.state.response &&
            (this.state.response.prettyName || (this.state.response.metadata ? this.state.response.metadata.name : undefined)));
    }
    /** ToolbarContainer updated the toolbar */
    didUpdateToolbar(toolbarText) {
        this.setState({ toolbarText });
    }
    /** Tell the world that we have changed the focused mode */
    broadcastFocusChange(idx) {
        // de-focus the old mode
        const oldMode = this.current.tabs[this.current.currentTabIndex];
        eventChannelUnsafe.emit(`/mode/focus/off/tab/${this.props.uuid}/mode/${oldMode.mode}`, oldMode);
        // re-focus the new mode
        const newMode = this.current.tabs[idx];
        eventChannelUnsafe.emit(`/mode/focus/on/tab/${this.props.uuid}/mode/${newMode.mode}`, newMode);
    }
    /** eventKey property for a Tab */
    eventKey(idx) {
        return idx;
    }
    /** idx from the encoded eventKey */
    idxFromEventKey(eventKey) {
        return eventKey;
    }
    /** User has changed selected Tab */
    onSelect(_, eventKey) {
        const idx = this.idxFromEventKey(eventKey);
        // tell the views that we have changed focus
        this.broadcastFocusChange(idx);
        this.setState(curState => {
            const toolbarText = curState.tabs[idx].toolbarText || curState.toolbarText;
            return Object.assign({}, curState, { currentTabIndex: idx, toolbarText });
        });
    }
    // first div used to be sidecar-top-stripe
    tabs(executable) {
        return (React.createElement("div", { className: "kui--sidecar-tabs-container zoomable full-height", onClick: this._stopPropagation },
            React.createElement(Tabs, { className: "sidecar-bottom-stripe-mode-bits sidecar-bottom-stripe-button-container kui--sidecar-tabs", activeKey: this.eventKey(this.current.currentTabIndex), onSelect: this._onSelect, mountOnEnter: true }, this.current.tabs.map((mode, idx) => (React.createElement(Tab, { key: mode.mode, id: mode.mode, eventKey: this.eventKey(idx), className: "sidecar-bottom-stripe-button kui--sidecar-tab", title: React.createElement(TabTitleText, { className: "kui--sidecar-tab-label" }, mode.label || mode.mode), "data-mode": mode.mode, "data-is-selected": idx === this.current.currentTabIndex || undefined, onMouseDown: this._preventDefault }, this.tabContent(idx, executable)))))));
    }
    bodyContent(idx) {
        if (!this._bodyContentMemo[idx]) {
            this._bodyContentMemo[idx] = (React.createElement(KuiContent, { tab: this.props.tab, mode: this.current.tabs[idx], isActive: true /* idx === this.current.currentTabIndex */, args: this.state.args, response: this.state.response, execUUID: this.props.execUUID }));
        }
        return this._bodyContentMemo[idx];
    }
    tabContent(idx, executable) {
        return (React.createElement("div", { className: "sidecar-content-container kui--tab-content", hidden: idx !== this.current.currentTabIndex || undefined },
            React.createElement("div", { className: "custom-content" },
                React.createElement(ToolbarContainer, { tab: this.props.tab, execUUID: this.props.execUUID, response: this.state.response, args: this.state.args, didUpdateToolbar: this._didUpdateToolbar, toolbarText: this.state.toolbarText, noAlerts: this.current.currentTabIndex !== this.current.defaultMode, buttons: executable ? this.current.viewButtons : [] }, this.bodyContent(idx)))));
    }
    /** Return a collection of badge elements */
    badges() {
        const badges = badgeRegistrar.filter(({ when }) => {
            // filter out any irrelevant badges (for this resource)
            try {
                return when(this.state.response);
            }
            catch (err) {
                debug('warning: registered badge threw an exception during filter', err);
                return false;
            }
        });
        return (badges &&
            badges.length > 0 && (React.createElement("div", { className: "badges" }, badges.map(({ badge }, idx) => (React.createElement(Badge, { key: idx, spec: badge, tab: this.props.tab, response: this.state.response }))))));
    }
    header() {
        const badges = this.badges();
        if (badges) {
            return (React.createElement("header", { className: "sidecar-header" },
                React.createElement("div", { className: "header-main-content" },
                    React.createElement("div", { className: "kui--sidecar-header-and-toolbar" },
                        React.createElement("div", { className: "header-top-bits" },
                            React.createElement("div", { className: "header-left-bits" }),
                            React.createElement("div", { className: "header-right-bits" },
                                React.createElement("div", { className: "custom-header-content" }, badges)))))));
        }
    }
    /**
     * The footer offers drilldown buttons that we assume depend on the
     * client supporting command execution. For example, a static single
     * page web app would not allow for this kind of functionality.
     *
     */
    footer() {
        return (this.props.executable && (React.createElement(Toolbar, { bottom: true, tab: this.props.tab, execUUID: this.props.execUUID, response: this.state.response, args: this.state.args, buttons: this.current.drilldownButtons })));
    }
    kindBreadcrumb() {
        const { kind, onclick } = this.state.response;
        return { label: kind, command: onclick && onclick.kind, className: 'kui--sidecar-kind' };
    }
    /** Show resource name as breadcrumb */
    nameBreadcrumb() {
        const { onclick } = this.state.response;
        return {
            label: this.prettyName(),
            command: onclick && onclick.name,
            isCurrentPage: true,
            className: 'kui--sidecar-entity-name'
        };
    }
    versionBreadcrumb() {
        return this.state.response.version
            ? { label: this.state.response.version, className: 'kui--version-breadcrumb' }
            : undefined;
    }
    nameHashBreadcrumb() {
        const { onclick } = this.state.response;
        return {
            label: this.state.response && this.state.response.nameHash,
            command: onclick && onclick.nameHash,
            deemphasize: true,
            className: 'kui--sidecar-entity-name-hash'
        };
    }
    namespaceBreadcrumb() {
        const { metadata: { namespace }, onclick } = this.state.response;
        return {
            label: namespace,
            command: onclick && onclick.namespace,
            deemphasize: true,
            className: 'kui--sidecar-entity-namespace'
        };
    }
    render() {
        if (!this.current || !this.state.response) {
            if (this.props.onRender) {
                this.props.onRender(false);
            }
            return React.createElement("div", null);
        }
        else if (this.props.onRender) {
            // needs to be async'd; see https://github.com/kubernetes-sigs/kui/issues/7539
            setTimeout(() => this.props.onRender(true));
        }
        const nameBreadCrumbs = this.context.sidecarName === 'breadcrumb'
            ? [this.nameBreadcrumb(), this.versionBreadcrumb(), this.nameHashBreadcrumb()]
            : [];
        try {
            const breadcrumbs = [this.kindBreadcrumb()]
                .concat(nameBreadCrumbs)
                .concat(this.namespaceBreadcrumb())
                .filter(_ => _);
            // Note: data-view helps with tests
            return (React.createElement("div", { className: "kui--sidecar kui--sidecar-nested visible", ref: this.dom, "data-view": "topnav", onClick: this._stopPropagation },
                this.title({
                    breadcrumbs
                }),
                React.createElement("div", { className: "kui--sidecar-header-and-body", style: this._style },
                    this.tabs(this.props.executable),
                    this.footer())));
        }
        catch (err) {
            console.error(err);
        }
    }
}
TopNavSidecar.contextType = KuiContext;
//# sourceMappingURL=TopNavSidecarV2.js.map