"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 _Job = require("@kui-shell/core/mdist/api/Job");
var _Table = require("@kui-shell/core/mdist/api/Table");
var _Icons = _interopRequireDefault(require("../../spi/Icons"));
var _kuiHeaderFromBody = _interopRequireDefault(require("./kuiHeaderFromBody"));
var _PaginatedTable = _interopRequireDefault(require("./PaginatedTable"));
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 strings = (0, _i18n.i18n)('plugin-client-common');
class LivePaginatedTable extends _PaginatedTable.default {
  constructor(props) {
    super(props);
    this.state = Object.assign(this.state, {
      isWatching: true,
      lastUpdatedMillis: Date.now(),
      progress: {},
      progressVersion: 1
    });
  }
  /**
   * Only after the dom is attached can we initialize the watcher,
   * because it may otherwise trigger render() calls before we have
   * been attached.
   *
   */
  componentDidMount() {
    this.initWatch();
  }
  /** @return whether the table is currently "live", and responding to updates from the controller */
  isWatching() {
    return this.state.isWatching;
  }
  dataAttrs() {
    return {
      'data-table-watching': this.isWatching(),
      'data-table-as-grid': this.state.asGrid
    };
  }
  /** Render the component */
  render() {
    if (this.props.onRender) {
      setTimeout(() => this.props.onRender(true));
    }
    return super.render();
  }
  /**
   * Initialize watcher channel to the provider, and attach the job so
   * that it can be managed w.r.t. UI contexts (such as tabs) coming
   * and going.
   *
   */
  initWatch() {
    // initiate the pusher watch
    const update = this.update.bind(this);
    const progress = this.progress.bind(this);
    const batchUpdateDone = this.batchUpdateDone.bind(this);
    const offline = this.offline.bind(this);
    const done = this.done.bind(this);
    const allOffline = this.allOffline.bind(this);
    const header = this.header.bind(this);
    const footer = this.footer.bind(this);
    const setBody = this.setBody.bind(this);
    this.props.response.watch.init({
      update,
      progress,
      setBody,
      batchUpdateDone,
      offline,
      done,
      allOffline,
      header,
      footer
    });
  }
  pauseWatch() {
    if (this.props.response.watch.xoff) {
      this.props.response.watch.xoff();
    } else {
      this.props.response.watch.abort();
    }
    this.setState({
      isWatching: false
    });
  }
  resumeWatch() {
    if (this.props.response.watch.xon) {
      this.props.response.watch.xon();
      this.setState({
        isWatching: true
      });
    }
  }
  watchControll() {
    if (this.state.isWatching) {
      this.pauseWatch();
    } else {
      this.resumeWatch();
    }
  }
  /** E.g. last updated time for live tables */
  caption() {
    if (this.state.lastUpdatedMillis) {
      const icon = this.state.isWatching ? 'Eye' : 'EyeSlash';
      const iconColor = this.state.isWatching ? 'green-text' : 'sub-text';
      const watchControlDescription = (0, _Job.isSuspendable)(this.props.response.watch) ? this.state.isWatching ? strings('Abort watcher') : '' : this.state.isWatching ? strings('Pause watcher') : strings('Resume watcher');
      return _react.default.createElement(_react.default.Fragment, null, _react.default.createElement("a", {
        href: "#",
        className: "kui--toolbar-button-watch",
        "data-online": this.state.isWatching,
        onClick: this.watchControll.bind(this),
        onMouseDown: evt => evt.preventDefault(),
        title: watchControlDescription,
        "aria-label": watchControlDescription
      }, _react.default.createElement(_Icons.default, {
        icon: icon,
        className: 'small-right-pad ' + iconColor
      })), strings('Last updated', new Date(this.state.lastUpdatedMillis).toLocaleTimeString()));
    }
  }
  setBody(rows) {
    let doOffline = false;
    let doUpdate = false;
    const lookup = (rows, row) => rows.findIndex(({
      rowKey,
      name
    }) => rowKey && row.rowKey ? rowKey === row.rowKey : name === row.name);
    this.props.response.body.forEach((old, originalIdx) => {
      const foundIndex = lookup(rows, old);
      if (foundIndex === -1) {
        doOffline = true;
        this.props.response.body.splice(originalIdx, 1);
      }
    });
    rows.forEach(newKuiRow => {
      const foundIndex = lookup(this.props.response.body, newKuiRow);
      if (foundIndex === -1 || !(0, _Table.sameRow)(newKuiRow, this.props.response.body[foundIndex])) {
        doUpdate = true;
        this.update(newKuiRow, true, undefined, foundIndex);
      }
    });
    if (doUpdate) {
      this.batchUpdateDone();
    } else if (doOffline) {
      this.setState({
        lastUpdatedMillis: Date.now()
      });
    }
  }
  /**
   * offline takes the rowKey of the row to be deleted and applies this to the table view
   *
   */
  offline(rowKey) {
    const existingRows = this.state.body;
    const foundIndex = existingRows.findIndex(_ => _.rowKey ? _.rowKey === rowKey : _.name === rowKey);
    if (foundIndex === -1) {
      console.error('table row went offline, but not found in view model', rowKey, existingRows);
    } else {
      // change the status badge to `offline`
      const kuiRow = this.props.response.body[foundIndex];
      kuiRow.attributes.forEach(attr => {
        if (/STATUS/i.test(attr.key)) {
          attr.value = 'Offline';
          attr.css = 'red-background';
        }
      });
      const newRow = kuiRow; // TODO Missing justUpdated: true kuiRow2carbonRow(this.state.header, true)(kuiRow, foundIndex)
      const newRows = existingRows.slice(0, foundIndex).concat([newRow]).concat(existingRows.slice(foundIndex + 1));
      this.setState({
        lastUpdatedMillis: Date.now(),
        body: newRows
      });
    }
  }
  /**
   * allOffline allows pollers to indicate that all resources are not to be found
   *
   */
  allOffline() {
    this.props.response.body = [];
    this.setState({
      isWatching: false,
      body: [],
      lastUpdatedMillis: Date.now()
    });
  }
  /** Incremental progress for a given row */
  progress(...parameters) {
    this.setState(curState => {
      const progress = parameters[0];
      if (this.props.response.body[progress.rowIdx]) {
        const rowKey = this.props.response.body[progress.rowIdx].rowKey;
        curState.progress[rowKey] = progress;
      }
      return {
        progressVersion: curState.progressVersion + 1
      };
    });
  }
  /**
   * update consumes the update notification and apply it to the table view
   *
   */
  /* eslint-disable-next-line @typescript-eslint/no-unused-vars */
  update(newKuiRow, batch = false, justUpdated = true, idxToUpdate) {
    if (!this.props.response.header) {
      const header = (0, _kuiHeaderFromBody.default)([newKuiRow]);
      if (header) {
        this.header(header);
      }
    }
    // the _.rowKey existence check here is important
    // because we didn't ask rowKey to be a required field
    // if both of the rowKey are undefined, we will get a wrong foundIndex
    const lookup = rows => rows.findIndex(_ => _.rowKey && newKuiRow.rowKey ? _.rowKey === newKuiRow.rowKey : _.name === newKuiRow.name);
    // update props model
    const foundIndex = idxToUpdate !== undefined ? idxToUpdate : lookup(this.props.response.body);
    if (foundIndex === -1) {
      this.props.response.body.push(newKuiRow);
    } else {
      this.props.response.body[foundIndex] = newKuiRow;
    }
    // deferred/batch update in progress?
    if (batch) {
      if (!this._deferredUpdate) {
        this._deferredUpdate = [];
      }
      const foundIndex = lookup(this._deferredUpdate);
      if (foundIndex >= 0) {
        this._deferredUpdate[foundIndex] = newKuiRow;
      } else {
        this._deferredUpdate.push(newKuiRow);
      }
    } else {
      this.setState({
        lastUpdatedMillis: Date.now()
      });
    }
  }
  /** End of a deferred batch of updates */
  batchUpdateDone() {
    if (this._deferredUpdate) {
      this._deferredUpdate = undefined;
      this.setState(curState => ({
        body: curState.body.slice(),
        lastUpdatedMillis: Date.now()
      }));
    }
  }
  /**
   * Update to reflect new header model
   *
   */
  header(newKuiHeader) {
    if (!(0, _Table.sameRow)(newKuiHeader, this.previouslyReceivedHeader)) {
      this.previouslyReceivedHeader = newKuiHeader;
      this.props.response.header = newKuiHeader;
      this.setState({
        header: newKuiHeader,
        lastUpdatedMillis: Date.now()
      });
    }
  }
  /**
   * Update to reflect new footer message
   *
   */
  footer(streams) {
    this.props.response.footer = this.props.response.footer ? this.props.response.footer.concat(streams) : streams;
    this.setState(curState => {
      return {
        lastUpdatedMillis: Date.now(),
        footer: curState.footer ? curState.footer.concat(streams) : streams
      };
    });
  }
  /**
   * Done watching!
   *
   */
  done() {
    this.setState({
      isWatching: false,
      lastUpdatedMillis: Date.now()
    });
    // TODO uncapture job-tab connection?
  }
}
exports.default = LivePaginatedTable;