"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "FocusOrder", {
    enumerable: true,
    get: function() {
        return FocusOrder;
    }
});
const _linkedList = /*#__PURE__*/ _interop_require_default(require("../../utils/dataStructures/linkedList"));
function _check_private_redeclaration(obj, privateCollection) {
    if (privateCollection.has(obj)) {
        throw new TypeError("Cannot initialize the same private elements twice on an object");
    }
}
function _class_apply_descriptor_get(receiver, descriptor) {
    if (descriptor.get) {
        return descriptor.get.call(receiver);
    }
    return descriptor.value;
}
function _class_apply_descriptor_set(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;
    }
}
function _class_extract_field_descriptor(receiver, privateMap, action) {
    if (!privateMap.has(receiver)) {
        throw new TypeError("attempted to " + action + " private field on non-instance");
    }
    return privateMap.get(receiver);
}
function _class_private_field_get(receiver, privateMap) {
    var descriptor = _class_extract_field_descriptor(receiver, privateMap, "get");
    return _class_apply_descriptor_get(receiver, descriptor);
}
function _class_private_field_init(obj, privateMap, value) {
    _check_private_redeclaration(obj, privateMap);
    privateMap.set(obj, value);
}
function _class_private_field_set(receiver, privateMap, value) {
    var descriptor = _class_extract_field_descriptor(receiver, privateMap, "set");
    _class_apply_descriptor_set(receiver, descriptor, value);
    return value;
}
function _class_private_method_get(receiver, privateSet, fn) {
    if (!privateSet.has(receiver)) {
        throw new TypeError("attempted to get private field on non-instance");
    }
    return fn;
}
function _class_private_method_init(obj, privateSet) {
    _check_private_redeclaration(obj, privateSet);
    privateSet.add(obj);
}
function _interop_require_default(obj) {
    return obj && obj.__esModule ? obj : {
        default: obj
    };
}
var /**
   * 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}
   */ _cellsHorizontalOrder = /*#__PURE__*/ new WeakMap(), /**
   * 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}
   */ _cellsVerticalOrder = /*#__PURE__*/ new WeakMap(), /**
   * The currently highlighted cell within the horizontal linked list.
   *
   * @type {NodeStructure | null}
   */ _currentHorizontalLinkedNode = /*#__PURE__*/ new WeakMap(), /**
   * The currently highlighted cell within the vertical linked list.
   *
   * @type {NodeStructure | null}
   */ _currentVerticalLinkedNode = /*#__PURE__*/ new WeakMap(), /**
   * The merged cells getter function.
   *
   * @type {function(): {row: number, col: number, rowspan: number, colspan: number} | null}}
   */ _mergedCellsGetter = /*#__PURE__*/ new WeakMap(), /**
   * The row index mapper.
   *
   * @type {IndexMapper}
   */ _rowIndexMapper = /*#__PURE__*/ new WeakMap(), /**
   * The column index mapper.
   *
   * @type {IndexMapper}
   */ _columnIndexMapper = /*#__PURE__*/ new WeakMap(), /**
   * Pushes a new node to the provided list order.
   *
   * @param {object} options The options object.
   * @param {CellRange} options.selectedRange The selected range to build the focus order for.
   * @param {number} options.selectionLayer The selection layer index.
   * @param {LinkedList} options.listOrder The list order to push the node to.
   * @param {WeakSet} options.mergeCellsVisitor The set of visited cells.
   * @param {number} options.row The visual row index.
   * @param {number} options.column The visual column index.
   * @returns {NodeStructure | null}
   */ _pushOrderNode = /*#__PURE__*/ new WeakSet();
class FocusOrder {
    /**
   * Gets the currently selected node data from the vertical focus order list.
   *
   * @returns {NodeStructure}
   */ getCurrentVerticalNode() {
        return _class_private_field_get(this, _currentVerticalLinkedNode).data;
    }
    /**
   * Gets the first node data from the vertical focus order list.
   *
   * @returns {NodeStructure}
   */ getFirstVerticalNode() {
        return _class_private_field_get(this, _cellsVerticalOrder).first.data;
    }
    /**
   * Gets the next selected node data from the vertical focus order list.
   *
   * @returns {NodeStructure}
   */ getNextVerticalNode() {
        return _class_private_field_get(this, _currentVerticalLinkedNode).next.data;
    }
    /**
   * Gets the previous selected node data from the vertical focus order list.
   *
   * @returns {NodeStructure}
   */ getPrevVerticalNode() {
        return _class_private_field_get(this, _currentVerticalLinkedNode).prev.data;
    }
    /**
   * Gets the currently selected node data from the horizontal focus order list.
   *
   * @returns {NodeStructure}
   */ getCurrentHorizontalNode() {
        return _class_private_field_get(this, _currentHorizontalLinkedNode).data;
    }
    /**
   * Gets the first node data from the horizontal focus order list.
   *
   * @returns {NodeStructure}
   */ getFirstHorizontalNode() {
        return _class_private_field_get(this, _cellsHorizontalOrder).first.data;
    }
    /**
   * Gets the next selected node data from the horizontal focus order list.
   *
   * @returns {NodeStructure}
   */ getNextHorizontalNode() {
        return _class_private_field_get(this, _currentHorizontalLinkedNode).next.data;
    }
    /**
   * Gets the previous selected node data from the horizontal focus order list.
   *
   * @returns {NodeStructure}
   */ getPrevHorizontalNode() {
        return _class_private_field_get(this, _currentHorizontalLinkedNode).prev.data;
    }
    /**
   * Sets the previous node from the vertical focus order list as active.
   */ setPrevNodeAsActive() {
        _class_private_field_set(this, _currentVerticalLinkedNode, _class_private_field_get(this, _currentVerticalLinkedNode).prev);
        _class_private_field_set(this, _currentHorizontalLinkedNode, _class_private_field_get(this, _currentHorizontalLinkedNode).prev);
    }
    /**
   * Sets the previous node from the horizontal focus order list as active.
   */ setNextNodeAsActive() {
        _class_private_field_set(this, _currentVerticalLinkedNode, _class_private_field_get(this, _currentVerticalLinkedNode).next);
        _class_private_field_set(this, _currentHorizontalLinkedNode, _class_private_field_get(this, _currentHorizontalLinkedNode).next);
    }
    /**
   * Rebuilds the focus order list based on the provided selection.
   *
   * @param {CellRange[]} selectedRanges The selected ranges to build the focus order for.
   */ buildFocusOrder(selectedRanges) {
        _class_private_field_set(this, _cellsHorizontalOrder, new _linkedList.default());
        selectedRanges.forEach((range, selectionLayer)=>{
            const visitedHorizontalCells = new WeakSet();
            const topStart = range.getTopStartCorner();
            const bottomEnd = range.getBottomEndCorner();
            for(let r = topStart.row; r <= bottomEnd.row; r++){
                if (_class_private_field_get(this, _rowIndexMapper).isHidden(r)) {
                    continue;
                }
                for(let c = topStart.col; c <= bottomEnd.col; c++){
                    if (_class_private_field_get(this, _columnIndexMapper).isHidden(c)) {
                        continue;
                    }
                    const node = _class_private_method_get(this, _pushOrderNode, pushOrderNode).call(this, {
                        selectedRange: range,
                        selectionLayer,
                        listOrder: _class_private_field_get(this, _cellsHorizontalOrder),
                        mergeCellsVisitor: visitedHorizontalCells,
                        row: r,
                        column: c
                    });
                    if (node) {
                        _class_private_field_set(this, _currentHorizontalLinkedNode, node);
                    }
                }
            }
        });
        // create circular linked list
        if (_class_private_field_get(this, _cellsHorizontalOrder).first) {
            _class_private_field_get(this, _cellsHorizontalOrder).first.prev = _class_private_field_get(this, _cellsHorizontalOrder).last;
            _class_private_field_get(this, _cellsHorizontalOrder).last.next = _class_private_field_get(this, _cellsHorizontalOrder).first;
        }
        _class_private_field_set(this, _cellsVerticalOrder, new _linkedList.default());
        selectedRanges.forEach((range, selectionLayer)=>{
            const visitedVerticalCells = new WeakSet();
            const topStart = range.getTopStartCorner();
            const bottomEnd = range.getBottomEndCorner();
            for(let c = topStart.col; c <= bottomEnd.col; c++){
                if (_class_private_field_get(this, _columnIndexMapper).isHidden(c)) {
                    continue;
                }
                for(let r = topStart.row; r <= bottomEnd.row; r++){
                    if (_class_private_field_get(this, _rowIndexMapper).isHidden(r)) {
                        continue;
                    }
                    const node = _class_private_method_get(this, _pushOrderNode, pushOrderNode).call(this, {
                        selectedRange: range,
                        selectionLayer,
                        listOrder: _class_private_field_get(this, _cellsVerticalOrder),
                        mergeCellsVisitor: visitedVerticalCells,
                        row: r,
                        column: c
                    });
                    if (node) {
                        _class_private_field_set(this, _currentVerticalLinkedNode, node);
                    }
                }
            }
        });
        // create circular linked list
        if (_class_private_field_get(this, _cellsVerticalOrder).first) {
            _class_private_field_get(this, _cellsVerticalOrder).first.prev = _class_private_field_get(this, _cellsVerticalOrder).last;
            _class_private_field_get(this, _cellsVerticalOrder).last.next = _class_private_field_get(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.
   * @param {number} selectionLayerIndex The index of the selection layer to which the focus should be marked as active.
   * @returns {FocusOrder}
   */ setActiveNode(row, column, selectionLayerIndex) {
        _class_private_field_get(this, _cellsHorizontalOrder).inorder((node)=>{
            const { selectionLayer, rowStart, rowEnd, colStart, colEnd } = node.data;
            if (selectionLayer === selectionLayerIndex && row >= rowStart && row <= rowEnd && column >= colStart && column <= colEnd) {
                _class_private_field_set(this, _currentHorizontalLinkedNode, node);
                return true;
            }
        });
        _class_private_field_get(this, _cellsVerticalOrder).inorder((node)=>{
            const { selectionLayer, rowStart, rowEnd, colStart, colEnd } = node.data;
            if (selectionLayer === selectionLayerIndex && row >= rowStart && row <= rowEnd && column >= colStart && column <= colEnd) {
                _class_private_field_set(this, _currentVerticalLinkedNode, node);
                return true;
            }
        });
        return this;
    }
    constructor({ mergedCellsGetter, rowIndexMapper, columnIndexMapper }){
        _class_private_method_init(this, _pushOrderNode);
        _class_private_field_init(this, _cellsHorizontalOrder, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _cellsVerticalOrder, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _currentHorizontalLinkedNode, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _currentVerticalLinkedNode, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _mergedCellsGetter, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _rowIndexMapper, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _columnIndexMapper, {
            writable: true,
            value: void 0
        });
        _class_private_field_set(this, _cellsHorizontalOrder, new _linkedList.default());
        _class_private_field_set(this, _cellsVerticalOrder, new _linkedList.default());
        _class_private_field_set(this, _currentHorizontalLinkedNode, null);
        _class_private_field_set(this, _currentVerticalLinkedNode, null);
        _class_private_field_set(this, _mergedCellsGetter, null);
        _class_private_field_set(this, _rowIndexMapper, null);
        _class_private_field_set(this, _columnIndexMapper, null);
        _class_private_field_set(this, _mergedCellsGetter, mergedCellsGetter);
        _class_private_field_set(this, _rowIndexMapper, rowIndexMapper);
        _class_private_field_set(this, _columnIndexMapper, columnIndexMapper);
    }
}
function pushOrderNode({ selectedRange, selectionLayer, listOrder, mergeCellsVisitor, row, column }) {
    const topStart = selectedRange.getTopStartCorner();
    const bottomEnd = selectedRange.getBottomEndCorner();
    const highlight = selectedRange.highlight.clone().normalize();
    const mergeParent = _class_private_field_get(this, _mergedCellsGetter).call(this, row, column);
    if (mergeParent && mergeCellsVisitor.has(mergeParent)) {
        return null;
    }
    const node = {
        selectionLayer,
        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;
}
