"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _reactSplitPane = _interopRequireDefault(require("react-split-pane"));

var React = _interopRequireWildcard(require("react"));

var _core = require("@kui-shell/core");

var _Icons = _interopRequireDefault(require("../spi/Icons"));

var _context = _interopRequireDefault(require("./context"));

var _Confirm = _interopRequireDefault(require("../Views/Confirm"));

var _WatchPane = _interopRequireDefault(require("../Views/WatchPane"));

var _getSize = _interopRequireDefault(require("../Views/Terminal/getSize"));

var _ScrollableTerminal = _interopRequireDefault(require("../Views/Terminal/ScrollableTerminal"));

function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/*
 * 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.
 */
var __awaiter = void 0 && (void 0).__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());
  });
};

/**
 *
 * TabContent
 * ----------------  <Tab/> from here down
 * | ST  |        |
 * |     |        |
 * |     |        |
 * |     |        |
 * |     |        |
 * |     |        |
 * ----------------
 *  ST: <ScrollableTerminal/>
 *
 */
class TabContent extends React.PureComponent {
  constructor(props) {
    super(props);
    this.cleaners = [];
    /** switching back or away from this tab */

    this.activateHandlers = [];
    this.state = {
      tab: undefined,
      sessionInit: 'NotYet',
      showSessionInitDone: true,
      sidecarWidth: "0%"
      /* Closed */
      ,
      priorSidecarWidth: "0%"
      /* Closed */
      ,
      sidecarHasContent: false,
      watchPaneHasContent: false,
      primaryHeight: "100%"
      /* NotSplit */
      ,
      activeView: 'TerminalOnly'
    };
  }

  componentDidMount() {
    const onTabNew = () => {
      this.setState({
        sessionInit: 'Done'
      });

      if (this.props.onTabReady) {
        this.props.onTabReady(this.state.tab);
      }
    };

    _core.eventChannelUnsafe.once(`/tab/new/${this.props.uuid}`, onTabNew);

    this.cleaners.push(() => _core.eventChannelUnsafe.off(`/tab/new/${this.props.uuid}`, onTabNew));

    const onError = sessionInitError => {
      this.setState({
        sessionInit: 'Error',
        sessionInitError
      });
    };

    _core.eventChannelUnsafe.on(`/tab/new/error/${this.props.uuid}`, onError);

    this.cleaners.push(() => _core.eventChannelUnsafe.off(`/tab/new/error/${this.props.uuid}`, onError));
    const onOffline = this.onOffline.bind(this);

    _core.eventBus.onWithTabId('/tab/offline', this.props.uuid, onOffline);

    this.cleaners.push(() => _core.eventBus.offWithTabId('/tab/offline', this.props.uuid, onOffline)); // see https://github.com/IBM/kui/issues/4683

    _core.eventBus.onceCommandStarts(this.props.uuid, () => {
      this.setState({
        showSessionInitDone: false
      });
    });
  }

  onOffline() {
    return __awaiter(this, void 0, void 0, function* () {
      this.setState({
        sessionInit: 'Reinit'
      });
      (0, _core.initializeSession)(this.state.tab).then(() => {
        this.setState({
          sessionInit: 'Done'
        });
      }).catch(TabContent.onSessionInitError.bind(undefined, this.props.uuid));
    });
  }

  static onSessionInitError(uuid, sessionInitError) {
    _core.eventChannelUnsafe.emit(`/tab/new/error/${uuid}`, sessionInitError);
  }
  /** emit /tab/new event, if we have now a tab, but have not yet
   * emitted the event */


  static getDerivedStateFromProps(props, state) {
    if (state.tab && state.sessionInit === 'NotYet') {
      try {
        state.tab.state = props.state; // session init hook goes here

        (0, _core.initializeSession)(state.tab).then(() => {
          _core.eventBus.emit('/tab/new', state.tab);

          _core.eventChannelUnsafe.emit(`/tab/new/${props.uuid}`);
        }).catch(TabContent.onSessionInitError.bind(undefined, props.uuid));
        TabContent.hackResizer(state);
        return {
          sessionInit: 'InProgress'
        };
      } catch (err) {
        console.error(err);
      }
    } else {
      return state;
    }
  }
  /** Hmm, SplitPane doesn't yet allow for styling of the Resizer */


  static hackResizer(state) {
    const resizer = state.splitPaneImpl['splitPane'].querySelector('.Resizer');
    const a = document.createElement('span');
    const b = document.createElement('span');
    const c = document.createElement('span');
    resizer.appendChild(a);
    resizer.appendChild(b);
    resizer.appendChild(c);
    a.classList.add('resizer-thumb-fill');
    c.classList.add('resizer-thumb-fill');
    b.classList.add('resizer-thumb');
  }

  componentWillUnmount() {
    _core.eventBus.emit('/tab/close', this.state.tab);

    this.cleaners.forEach(cleaner => cleaner());
  }

  defaultLoading() {
    // this is a failsafe
    return 'Please wait while we connect to your cluster';
  }

  terminal() {
    if (this.state.sessionInit !== 'Done') {
      return React.createElement(_context.default.Consumer, null, config => {
        if (this.state.sessionInit === 'Error' && config.loadingError) {
          return config.loadingError(this.state.sessionInitError);
        } else if (this.state.sessionInit === 'Reinit' && config.reinit) {
          return config.reinit;
        }

        return config.loading || this.defaultLoading();
      });
    } else {
      return React.createElement(React.Fragment, null, React.createElement(_context.default.Consumer, null, config => React.createElement(_ScrollableTerminal.default, Object.assign({}, this.props, {
        tab: this.state.tab,
        config: config,
        sidecarIsVisible: this.state.sidecarWidth !== "0%"
        /* Closed */
        ,
        closeSidecar: () => this.setState({
          sidecarWidth: "0%"
          /* Closed */

        }),
        onClear: () => {
          this.setState({
            showSessionInitDone: false
          });
        },
        ref: c => {
          // so that we can refocus/blur
          this._terminal = c;
        }
      }))));
    }
  }

  onWillChangeSize(desiredWidth) {
    this.setState(curState => {
      const sidecarWidth = desiredWidth;
      const watchPaneOpen = curState.primaryHeight === "calc(100% - 8em - 2 * 0.5em)"
      /* Split */
      ;
      const activeView = sidecarWidth === "0%"
      /* Closed */
      ? watchPaneOpen ? 'TerminalPlusWatcher' : 'TerminalOnly' : watchPaneOpen ? 'TerminalSidecarWatcher' : 'TerminalPlusSidecar';
      const newState = {
        sidecarHasContent: true,
        sidecarWidth,
        priorSidecarWidth: curState.sidecarWidth,
        activeView
      };
      this.updateTopTabButtons(newState);
      return newState;
    });
  }

  show(activeView) {
    this.setState(curState => {
      const showSidecar = activeView === 'TerminalPlusSidecar' || activeView === 'TerminalSidecarWatcher';
      const showWatchPane = activeView === 'TerminalPlusWatcher' || activeView === 'TerminalSidecarWatcher';
      const sidecarWidth = showSidecar ? "60%"
      /* Split60 */
      : "0%"
      /* Closed */
      ;
      const primaryHeight = showWatchPane ? "calc(100% - 8em - 2 * 0.5em)"
      /* Split */
      : "100%"
      /* NotSplit */
      ;
      const newState = {
        sidecarWidth,
        activeView,
        priorSidecarWidth: curState.sidecarWidth,
        primaryHeight,
        sidecarHasContent: curState.sidecarHasContent
      };
      this.updateTopTabButtons(newState);
      return newState;
    });
  }
  /** Switch to the given view, if we aren't already there */


  showIfNot(desiredView) {
    if (this.state.activeView !== desiredView) {
      this.show(desiredView);
    }
  }

  openWatchPane() {
    const notWatching = this.state.activeView === 'TerminalOnly' || this.state.activeView === 'TerminalPlusSidecar';

    if (notWatching) {
      this.setState(curState => {
        const sidecarClosed = curState.sidecarWidth === "0%"
        /* Closed */
        ;
        return {
          activeView: sidecarClosed ? 'TerminalPlusWatcher' : 'TerminalSidecarWatcher',
          watchPaneHasContent: true,
          primaryHeight: "calc(100% - 8em - 2 * 0.5em)"
          /* Split */

        };
      });
    }
  }

  closeWatchPane() {
    this.setState(curState => {
      return {
        activeView: curState.activeView === 'TerminalSidecarWatcher' ? 'TerminalPlusSidecar' : 'TerminalOnly',
        watchPaneHasContent: false,
        primaryHeight: "100%"
        /* NotSplit */

      };
    });
  }

  onWillLoseFocus() {
    if (this._terminal) {
      this._terminal.doFocus();
    }
  }

  graft(node, key) {
    if (React.isValidElement(node)) {
      // ^^^ this check avoids tsc errors
      return React.cloneElement(node, {
        key,
        uuid: this.props.uuid,
        active: this.props.active,
        width: this.state.sidecarWidth,
        willChangeSize: this.onWillChangeSize.bind(this),
        willLoseFocus: this.onWillLoseFocus.bind(this)
      });
    } else {
      return node;
    }
  }
  /** Graft on the REPL focus management */


  children() {
    if (Array.isArray(this.props.children)) {
      return this.props.children.map((child, idx) => this.graft(child, idx));
    } else {
      return this.graft(this.props.children);
    }
  }
  /** Graft on the tab uuid */


  bottom() {
    if (React.isValidElement(this.props.bottom)) {
      // ^^^ this check avoids tsc errors
      return React.cloneElement(this.props.bottom, {
        uuid: this.props.uuid,
        tab: this.state.tab
      });
    } else {
      return this.props.bottom;
    }
  }
  /** Construct the `className` property of the tab element */


  tabClassName() {
    return 'kui--tab-content' + (this.props.active ? ' visible' : '') + (!this.state.tabClassList ? '' : ' ' + Object.keys(this.state.tabClassList).join(' '));
  }

  render() {
    this.activateHandlers.forEach(handler => handler(this.props.active));
    return React.createElement(React.Fragment, null, React.createElement("div", {
      ref: c => {
        const tab = c;
        this.setState({
          tab
        });

        if (tab) {
          tab.uuid = this.props.uuid;
          tab.getSize = _getSize.default.bind(c);

          tab.scrollToBottom = () => {
            c.scrollTop = c.scrollHeight;
          };

          tab.onActivate = handler => {
            this.activateHandlers.push(handler);
          };

          tab.offActivate = handler => {
            const idx = this.activateHandlers.findIndex(_ => _ === handler);

            if (idx >= 0) {
              this.activateHandlers.splice(idx, 1);
            }
          };

          tab.addClass = cls => {
            this.setState(curState => {
              if (!curState.tabClassList || !curState.tabClassList[cls]) {
                return {
                  tabClassList: Object.assign({}, curState.tabClassList, {
                    [cls]: true
                  })
                };
              }
            });
          };

          tab.removeClass = cls => {
            this.setState(curState => {
              if (curState.tabClassList && curState.tabClassList[cls]) {
                const update = Object.assign({}, curState.tabClassList);
                delete update[cls];
                return {
                  tabClassList: update
                };
              }
            });
          };
        }
      },
      className: this.tabClassName(),
      "data-tab-id": this.props.uuid
    }, React.createElement("div", {
      className: "kui--rows"
    }, React.createElement("div", {
      className: "kui--columns",
      style: {
        position: 'relative'
      }
    }, this.topDownSplit()), this.bottom()), this.state.tab && React.createElement(_Confirm.default, {
      tab: this.state.tab,
      uuid: this.props.uuid
    })));
  }
  /**
   *  Terminal | Sidecar
   *  ------------------
   *      WatchPane
   */


  topDownSplit() {
    return React.createElement(_reactSplitPane.default, {
      split: "horizontal",
      allowResize: false,
      size: this.state.primaryHeight,
      className: this.state.primaryHeight === "100%"
      /* NotSplit */
      ? 'kui--watch-pane-closed' : undefined
    }, this.leftRightSplit(), React.createElement(_context.default.Consumer, null, config => config.enableWatchPane && React.createElement(_WatchPane.default, {
      uuid: this.props.uuid,
      tab: this.state.tab,
      openWatchPane: this.openWatchPane.bind(this),
      closeWatchPane: this.closeWatchPane.bind(this)
    })));
  }
  /**
   * [ Terminal | Sidecar ]
   */


  leftRightSplit() {
    return React.createElement(_reactSplitPane.default, {
      ref: c => {
        this.setState({
          splitPaneImpl: c
        });
      },
      split: "vertical",
      resizerStyle: this.state.sidecarWidth === "100%"
      /* Maximized */
      && {
        display: 'none'
      },
      minSize: 0,
      className: this.state.sidecarWidth === "0%"
      /* Closed */
      ? 'kui--sidecar-closed' : undefined,
      size: this.state.sidecarWidth,
      style: {
        height: 'inherit'
      },
      primary: "second"
    }, this.terminal(), this.children());
  }
  /**
   * If given, use the top tab button controller to provide the
   * latest button model.
   *
   */


  updateTopTabButtons(newState) {
    if (this.props.willUpdateTopTabButtons) {
      this.props.willUpdateTopTabButtons([this.terminalButton(newState), this.sidecarButton(newState)].filter(_ => _));
    }
  }
  /**
   * Note how we use the argument `state` to initialize things, but we
   * intentionally use this.state in the onClick. The onClick handler
   * may be invoked after any number of onClicks in this or other
   * buttons. So it must pay attention to the *current* state, not the
   * state at the time of creation.
   *
   */


  terminalButton(state) {
    const key = 'show only terminal';
    return {
      icon: React.createElement(_Icons.default, {
        icon: "TerminalOnly",
        key: key,
        "data-mode": key,
        "data-active": state.activeView === 'TerminalOnly' || undefined,
        onClick: this.showIfNot.bind(this, 'TerminalOnly')
      })
    };
  }
  /** Caution: see the Note for this.terminalButton, re: `state` versus `this.state` */


  sidecarButton(state) {
    if (state.sidecarHasContent) {
      const key = 'show terminal and sidecar';
      return {
        icon: React.createElement(_Icons.default, {
          icon: "TerminalPlusSidecar",
          key: key,
          "data-mode": key,
          "data-active": state.activeView === 'TerminalPlusSidecar' || undefined,
          onClick: this.showIfNot.bind(this, 'TerminalPlusSidecar')
        })
      };
    }
  }

}

exports.default = TabContent;