"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
function _export(target, all) {
    for(var name in all)Object.defineProperty(target, name, {
        enumerable: true,
        get: Object.getOwnPropertyDescriptor(all, name).get
    });
}
_export(exports, {
    get HEADER_CONFIGURABLE_PROPS () {
        return HEADER_CONFIGURABLE_PROPS;
    },
    get default () {
        return SourceSettings;
    }
});
const _object = require("../../../helpers/object");
const _errors = require("../../../helpers/errors");
const _array = require("../../../helpers/array");
const _settingsNormalizer = require("./settingsNormalizer");
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;
}
const HEADER_CONFIGURABLE_PROPS = [
    'label',
    'collapsible'
];
var /**
   * The normalized source data (normalized user-defined settings for nested headers).
   *
   * @private
   * @type {Array[]}
   */ _data = /*#__PURE__*/ new WeakMap(), /**
   * The total length of the nested header layers.
   *
   * @private
   * @type {number}
   */ _dataLength = /*#__PURE__*/ new WeakMap(), /**
   * Columns count limit value trims source settings to that value. If columns
   * count limit intersects nested header, the header's colspan value is reduced
   * to keep the whole structure stable (trimmed precisely where the limit is set).
   *
   * @type {number}
   */ _columnsLimit = /*#__PURE__*/ new WeakMap();
class SourceSettings {
    /**
   * Sets columns limit to the source settings will be trimmed. All headers which
   * overlap the column limit will be reduced to keep the structure solid.
   *
   * @param {number} columnsCount The number of columns to limit to.
   */ setColumnsLimit(columnsCount) {
        _class_private_field_set(this, _columnsLimit, columnsCount);
    }
    /**
   * Sets a new nested header configuration.
   *
   * @param {Array[]} [nestedHeadersSettings=[]] The user-defined nested headers settings.
   */ setData(nestedHeadersSettings = []) {
        _class_private_field_set(this, _data, (0, _settingsNormalizer.normalizeSettings)(nestedHeadersSettings, _class_private_field_get(this, _columnsLimit)));
        _class_private_field_set(this, _dataLength, _class_private_field_get(this, _data).length);
    }
    /**
   * Gets normalized source settings.
   *
   * @returns {Array[]}
   */ getData() {
        return _class_private_field_get(this, _data);
    }
    /**
   * Merges settings with current source settings.
   *
   * @param {object[]} additionalSettings An array of objects with `row`, `col` and additional
   *                                      properties to merge with current source settings.
   */ mergeWith(additionalSettings) {
        (0, _array.arrayEach)(additionalSettings, ({ row, col, ...rest })=>{
            const headerSettings = this.getHeaderSettings(row, col);
            if (headerSettings !== null) {
                (0, _object.extend)(headerSettings, rest, HEADER_CONFIGURABLE_PROPS);
            }
        });
    }
    /**
   * Maps the current state with a callback. For each source settings the callback function
   * is called. If the function returns value that value is merged with the source settings.
   *
   * @param {Function} callback A function that is called for every header settings.
   *                            Each time the callback is called, the returned value extends
   *                            header settings.
   */ map(callback) {
        (0, _array.arrayEach)(_class_private_field_get(this, _data), (header)=>{
            (0, _array.arrayEach)(header, (headerSettings)=>{
                const propsToExtend = callback({
                    ...headerSettings
                });
                if ((0, _object.isObject)(propsToExtend)) {
                    (0, _object.extend)(headerSettings, propsToExtend, HEADER_CONFIGURABLE_PROPS);
                }
            });
        });
    }
    /**
   * Gets source column header settings for a specified header. The returned
   * object contains information about the header label, its colspan length,
   * or if it is hidden in the header renderers.
   *
   * @param {number} headerLevel Header level (0 = most distant to the table).
   * @param {number} columnIndex A visual column index.
   * @returns {object|null}
   */ getHeaderSettings(headerLevel, columnIndex) {
        if (headerLevel >= _class_private_field_get(this, _dataLength) || headerLevel < 0) {
            return null;
        }
        const headersSettings = _class_private_field_get(this, _data)[headerLevel];
        if (Array.isArray(headersSettings) === false || columnIndex >= headersSettings.length) {
            return null;
        }
        return headersSettings[columnIndex] ?? null;
    }
    /**
   * Gets source of column headers settings for specified headers. If the retrieved column
   * settings overlap the range "box" determined by "columnIndex" and "columnsLength"
   * the exception will be thrown.
   *
   * @param {number} headerLevel Header level (0 = most distant to the table).
   * @param {number} columnIndex A visual column index from which the settings will be extracted.
   * @param {number} [columnsLength=1] The number of columns involved in the extraction of settings.
   * @returns {object}
   */ getHeadersSettings(headerLevel, columnIndex, columnsLength = 1) {
        const headersSettingsChunks = [];
        if (headerLevel >= _class_private_field_get(this, _dataLength) || headerLevel < 0) {
            return headersSettingsChunks;
        }
        const headersSettings = _class_private_field_get(this, _data)[headerLevel];
        let currentLength = 0;
        for(let i = columnIndex; i < headersSettings.length; i++){
            const headerSettings = headersSettings[i];
            if (headerSettings.isPlaceholder) {
                (0, _errors.throwWithCause)('The first column settings cannot overlap the other header layers');
            }
            currentLength += headerSettings.colspan;
            headersSettingsChunks.push(headerSettings);
            if (headerSettings.colspan > 1) {
                i += headerSettings.colspan - 1;
            }
            // We met the current sum of the child colspans
            if (currentLength === columnsLength) {
                break;
            }
            // We exceeds the current sum of the child colspans, the last columns colspan overlaps the "columnsLength" length.
            if (currentLength > columnsLength) {
                (0, _errors.throwWithCause)('The last column settings cannot overlap the other header layers');
            }
        }
        return headersSettingsChunks;
    }
    /**
   * Gets a total number of headers levels.
   *
   * @returns {number}
   */ getLayersCount() {
        return _class_private_field_get(this, _dataLength);
    }
    /**
   * Gets a total number of columns count.
   *
   * @returns {number}
   */ getColumnsCount() {
        return _class_private_field_get(this, _dataLength) > 0 ? _class_private_field_get(this, _data)[0].length : 0;
    }
    /**
   * Clears the data.
   */ clear() {
        _class_private_field_set(this, _data, []);
        _class_private_field_set(this, _dataLength, 0);
    }
    constructor(){
        _class_private_field_init(this, _data, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _dataLength, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _columnsLimit, {
            writable: true,
            value: void 0
        });
        _class_private_field_set(this, _data, []);
        _class_private_field_set(this, _dataLength, 0);
        _class_private_field_set(this, _columnsLimit, Infinity);
    }
}
