import "core-js/modules/es.array.push.js";
import "core-js/modules/es.error.cause.js";
function _classPrivateMethodInitSpec(obj, privateSet) { _checkPrivateRedeclaration(obj, privateSet); privateSet.add(obj); }
function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }
function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
function _classPrivateMethodGet(receiver, privateSet, fn) { if (!privateSet.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return fn; }
function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); }
function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; }
function _classPrivateFieldSet(receiver, privateMap, value) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "set"); _classApplyDescriptorSet(receiver, descriptor, value); return value; }
function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to " + action + " private field on non-instance"); } return privateMap.get(receiver); }
function _classApplyDescriptorSet(receiver, descriptor, value) { if (descriptor.set) { descriptor.set.call(receiver, value); } else { if (!descriptor.writable) { throw new TypeError("attempted to set read only private field"); } descriptor.value = value; } }
import LinkedList from "../../utils/dataStructures/linkedList.mjs";
/**
 * Class responsible for providing the correct focus order (vertical and horizontal) within a selection that
 * contains merged cells.
 */
var _cellsHorizontalOrder = /*#__PURE__*/new WeakMap();
var _cellsVerticalOrder = /*#__PURE__*/new WeakMap();
var _currentHorizontalLinkedNode = /*#__PURE__*/new WeakMap();
var _currentVerticalLinkedNode = /*#__PURE__*/new WeakMap();
var _mergedCellsGetter = /*#__PURE__*/new WeakMap();
var _rowIndexMapper = /*#__PURE__*/new WeakMap();
var _columnIndexMapper = /*#__PURE__*/new WeakMap();
var _pushOrderNode = /*#__PURE__*/new WeakSet();
export class FocusOrder {
  constructor(_ref) {
    let {
      mergedCellsGetter,
      rowIndexMapper,
      columnIndexMapper
    } = _ref;
    /**
     * Pushes a new node to the provided list order.
     *
     * @param {CellRange} selectedRange The selected range to build the focus order for.
     * @param {LinkedList} listOrder The list order to push the node to.
     * @param {WeakSet} mergeCellsVisitor The set of visited cells.
     * @param {number} row The visual row index.
     * @param {number} column The visual column index.
     * @returns {NodeStructure | null}
     */
    _classPrivateMethodInitSpec(this, _pushOrderNode);
    /**
     * The linked list of the all cells within the current selection in horizontal order. The list is
     * recreated every time the selection is changed.
     *
     * @type {LinkedList}
     */
    _classPrivateFieldInitSpec(this, _cellsHorizontalOrder, {
      writable: true,
      value: new LinkedList()
    });
    /**
     * The linked list of the all cells within the current selection in horizontal order. The list is
     * recreated every time the selection is changed.
     *
     * @type {LinkedList}
     */
    _classPrivateFieldInitSpec(this, _cellsVerticalOrder, {
      writable: true,
      value: new LinkedList()
    });
    /**
     * The currently highlighted cell within the horizontal linked list.
     *
     * @type {NodeStructure | null}
     */
    _classPrivateFieldInitSpec(this, _currentHorizontalLinkedNode, {
      writable: true,
      value: null
    });
    /**
     * The currently highlighted cell within the vertical linked list.
     *
     * @type {NodeStructure | null}
     */
    _classPrivateFieldInitSpec(this, _currentVerticalLinkedNode, {
      writable: true,
      value: null
    });
    /**
     * The merged cells getter function.
     *
     * @type {function(): {row: number, col: number, rowspan: number, colspan: number} | null}}
     */
    _classPrivateFieldInitSpec(this, _mergedCellsGetter, {
      writable: true,
      value: null
    });
    /**
     * The row index mapper.
     *
     * @type {IndexMapper}
     */
    _classPrivateFieldInitSpec(this, _rowIndexMapper, {
      writable: true,
      value: null
    });
    /**
     * The column index mapper.
     *
     * @type {IndexMapper}
     */
    _classPrivateFieldInitSpec(this, _columnIndexMapper, {
      writable: true,
      value: null
    });
    _classPrivateFieldSet(this, _mergedCellsGetter, mergedCellsGetter);
    _classPrivateFieldSet(this, _rowIndexMapper, rowIndexMapper);
    _classPrivateFieldSet(this, _columnIndexMapper, columnIndexMapper);
  }

  /**
   * Gets the currently selected node data from the vertical focus order list.
   *
   * @returns {NodeStructure}
   */
  getCurrentVerticalNode() {
    return _classPrivateFieldGet(this, _currentVerticalLinkedNode).data;
  }

  /**
   * Gets the first node data from the vertical focus order list.
   *
   * @returns {NodeStructure}
   */
  getFirstVerticalNode() {
    return _classPrivateFieldGet(this, _cellsVerticalOrder).first.data;
  }

  /**
   * Gets the next selected node data from the vertical focus order list.
   *
   * @returns {NodeStructure}
   */
  getNextVerticalNode() {
    return _classPrivateFieldGet(this, _currentVerticalLinkedNode).next.data;
  }

  /**
   * Gets the previous selected node data from the vertical focus order list.
   *
   * @returns {NodeStructure}
   */
  getPrevVerticalNode() {
    return _classPrivateFieldGet(this, _currentVerticalLinkedNode).prev.data;
  }

  /**
   * Gets the currently selected node data from the horizontal focus order list.
   *
   * @returns {NodeStructure}
   */
  getCurrentHorizontalNode() {
    return _classPrivateFieldGet(this, _currentHorizontalLinkedNode).data;
  }

  /**
   * Gets the first node data from the horizontal focus order list.
   *
   * @returns {NodeStructure}
   */
  getFirstHorizontalNode() {
    return _classPrivateFieldGet(this, _cellsHorizontalOrder).first.data;
  }

  /**
   * Gets the next selected node data from the horizontal focus order list.
   *
   * @returns {NodeStructure}
   */
  getNextHorizontalNode() {
    return _classPrivateFieldGet(this, _currentHorizontalLinkedNode).next.data;
  }

  /**
   * Gets the previous selected node data from the horizontal focus order list.
   *
   * @returns {NodeStructure}
   */
  getPrevHorizontalNode() {
    return _classPrivateFieldGet(this, _currentHorizontalLinkedNode).prev.data;
  }

  /**
   * Sets the previous node from the vertical focus order list as active.
   */
  setPrevNodeAsActive() {
    _classPrivateFieldSet(this, _currentVerticalLinkedNode, _classPrivateFieldGet(this, _currentVerticalLinkedNode).prev);
    _classPrivateFieldSet(this, _currentHorizontalLinkedNode, _classPrivateFieldGet(this, _currentHorizontalLinkedNode).prev);
  }

  /**
   * Sets the previous node from the horizontal focus order list as active.
   */
  setNextNodeAsActive() {
    _classPrivateFieldSet(this, _currentVerticalLinkedNode, _classPrivateFieldGet(this, _currentVerticalLinkedNode).next);
    _classPrivateFieldSet(this, _currentHorizontalLinkedNode, _classPrivateFieldGet(this, _currentHorizontalLinkedNode).next);
  }

  /**
   * Rebuilds the focus order list based on the provided selection.
   *
   * @param {CellRange} selectedRange The selected range to build the focus order for.
   */
  buildFocusOrder(selectedRange) {
    const topStart = selectedRange.getTopStartCorner();
    const bottomEnd = selectedRange.getBottomEndCorner();
    const visitedHorizontalCells = new WeakSet();
    _classPrivateFieldSet(this, _cellsHorizontalOrder, new LinkedList());
    for (let r = topStart.row; r <= bottomEnd.row; r++) {
      if (_classPrivateFieldGet(this, _rowIndexMapper).isHidden(r)) {
        // eslint-disable-next-line no-continue
        continue;
      }
      for (let c = topStart.col; c <= bottomEnd.col; c++) {
        if (_classPrivateFieldGet(this, _columnIndexMapper).isHidden(c)) {
          // eslint-disable-next-line no-continue
          continue;
        }
        const node = _classPrivateMethodGet(this, _pushOrderNode, _pushOrderNode2).call(this, selectedRange, _classPrivateFieldGet(this, _cellsHorizontalOrder), visitedHorizontalCells, r, c);
        if (node) {
          _classPrivateFieldSet(this, _currentHorizontalLinkedNode, node);
        }
      }
    }

    // create circular linked list
    if (_classPrivateFieldGet(this, _cellsHorizontalOrder).first) {
      _classPrivateFieldGet(this, _cellsHorizontalOrder).first.prev = _classPrivateFieldGet(this, _cellsHorizontalOrder).last;
      _classPrivateFieldGet(this, _cellsHorizontalOrder).last.next = _classPrivateFieldGet(this, _cellsHorizontalOrder).first;
    }
    const visitedVerticalCells = new WeakSet();
    _classPrivateFieldSet(this, _cellsVerticalOrder, new LinkedList());
    for (let c = topStart.col; c <= bottomEnd.col; c++) {
      if (_classPrivateFieldGet(this, _columnIndexMapper).isHidden(c)) {
        // eslint-disable-next-line no-continue
        continue;
      }
      for (let r = topStart.row; r <= bottomEnd.row; r++) {
        if (_classPrivateFieldGet(this, _rowIndexMapper).isHidden(r)) {
          // eslint-disable-next-line no-continue
          continue;
        }
        const node = _classPrivateMethodGet(this, _pushOrderNode, _pushOrderNode2).call(this, selectedRange, _classPrivateFieldGet(this, _cellsVerticalOrder), visitedVerticalCells, r, c);
        if (node) {
          _classPrivateFieldSet(this, _currentVerticalLinkedNode, node);
        }
      }
    }

    // create circular linked list
    if (_classPrivateFieldGet(this, _cellsVerticalOrder).first) {
      _classPrivateFieldGet(this, _cellsVerticalOrder).first.prev = _classPrivateFieldGet(this, _cellsVerticalOrder).last;
      _classPrivateFieldGet(this, _cellsVerticalOrder).last.next = _classPrivateFieldGet(this, _cellsVerticalOrder).first;
    }
  }
  /**
   * Sets the active node based on the provided row and column.
   *
   * @param {number} row The visual row index.
   * @param {number} column The visual column index.
   * @returns {FocusOrder}
   */
  setActiveNode(row, column) {
    _classPrivateFieldGet(this, _cellsHorizontalOrder).inorder(node => {
      const {
        rowStart,
        rowEnd,
        colStart,
        colEnd
      } = node.data;
      if (row >= rowStart && row <= rowEnd && column >= colStart && column <= colEnd) {
        _classPrivateFieldSet(this, _currentHorizontalLinkedNode, node);
        return false;
      }
    });
    _classPrivateFieldGet(this, _cellsVerticalOrder).inorder(node => {
      const {
        rowStart,
        rowEnd,
        colStart,
        colEnd
      } = node.data;
      if (row >= rowStart && row <= rowEnd && column >= colStart && column <= colEnd) {
        _classPrivateFieldSet(this, _currentVerticalLinkedNode, node);
        return false;
      }
    });
    return this;
  }
}
function _pushOrderNode2(selectedRange, listOrder, mergeCellsVisitor, row, column) {
  const topStart = selectedRange.getTopStartCorner();
  const bottomEnd = selectedRange.getBottomEndCorner();
  const highlight = selectedRange.highlight.clone().normalize();
  const mergeParent = _classPrivateFieldGet(this, _mergedCellsGetter).call(this, row, column);
  if (mergeParent && mergeCellsVisitor.has(mergeParent)) {
    return null;
  }
  const node = {
    colStart: column,
    colEnd: column,
    rowStart: row,
    rowEnd: row
  };
  if (mergeParent) {
    mergeCellsVisitor.add(mergeParent);
    if (mergeParent.row < topStart.row || mergeParent.row + mergeParent.rowspan - 1 > bottomEnd.row || mergeParent.col < topStart.col || mergeParent.col + mergeParent.colspan - 1 > bottomEnd.col) {
      return null;
    }
    node.colStart = mergeParent.col;
    node.colEnd = mergeParent.col + mergeParent.colspan - 1;
    node.rowStart = mergeParent.row;
    node.rowEnd = mergeParent.row + mergeParent.rowspan - 1;
  }
  const linkedNode = listOrder.push(node);
  if (row === highlight.row && column === highlight.col || mergeParent && highlight.row >= mergeParent.row && highlight.row <= mergeParent.row + mergeParent.rowspan - 1 && highlight.col >= mergeParent.col && highlight.col <= mergeParent.col + mergeParent.colspan - 1) {
    return linkedNode;
  }
  return null;
}