"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "default", {
    enumerable: true,
    get: function() {
        return HeadersTree;
    }
});
const _array = require("../../../helpers/array");
const _tree = /*#__PURE__*/ _interop_require_default(require("../../../utils/dataStructures/tree"));
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 _interop_require_default(obj) {
    return obj && obj.__esModule ? obj : {
        default: obj
    };
}
var /**
   * The collection of nested headers settings structured into trees. The root trees are stored
   * under the visual column index.
   *
   * @private
   * @type {Map<number, TreeNode>}
   */ _rootNodes = /*#__PURE__*/ new WeakMap(), /**
   * A map that translates the visual column indexes that intersect the range
   * defined by the header colspan width to the root index.
   *
   * @private
   * @type {Map<number, number>}
   */ _rootsIndex = /*#__PURE__*/ new WeakMap(), /**
   * The instance of the SourceSettings class.
   *
   * @private
   * @type {SourceSettings}
   */ _sourceSettings = /*#__PURE__*/ new WeakMap();
class HeadersTree {
    /**
   * Gets an array of the all root nodes.
   *
   * @returns {TreeNode[]}
   */ getRoots() {
        return Array.from(_class_private_field_get(this, _rootNodes).values());
    }
    /**
   * Gets a root node by specified visual column index.
   *
   * @param {number} columnIndex A visual column index.
   * @returns {TreeNode|undefined}
   */ getRootByColumn(columnIndex) {
        let node;
        if (_class_private_field_get(this, _rootsIndex).has(columnIndex)) {
            node = _class_private_field_get(this, _rootNodes).get(_class_private_field_get(this, _rootsIndex).get(columnIndex));
        }
        return node;
    }
    /**
   * Gets a tree node by its position in the grid settings.
   *
   * @param {number} headerLevel Header level index (there is support only for positive values).
   * @param {number} columnIndex A visual column index.
   * @returns {TreeNode|undefined}
   */ getNode(headerLevel, columnIndex) {
        const rootNode = this.getRootByColumn(columnIndex);
        if (!rootNode) {
            return;
        }
        // Normalize the visual column index to a 0-based system for a specific "box" defined
        // by root node colspan width.
        const normColumnIndex = columnIndex - _class_private_field_get(this, _rootsIndex).get(columnIndex);
        let columnCursor = 0;
        let treeNode;
        // Collect all parent nodes that depend on the collapsed node.
        rootNode.walkDown((node)=>{
            const { data: { origColspan, headerLevel: nodeHeaderLevel } } = node;
            if (headerLevel === nodeHeaderLevel) {
                if (normColumnIndex >= columnCursor && normColumnIndex <= columnCursor + origColspan - 1) {
                    treeNode = node;
                    treeNode.data.isRoot = columnIndex === treeNode.data.columnIndex;
                    return false; // Cancel tree traversing.
                }
                columnCursor += origColspan;
            }
        });
        return treeNode;
    }
    /**
   * Builds (or rebuilds if called again) root nodes indexes.
   */ rebuildTreeIndex() {
        let columnIndex = 0;
        _class_private_field_get(this, _rootsIndex).clear();
        (0, _array.arrayEach)(_class_private_field_get(this, _rootNodes), ([, { data: { colspan } }])=>{
            // Map tree range (colspan range/width) into visual column index of the root node.
            for(let i = columnIndex; i < columnIndex + colspan; i++){
                _class_private_field_get(this, _rootsIndex).set(i, columnIndex);
            }
            columnIndex += colspan;
        });
    }
    /**
   * Builds trees based on SourceSettings class. Calling a method causes clearing the tree state built
   * from the previous call.
   */ buildTree() {
        this.clear();
        const columnsCount = _class_private_field_get(this, _sourceSettings).getColumnsCount();
        let columnIndex = 0;
        while(columnIndex < columnsCount){
            const columnSettings = _class_private_field_get(this, _sourceSettings).getHeaderSettings(0, columnIndex);
            const rootNode = new _tree.default();
            _class_private_field_get(this, _rootNodes).set(columnIndex, rootNode);
            this.buildLeaves(rootNode, columnIndex, 0, columnSettings.origColspan);
            columnIndex += columnSettings.origColspan;
        }
        this.rebuildTreeIndex();
    }
    /**
   * Builds leaves for specified tree node.
   *
   * @param {TreeNode} parentNode A node to which the leaves applies.
   * @param {number} columnIndex A visual column index.
   * @param {number} headerLevel Currently processed header level.
   * @param {number} [extractionLength=1] Determines column extraction length for node children.
   */ buildLeaves(parentNode, columnIndex, headerLevel, extractionLength = 1) {
        const columnsSettings = _class_private_field_get(this, _sourceSettings).getHeadersSettings(headerLevel, columnIndex, extractionLength);
        headerLevel += 1;
        (0, _array.arrayEach)(columnsSettings, (columnSettings)=>{
            const nodeData = {
                ...columnSettings,
                /**
         * The header level (tree node depth level).
         *
         * @type {number}
         */ headerLevel: headerLevel - 1,
                /**
         * A visual column index.
         *
         * @type {number}
         */ columnIndex
            };
            let node;
            if (headerLevel === 1) {
                parentNode.data = nodeData;
                node = parentNode;
            } else {
                node = new _tree.default(nodeData);
                parentNode.addChild(node);
            }
            if (headerLevel < _class_private_field_get(this, _sourceSettings).getLayersCount()) {
                this.buildLeaves(node, columnIndex, headerLevel, columnSettings.origColspan);
            }
            columnIndex += columnSettings.origColspan;
        });
    }
    /**
   * Clears the tree to the initial state.
   */ clear() {
        _class_private_field_get(this, _rootNodes).clear();
        _class_private_field_get(this, _rootsIndex).clear();
    }
    constructor(sourceSettings){
        _class_private_field_init(this, _rootNodes, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _rootsIndex, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _sourceSettings, {
            writable: true,
            value: void 0
        });
        _class_private_field_set(this, _rootNodes, new Map());
        _class_private_field_set(this, _rootsIndex, new Map());
        _class_private_field_set(this, _sourceSettings, null);
        _class_private_field_set(this, _sourceSettings, sourceSettings);
    }
}
