"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = exports.ReactCommentary = void 0;
exports.offCommentaryEdit = offCommentaryEdit;
exports.onCommentaryEdit = onCommentaryEdit;
var _react = _interopRequireDefault(require("react"));
var _events = require("events");
var _i18n = require("@kui-shell/core/mdist/api/i18n");
var _TextContent = require("@patternfly/react-core/dist/esm/components/Text/TextContent");
var _Card = _interopRequireDefault(require("../spi/Card"));
var _MutabilityContext = require("../Client/MutabilityContext");
var _tabbed = require("./Markdown/components/tabbed");
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.
 */

const Markdown = _react.default.lazy(() => Promise.resolve().then(() => require('./Markdown')));
const SimpleMarkdown = _react.default.lazy(() => Promise.resolve().then(() => require('./Markdown/Simple')));
const Button = _react.default.lazy(() => Promise.resolve().then(() => require('../spi/Button')));
const SimpleEditor = _react.default.lazy(() => Promise.resolve().then(() => require('./Editor/SimpleEditor')));
const strings = (0, _i18n.i18n)('plugin-client-common');
/** Allows decoupled edit/preview */
const events = new _events.EventEmitter();
/** Requests for current textValue */
function getChannel(props) {
  return `/get/${props.receive || props.send}`;
}
/** Broadcast current textValue */
function editChannel(props) {
  return `/edit/${props.receive || props.send}`;
}
function onCommentaryEdit(channel, cb) {
  const props = {
    receive: channel
  };
  events.on(editChannel(props), cb);
  events.emit(getChannel(props), cb);
}
function offCommentaryEdit(channel, cb) {
  events.off(editChannel({
    receive: channel
  }), cb);
}
class Commentary extends _react.default.PureComponent {
  constructor(props) {
    super(props);
    this.cleaners = [];
    this.onGet = cb => cb(this.state.textValue);
    this.onEdit = textValue => {
      this.setState({
        textValue
      });
    };
    this._onCancel = this.onCancel.bind(this);
    this._onRevert = this.onRevert.bind(this);
    this._onDone = this.onDone.bind(this);
    this._setEdit = this.setEdit.bind(this);
    this._onContentChange = this.onContentChange.bind(this);
    this._onSaveFromEditor = this.onSaveFromEditor.bind(this);
    this._onCancelFromEditor = this.onCancelFromEditor.bind(this);
    const textValue = this.initialTextValue();
    this.state = {
      initialActiveKey: props.activeKey,
      isEdit: props.edit === false ? false : props.edit || textValue.length === 0,
      textValue,
      lastAppliedTextValue: textValue
    };
  }
  static getDerivedStateFromProps(props, state) {
    if (props.previousActiveKey !== undefined && props.activeKey === state.initialActiveKey) {
      events.emit(editChannel(props), state.textValue, props.filepath);
    }
    return state;
  }
  componentDidMount() {
    this.initCouplingEvents();
  }
  componentWillUnmount() {
    this.cleaners.forEach(_ => _());
    if (this.props.send) {
      // broadcast clear
      events.emit(editChannel(this.props), '');
    }
  }
  componentDidUpdate(_, prevState) {
    if (this.props.send) {
      // broadcast new textValue if either:
      // a. different textValue
      // b. there has been a tab switch above us (i.e. in a contanining component)
      if (prevState.textValue !== this.state.textValue || this.props.previousActiveKey !== undefined && this.props.activeKey === this.state.initialActiveKey) {
        events.emit(editChannel(this.props), this.state.textValue, this.props.filepath);
      }
    }
  }
  /** Are we a coupled view, i.e. split edit/preview? */
  get isCoupled() {
    return this.props.send || this.props.receive;
  }
  /**
   * Register either as a producer or consumer of edit events. Allows
   * for decoupled edit/preview views.
   *
   */
  initCouplingEvents() {
    if (this.props.receive) {
      // this is the preview side of the coupling
      events.on(editChannel(this.props), this.onEdit);
      this.cleaners.push(() => events.off(editChannel(this.props), this.onEdit));
      // emit an initial request for the content
      setTimeout(() => events.emit(getChannel(this.props), this.onEdit));
    } else if (this.props.send) {
      // this is the edit side of the coupling
      events.on(getChannel(this.props), this.onGet);
      this.cleaners.push(() => events.off(getChannel(this.props), this.onGet));
      // emit an initial value for the content
      setTimeout(() => events.emit(editChannel(this.props), this.state.textValue, this.props.filepath));
    }
  }
  /** update state to cancel any edits and close the editor */
  onCancel(evt) {
    this.onRevert(evt, false);
    this.removeOurselvesIfEmpty();
  }
  /** cancel button */
  cancel() {
    return _react.default.createElement(Button, {
      variant: "secondary",
      isSmall: true,
      className: "kui--tab-navigatable kui--commentary-button kui--commentary-cancel-button",
      onClick: this._onCancel
    }, strings('Cancel'));
  }
  /** Update state to cancel any updates, but leave editor open */
  onRevert(evt, isEdit = true) {
    if (evt) {
      // so that the event doesn't propagate to the onClick on the Card itself
      evt.stopPropagation();
    }
    this.setState(curState => {
      // switch back to the lastAppliedTextValue
      const textValue = curState.lastAppliedTextValue;
      if (this.props.willUpdateResponse) {
        this.props.willUpdateResponse(textValue);
      }
      return {
        isEdit,
        textValue
      };
    });
  }
  /** revert button */
  revert() {
    return _react.default.createElement(Button, {
      variant: "tertiary",
      isSmall: true,
      className: "kui--tab-navigatable kui--commentary-button kui--commentary-revert-button",
      onClick: this._onRevert
    }, strings('Revert'));
  }
  /** If the user clicks Done or Cancel and there is no text, remove ourselves */
  removeOurselvesIfEmpty() {
    if (this.state.textValue === '') {
      if (this.props.willRemove) {
        this.props.willRemove();
      }
      return true;
    } else {
      return false;
    }
  }
  /** Update state to reflect lastAppliedTextValue, and close the editor */
  onDone(evt) {
    if (evt) {
      // so that the event doesn't propagate to the onClick on the Card itself
      evt.stopPropagation();
    }
    if (!this.removeOurselvesIfEmpty()) {
      this.setState(curState => {
        this.props.willUpdateCommand(`# ${curState.textValue.replace(/\n/g, '\\n').replace(/\t/g, '\\t')}`);
        return {
          isEdit: false,
          lastAppliedTextValue: curState.textValue
        };
      });
    }
  }
  /** done button removes the editor  */
  done() {
    return _react.default.createElement(Button, {
      isSmall: true,
      className: "kui--tab-navigatable kui--commentary-button kui--commentary-done-button",
      onClick: this._onDone
    }, strings('Done'));
  }
  /** toolbar hosts editor actions */
  toolbar() {
    return _react.default.createElement("div", {
      className: "kui--commentary-editor-toolbar fill-container flush-right"
    }, this.done(), "\u00A0", this.cancel(), "\u00A0", this.revert());
  }
  /** Enter isEdit mode */
  setEdit() {
    this.setState({
      isEdit: true
    });
  }
  preview() {
    if (this.props.preview !== false) {
      if (this.props.simple) {
        return _react.default.createElement(SimpleMarkdown, {
          nested: true,
          execUUID: this.props.execUUID,
          filepath: this.props.filepath,
          baseUrl: this.props.baseUrl,
          source: this.state.textValue,
          tab: this.props.tab
        });
      } else {
        return _react.default.createElement(Markdown, {
          nested: true,
          execUUID: this.props.execUUID,
          filepath: this.props.filepath,
          source: this.state.textValue,
          codeBlockResponses: this.props.codeBlockResponses,
          baseUrl: this.props.baseUrl,
          snippetBasePath: this.props.snippetBasePath,
          tab: this.props.tab
        });
      }
    }
  }
  card() {
    return _react.default.createElement(_MutabilityContext.MutabilityContext.Consumer, null, value => _react.default.createElement("span", {
      className: "kui--commentary-card",
      onDoubleClick: !value.editable ? undefined : this._setEdit
    }, _react.default.createElement(_Card.default, Object.assign({}, this.props, {
      "data-is-editing": this.state.isEdit || undefined,
      header: this.state.isEdit && this.props.header !== false && strings('Editing Comment as Markdown'),
      footer: this.state.isEdit && !this.isCoupled && this.toolbar()
    }), this.preview(), this.state.isEdit && this.editor())));
  }
  /** Percolate `SimpleEditor` edits up to the Preview view */
  onContentChange(value) {
    this.setState(curState => {
      if (!curState.isEdit) {
        // then we've already exited edit mode, ignore this content
        // change event
        return null;
      } else {
        return {
          textValue: value
        };
      }
    });
    if (this.props.willUpdateResponse) {
      this.props.willUpdateResponse(value);
    }
  }
  /** User has requested to save changes via keyboard shortcut, from within `SimpleEditor` */
  onSaveFromEditor(value) {
    this.onContentChange(value);
    this.onDone();
  }
  /** User has requested to cancel changes via keyboard shortcut, from within `SimpleEditor` */
  onCancelFromEditor() {
    this.onCancel();
  }
  /** @return the initial content to display, before any editing */
  initialTextValue() {
    return this.props.children || '';
  }
  editor() {
    return _react.default.createElement(_react.default.Suspense, {
      fallback: _react.default.createElement("div", null)
    }, _react.default.createElement(SimpleEditor, {
      tabUUID: this.props.tab.uuid,
      content: this.state.textValue,
      className: "kui--source-ref-editor kui--commentary-editor",
      readonly: false,
      simple: true,
      wordWrap: "on",
      onSave: this._onSaveFromEditor,
      onCancel: !this.isCoupled && this._onCancelFromEditor,
      onContentChange: this._onContentChange,
      contentType: "markdown",
      scrollIntoView: false
    }));
  }
  render() {
    this.props.onRender();
    return _react.default.createElement("div", {
      className: "kui--commentary",
      "data-is-editing": this.state.isEdit || undefined,
      "data-no-header": this.props.header === false || undefined
    }, this.card());
  }
}
class CommentaryExternal extends _react.default.PureComponent {
  render() {
    return _react.default.createElement(_tabbed.CurrentMarkdownTab.Consumer, null, config => _react.default.createElement(Commentary, Object.assign({}, this.props, config)));
  }
}
exports.default = CommentaryExternal;
class ReactCommentary extends _react.default.PureComponent {
  render() {
    return _react.default.createElement("div", {
      className: "kui--commentary"
    }, _react.default.createElement("span", {
      className: "kui--commentary-card"
    }, _react.default.createElement(_Card.default, null, _react.default.createElement(_TextContent.TextContent, null, this.props.children))));
  }
}
exports.ReactCommentary = ReactCommentary;