"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _react = _interopRequireDefault(require("react"));
var _i18n = require("@kui-shell/core/mdist/api/i18n");
var _Events = require("@kui-shell/core/mdist/api/Events");
var _Response = require("@kui-shell/core/mdist/api/Response");
var _BlockModel = require("./BlockModel");
var _Actions = _interopRequireDefault(require("./Actions"));
var _Scalar = _interopRequireDefault(require("../../../Content/Scalar/"));
var _context = _interopRequireDefault(require("../../../Client/context"));
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 Ansi = _react.default.lazy(() => Promise.resolve().then(() => require('../../../Content/Scalar/Ansi')));
const ExpandableSection = _react.default.lazy(() => Promise.resolve().then(() => require('../../../spi/ExpandableSection')));
const strings = (0, _i18n.i18n)('plugin-client-common');
class Output extends _react.default.PureComponent {
  constructor(props) {
    super(props);
    this._willRemove = () => this.props.willRemove(undefined, this.props.idx);
    this._willUpdateCommand = command => this.props.willUpdateCommand(this.props.idx, command);
    this.streamingOutput = [];
    this.unmounted = true;
    this._onRender = this.onRender.bind(this);
    const streamingConsumer = this.streamingConsumer.bind(this);
    this.state = {
      alreadyListen: false,
      isResultRendered: false,
      nStreamingOutputs: 0,
      streamingConsumer
    };
  }
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  streamingConsumer(part) {
    return __awaiter(this, void 0, void 0, function* () {
      if ((0, _BlockModel.hasUUID)(this.props.model)) {
        const tabUUID = this.props.uuid;
        const execUUID = this.props.model.execUUID;
        // done with this part... not done with all parts
        const done = () => {
          this.props.onRender();
          _Events.eventChannelUnsafe.emit(`/command/stdout/done/${tabUUID}/${execUUID}`);
        };
        // part === null: the controller wants to clear prior output
        if (part === null) {
          // remove last output
          this.streamingOutput.pop();
          this.setState(curState => ({
            nStreamingOutputs: curState.nStreamingOutputs - 1
          }));
          done();
        } else {
          if ((0, _BlockModel.isOutputRedirected)(this.props.model)) {
            // if we were asked to redirect to a file, then we can
            // immediately indicate that we are done with this part
            done();
          } else {
            this.streamingOutput.push(Buffer.isBuffer(part) ? part.toString() : part);
            // use setTimeout to introduce hysteresis, so we aren't
            // forcing a react re-render for a bunch of tiny streaming
            // updates
            const update = () => {
              this.setState({
                nStreamingOutputs: this.streamingOutput.length
              });
              setTimeout(done, 10);
            };
            if (this.state.nStreamingOutputs === 0) {
              update();
            } else {
              setTimeout(update, 10);
            }
          }
        }
      }
    });
  }
  static getDerivedStateFromProps(props, state) {
    if (!state.alreadyListen && ((0, _BlockModel.isActive)(props.model) || (0, _BlockModel.isProcessingOrBeingRerun)(props.model))) {
      // listen for streaming output (unless the output has been redirected to a file)
      const tabUUID = props.uuid;
      _Events.eventChannelUnsafe.on(`/command/stdout/${tabUUID}/${props.model.execUUID}`, state.streamingConsumer);
      return {
        alreadyListen: true,
        isResultRendered: false,
        streamingOutput: []
      };
    } else if ((0, _BlockModel.isFinished)(props.model) && !state.isResultRendered) {
      const tabUUID = props.uuid;
      if (!(0, _BlockModel.isEmpty)(props.model)) {
        _Events.eventChannelUnsafe.off(`/command/stdout/${tabUUID}/${props.model.execUUID}`, state.streamingConsumer);
      }
      return {
        alreadyListen: false,
        isResultRendered: true
      };
    } else {
      return state;
    }
  }
  componentDidMount() {
    this.unmounted = false;
  }
  componentWillUnmount() {
    this.unmounted = true;
  }
  onRender(assertHasContent) {
    if (this.unmounted) return;else if (this.props.onRender && !(0, _BlockModel.isBeingRerun)(this.props.model) && !(0, _BlockModel.hasBeenRerun)(this.props.model)) {
      // we don't want reruns to trigger any scrolling behavior
      this.props.onRender();
    }
    this.setState({
      assertHasContent
    });
  }
  hasStreamingOutput() {
    return this.state.nStreamingOutputs > 0;
  }
  outputWillOverflow() {
    // RadioTable currently uses the Dropdown component that will overflow
    return (0, _BlockModel.isWithCompleteEvent)(this.props.model) && (0, _Response.isRadioTable)(this.props.model.response);
  }
  stream() {
    if (this.hasStreamingOutput()) {
      // we may have duplicates in streamingOutput and response
      // see https://github.com/kubernetes-sigs/kui/pull/8354
      const response = (0, _BlockModel.isWithCompleteEvent)(this.props.model) ? this.props.model.response : undefined;
      const streamingOutput = !response ? this.streamingOutput : this.streamingOutput.filter(_ => {
        if ((0, _Response.isMixedResponse)(response)) {
          return !response.find(_2 => _ === _2);
        } else {
          return _ !== response;
        }
      });
      if (streamingOutput.length < this.streamingOutput.length) {
        this.streamingOutput = streamingOutput;
      }
      if (streamingOutput.every(_ => typeof _ === 'string')) {
        const combined = streamingOutput.join('');
        return combined.length > 0 && _react.default.createElement("div", {
          className: "repl-result-like result-vertical",
          "data-stream": true
        }, _react.default.createElement(_react.default.Suspense, {
          fallback: _react.default.createElement("div", null)
        }, _react.default.createElement(Ansi, null, combined)));
      }
      return _react.default.createElement("div", {
        className: "repl-result-like result-vertical",
        "data-stream": true
      }, streamingOutput.map((part, idx) => _react.default.createElement(_react.default.Suspense, {
        fallback: _react.default.createElement("div", null),
        key: idx
      }, _react.default.createElement(_Scalar.default, {
        tab: this.props.tab,
        execUUID: (0, _BlockModel.hasUUID)(this.props.model) && this.props.model.execUUID,
        response: part,
        isWidthConstrained: this.props.isWidthConstrained,
        willChangeSize: this.props.willChangeSize,
        willUpdateCommand: this._willUpdateCommand,
        onRender: this._onRender
      }))));
    }
  }
  result() {
    if ((0, _BlockModel.isActive)(this.props.model) || (0, _BlockModel.isProcessingOrBeingRerun)(this.props.model)) {
      return _react.default.createElement("div", {
        className: "repl-result"
      });
    } else if ((0, _BlockModel.isEmpty)(this.props.model)) {
      // no result to display for these cases
      return _react.default.createElement(_react.default.Fragment, null);
    } else {
      const statusCode = (0, _BlockModel.isOops)(this.props.model) ? (0, _Response.isCodedError)(this.props.model.response) ? this.props.model.response.code || this.props.model.response.statusCode : 500 : (0, _BlockModel.isFinished)(this.props.model) ? 0 : undefined;
      return _react.default.createElement("div", {
        className: 'repl-result' + ((0, _BlockModel.isOops)(this.props.model) ? ' oops' : '') + ((0, _BlockModel.isWithCompleteEvent)(this.props.model) && (0, _Response.isMixedResponse)(this.props.model.response) ? ' flex-column' : '') + (this.outputWillOverflow() ? ' overflow-visible' : ''),
        "data-status-code": statusCode
      }, (0, _BlockModel.isCancelled)(this.props.model) ? _react.default.createElement(_react.default.Fragment, null) : _react.default.createElement(_react.default.Suspense, {
        fallback: _react.default.createElement("div", null)
      }, _react.default.createElement(_Scalar.default, {
        tab: this.props.tab,
        execUUID: (0, _BlockModel.hasUUID)(this.props.model) && this.props.model.execUUID,
        response: this.props.model.response,
        completeEvent: this.props.model.completeEvent,
        isWidthConstrained: this.props.isWidthConstrained,
        willChangeSize: this.props.willChangeSize,
        willFocusBlock: this.props.willFocusBlock,
        willRemove: this._willRemove,
        willUpdateCommand: this._willUpdateCommand,
        onRender: this._onRender
      })));
    }
  }
  cursor() {
    return _react.default.createElement(_react.default.Fragment, null);
    /* if (isProcessing(this.props.model)) {
      return (
        <div className="repl-result-spinner">
          <div className="repl-result-spinner-inner"></div>
        </div>
      )
    } */
  }

  isShowingSomethingInTerminal(block) {
    if ((0, _BlockModel.isProcessingOrBeingRerun)(this.props.model)) {
      return this.hasStreamingOutput();
    } else if ((0, _BlockModel.isFinished)(block) && !(0, _BlockModel.isCancelled)(block) && !(0, _BlockModel.isEmpty)(block)) {
      const {
        response
      } = block;
      return (0, _BlockModel.isOops)(block) || (0, _Response.isAbortableResponse)(response) || (0, _Response.isMultiModalResponse)(response) || (0, _Response.isNavResponse)(response) || (0, _Response.isCommentaryResponse)(response) || (0, _Response.isTabLayoutModificationResponse)(response) || (0, _Response.isReactResponse)(response) || (0, _Response.isHTML)(response) || (0, _Response.isMarkdownResponse)(response) || Array.isArray(response) && response.length > 0 || typeof response === 'string' && response.length > 0 || typeof response === 'number' || (0, _Response.isTable)(response) || (0, _Response.isMixedResponse)(response) || (0, _Response.isXtermResponse)(response) && response.rows && response.rows.length !== 0 || this.hasStreamingOutput();
    } else {
      return false;
    }
  }
  ok(hasContent) {
    if ((0, _BlockModel.isOk)(this.props.model)) {
      if (hasContent) {
        return _react.default.createElement("div", {
          className: "ok"
        });
      } else {
        return _react.default.createElement("div", {
          className: "ok"
        }, strings('ok'));
      }
    }
  }
  ctx( /* insideBrackets: React.ReactNode = this.props.displayedIdx || this.props.idx + 1 */
  ) {
    return _react.default.createElement(_context.default.Consumer, null, config => !config.noPromptContext && _react.default.createElement("span", {
      className: "repl-context",
      onClick: this.props.willFocusBlock,
      "data-input-count": this.props.idx,
      "data-custom-prompt": !!config.prompt || undefined
    }));
  }
  /** For output-only blocks, render the Block Actions */
  actions() {
    if ((0, _BlockModel.isOutputOnly)(this.props.model)) {
      return _react.default.createElement(_Actions.default, Object.assign({}, this.props, {
        command: (0, _BlockModel.hasCommand)(this.props.model) && this.props.model.command
      }));
    }
  }
  /** @return the UI for the main/content part of the Output */
  content(hasContent) {
    return _react.default.createElement("div", {
      className: "result-vertical"
    }, this.stream(), this.result(), this.cursor(), this.ok(hasContent));
  }
  render() {
    const hasContent = this.state.assertHasContent !== undefined ? this.state.assertHasContent : this.isShowingSomethingInTerminal(this.props.model);
    // when displaying blocks in a loaded Snapshot (isReplay) that
    // have not yet been rerun (hasBeenRerun), then wrap an expando
    // around them (except don't wrap expandos around output-only
    // blocks, such as for Commentary)
    const noExpando = !(0, _BlockModel.isReplay)(this.props.model) || (0, _BlockModel.hasBeenRerun)(this.props.model) || (0, _BlockModel.isOutputOnly)(this.props.model);
    const content = noExpando ? this.content(hasContent) : _react.default.createElement(ExpandableSection, {
      className: "flex-fill",
      showMore: strings('Show Sample Output'),
      showLess: strings('Hide Sample Output')
    }, this.content(hasContent));
    return _react.default.createElement("div", {
      className: 'repl-output ' + (hasContent ? ' repl-result-has-content' : '')
    }, hasContent && this.ctx(), content, this.actions());
  }
}
exports.default = Output;