"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _react = _interopRequireDefault(require("react"));
var _core = require("@kui-shell/core");
var _context = _interopRequireDefault(require("./context"));
var _getSize = _interopRequireDefault(require("../Views/Terminal/getSize"));
var _ScrollableTerminal = _interopRequireDefault(require("../Views/Terminal/ScrollableTerminal"));
var _MutabilityContext = require("./MutabilityContext");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/*
 * 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 = 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());
  });
};
const Confirm = _react.default.lazy(() => Promise.resolve().then(() => require('../Views/Confirm')));
/**
 *
 * TabContent
 * ----------------  <Tab/> from here down
 * | ST  |        |
 * |     |        |
 * |     |        |
 * |     |        |
 * |     |        |
 * |     |        |
 * ----------------
 *  ST: <ScrollableTerminal/>
 *
 */
class TabContent extends _react.default.PureComponent {
  constructor(props) {
    super(props);
    this._firstRenderDone = false;
    this.cleaners = [];
    /** switching back or away from this tab */
    this.activateHandlers = [];
    this.toggleEditMode = () => {
      this.setState(state => ({
        mutability: (0, _MutabilityContext.toggleReadOnlyBit)(state.mutability)
      }));
    };
    this.setEditMode = value => () => {
      this.setState(state => ({
        mutability: (0, _MutabilityContext.setReadOnlyBit)(state.mutability, value)
      }));
    };
    /** Reset any notion of left strip etc. */
    this._resetSplitLayout = () => this.setState({
      hasLeftStrip: false,
      hasRightStrip: false
    });
    /** Enter/exit mode where one split is displayed along the left */
    this._toggleLeftStripMode = () => this.setState(curState => ({
      hasLeftStrip: !curState.hasLeftStrip
    }));
    /** Enter/exit mode where one split is displayed along the right */
    this._toggleRightStripMode = () => this.setState(curState => ({
      hasRightStrip: !curState.hasRightStrip
    }));
    /** Toggle attribute on Tab DOM */
    this._toggleAttribute = attr => {
      if (this.state.tab.current) {
        this.state.tab.current.toggleAttribute(attr);
      }
    };
    /** Request to clear the contents of the ScrollableTerminal */
    this._onClear = () => {
      this.setState({
        showSessionInitDone: false
      });
      // reset the status stripe on clearing of the terminal
      // eslint false positive:
      // eslint-disable-next-line react/no-direct-mutation-state
      this.state.tab.current.state.desiredStatusStripeDecoration = {
        type: 'default'
      };
    };
    /** Terminal is ready for action */
    this._onTerminalReady = () => {
      this.setState({
        isTerminalReady: true
      });
      this.maybeFireTabReady(true, this.state.sessionInit === 'Done');
    };
    this.state = {
      active: props.active,
      tab: _react.default.createRef(),
      sessionInit: 'NotYet',
      showSessionInitDone: true,
      isTerminalReady: false,
      hasLeftStrip: false,
      hasRightStrip: false,
      _terminal: _react.default.createRef(),
      mutability: (0, _MutabilityContext.initializeState)()
    };
  }
  static getDerivedStateFromProps(props, state) {
    const isNowActive = props.active;
    const isCurrentlyActive = state.active;
    state.active = props.active;
    if (isNowActive && !isCurrentlyActive) {
      // see https://github.com/kubernetes-sigs/kui/issues/8208
      TabContent.delayedFocus(state, 0);
    }
    return state;
  }
  static delayedFocus(state, delayMillis = 300) {
    if (state.active) {
      setTimeout(() => {
        if (state.active && state._terminal.current) {
          state._terminal.current.doFocusIfNeeded();
        }
      }, delayMillis);
    }
  }
  /**
   * This method fires `this.props.onTabReady` after *both* the tab is
   * ready and the terminal is ready.
   */
  maybeFireTabReady(isTabReady, isTerminalReady) {
    try {
      if (this.props.onTabReady && isTabReady && isTerminalReady) {
        this.props.onTabReady(this.state.tab.current);
      }
    } catch (err) {
      console.error('Error in onTabReady', err);
    }
  }
  componentDidMount() {
    const onTabNew = () => {
      this.setState({
        sessionInit: 'Done'
      });
      TabContent.delayedFocus(this.state);
      this.maybeFireTabReady(true, this.state.isTerminalReady);
    };
    _core.Events.eventChannelUnsafe.once(`/tab/new/${this.props.uuid}`, onTabNew);
    this.cleaners.push(() => _core.Events.eventChannelUnsafe.off(`/tab/new/${this.props.uuid}`, onTabNew));
    const onError = sessionInitError => {
      this.setState({
        sessionInit: 'Error',
        sessionInitError
      });
    };
    _core.Events.eventChannelUnsafe.on(`/tab/new/error/${this.props.uuid}`, onError);
    this.cleaners.push(() => _core.Events.eventChannelUnsafe.off(`/tab/new/error/${this.props.uuid}`, onError));
    const onOffline = this.onOffline.bind(this);
    _core.Events.eventBus.onWithTabId('/tab/offline', this.props.uuid, onOffline);
    this.cleaners.push(() => _core.Events.eventBus.offWithTabId('/tab/offline', this.props.uuid, onOffline));
    _core.Events.eventBus.onWithTabId('/kui/tab/edit/toggle', this.props.uuid, this.toggleEditMode);
    this.cleaners.push(() => _core.Events.eventBus.offWithTabId('/kui/tab/edit/toggle', this.props.uuid, this.toggleEditMode));
    const onEditSet = this.setEditMode(true);
    _core.Events.eventBus.onWithTabId('/kui/tab/edit/set', this.props.uuid, onEditSet);
    this.cleaners.push(() => _core.Events.eventBus.offWithTabId('/kui/tab/edit/set', this.props.uuid, onEditSet));
    const onEditUnset = this.setEditMode(false);
    _core.Events.eventBus.onWithTabId('/kui/tab/edit/unset', this.props.uuid, onEditUnset);
    this.cleaners.push(() => _core.Events.eventBus.offWithTabId('/kui/tab/edit/unset', this.props.uuid, onEditUnset));
    this.oneTimeInit();
    this.execCommand();
  }
  execCommand() {
    if (this.props.initialCommandLine) {
      setTimeout(() => __awaiter(this, void 0, void 0, function* () {
        // execute a command onReady?
        while (true) {
          const hack = this.state.tab.current.querySelector('.kui--tab-content.visible .kui--scrollback[data-position="default"]');
          if (hack) {
            break;
          } else {
            yield new Promise(resolve => setTimeout(resolve, 500));
          }
        }
        try {
          // const quiet = tabModel.exec && tabModel.exec === 'qexec'
          (0, _core.pexecInCurrentTab)(this.props.initialCommandLine, this.state.tab.current);
        } catch (err) {
          console.error('Error executing initial command line in new tab', err);
        }
      }));
    }
  }
  onOffline() {
    return __awaiter(this, void 0, void 0, function* () {
      this.setState({
        sessionInit: 'Reinit'
      });
      (0, _core.initializeSession)(this.state.tab.current).then(() => {
        this.setState({
          sessionInit: 'Done'
        });
      }).catch(TabContent.onSessionInitError.bind(undefined, this.props.uuid));
    });
  }
  static onSessionInitError(uuid, sessionInitError) {
    _core.Events.eventChannelUnsafe.emit(`/tab/new/error/${uuid}`, sessionInitError);
  }
  oneTimeInit() {
    const {
      props,
      state
    } = this;
    if (state.sessionInit === 'NotYet') {
      this.initTab(this.state.tab.current);
      try {
        state.tab.current.state = props.state;
        // session init hook goes here
        (0, _core.initializeSession)(state.tab.current).then(() => {
          _core.Events.eventBus.emit('/tab/new', state.tab.current);
          _core.Events.eventChannelUnsafe.emit(`/tab/new/${props.uuid}`, state.tab.current);
        }).catch(TabContent.onSessionInitError.bind(undefined, props.uuid));
        // TabContent.hackResizer(state)
        return {
          sessionInit: 'InProgress'
        };
      } catch (err) {
        console.error(err);
      }
    }
  }
  initTab(tab) {
    if (tab) {
      tab.uuid = this.props.uuid;
      const c = tab;
      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.addTopClass = tab.addClass;
      tab.removeClass = cls => {
        this.setState(curState => {
          if (curState.tabClassList && curState.tabClassList[cls]) {
            const update = Object.assign({}, curState.tabClassList);
            delete update[cls];
            return {
              tabClassList: update
            };
          }
        });
      };
      tab.removeTopClass = tab.removeClass;
      tab.setTitle = title => {
        if (this.props.willSetTitle) {
          this.props.willSetTitle(this.props.uuid, title);
        }
      };
    }
  }
  componentWillUnmount() {
    _core.Events.eventBus.emit('/tab/close', this.state.tab.current);
    this.cleaners.forEach(cleaner => cleaner());
  }
  defaultLoading() {
    // this is a failsafe
    return 'Please wait while we connect to your cluster';
  }
  terminal() {
    if (this.state.sessionInit !== 'NotYet') {
      return _react.default.createElement(_context.default.Consumer, null, config => _react.default.createElement(_ScrollableTerminal.default, Object.assign({}, this.props, {
        tab: this.state.tab.current,
        sessionInit: this.state.sessionInit,
        config: config,
        toggleAttribute: this._toggleAttribute,
        onClear: this._onClear,
        onReady: this._onTerminalReady,
        ref: this.state._terminal,
        hasLeftStrip: this.state.hasLeftStrip,
        hasRightStrip: this.state.hasRightStrip,
        willToggleLeftStripMode: this._toggleLeftStripMode,
        willToggleRightStripMode: this._toggleRightStripMode,
        resetSplitLayout: this._resetSplitLayout,
        noActiveInput: this.props.noActiveInput || !this.state.mutability.editable
      }), this.children()));
    }
  }
  /** Use client-provided (or default) proxy disconnected notice, if warranted */
  proxyDisconnectNotice() {
    if (_core.Capabilities.inBrowser() && this.state.sessionInit !== 'Done') {
      return _react.default.createElement(_context.default.Consumer, null, config => _react.default.createElement(_react.default.Suspense, {
        fallback: _react.default.createElement("div", null)
      }, this.state.sessionInit === 'Error' && config.loadingError ? config.loadingError(this.state.sessionInitError) : this.state.sessionInit === 'Reinit' && config.reinit ? config.reinit : this.state.sessionInit !== 'Done' && (config.loading || this.defaultLoading())));
    }
  }
  graft(node, key) {
    if (_react.default.isValidElement(node)) {
      // ^^^ this check avoids tsc errors
      return _react.default.cloneElement(node, {
        key,
        uuid: this.props.uuid,
        active: this.state.active
      });
    } 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.default.isValidElement(this.props.bottom)) {
      // ^^^ this check avoids tsc errors
      return _react.default.cloneElement(this.props.bottom, {
        uuid: this.props.uuid,
        tab: this.state.tab.current
      });
    } else {
      return this.props.bottom;
    }
  }
  /** Construct the `className` property of the tab element */
  tabClassName() {
    return 'kui--tab-content' + (this.state.active ? ' visible' : '') + (!this.state.tabClassList ? '' : ' ' + Object.keys(this.state.tabClassList).join(' '));
  }
  body() {
    if (!this.state.active && !this._firstRenderDone) {
      return _react.default.createElement(_react.default.Fragment, null);
    }
    this._firstRenderDone = true;
    return _react.default.createElement(_react.default.Fragment, null, _react.default.createElement("div", {
      className: "kui--rows"
    }, this.proxyDisconnectNotice(), _react.default.createElement("div", {
      className: "kui--columns",
      style: {
        position: 'relative'
      }
    }, this.terminal()), this.bottom()), this.state.tab.current && _react.default.createElement(_react.default.Suspense, {
      fallback: _react.default.createElement("div", null)
    }, _react.default.createElement(Confirm, {
      tab: this.state.tab.current,
      uuid: this.props.uuid
    })));
  }
  render() {
    this.activateHandlers.forEach(handler => handler(this.state.active));
    return _react.default.createElement(_MutabilityContext.MutabilityContext.Provider, {
      value: this.state.mutability
    }, _react.default.createElement("div", {
      ref: this.state.tab,
      className: this.tabClassName(),
      "data-tab-id": this.props.uuid,
      "data-has-left-strip": this.state.hasLeftStrip || undefined,
      "data-has-right-strip": this.state.hasRightStrip || undefined
    }, this.body()));
  }
}
exports.default = TabContent;