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_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);
}
import { arrayEach, arrayMap, arrayFilter } from '../../helpers/array.mjs';
import { mixin, objectEach } from '../../helpers/object.mjs';
import { curry } from '../../helpers/function.mjs';
import localHooks from '../../mixins/localHooks.mjs';
import ConditionCollection from './conditionCollection.mjs';
import DataFilter from './dataFilter.mjs';
import { createArrayAssertion } from './utils.mjs';
var /**
   * On before modify condition (add or remove from collection),.
   *
   * @param {number} column Column index.
   * @private
   */ _onConditionBeforeModify = /*#__PURE__*/ new WeakSet(), /**
   * On before conditions clean listener.
   *
   * @private
   */ _onConditionBeforeClean = /*#__PURE__*/ new WeakSet(), /**
   * On after conditions clean listener.
   *
   * @private
   */ _onConditionAfterClean = /*#__PURE__*/ new WeakSet();
/**
 * Class which is designed for observing changes in condition collection. When condition is changed by user at specified
 * column it's necessary to update all conditions defined after this edited one.
 *
 * Object fires `update` hook for every column conditions change.
 *
 * @private
 * @class ConditionUpdateObserver
 */ class ConditionUpdateObserver {
    /**
   * Enable grouping changes. Grouping is helpful in situations when a lot of conditions is added in one moment. Instead of
   * trigger `update` hook for every condition by adding/removing you can group this changes and call `flush` method to trigger
   * it once.
   */ groupChanges() {
        this.grouping = true;
    }
    /**
   * Flush all collected changes. This trigger `update` hook for every previously collected change from condition collection.
   */ flush() {
        this.grouping = false;
        arrayEach(this.changes, (column)=>{
            this.updateStatesAtColumn(column);
        });
        this.changes.length = 0;
    }
    /**
   * Update all related states which should be changed after invoking changes applied to current column.
   *
   * @param {number} column The column index.
   * @param {object} conditionArgsChange Object describing condition changes which can be handled by filters on `update` hook.
   * It contains keys `conditionKey` and `conditionValue` which refers to change specified key of condition to specified value
   * based on referred keys.
   */ updateStatesAtColumn(column, conditionArgsChange) {
        if (this.grouping) {
            if (this.changes.indexOf(column) === -1) {
                this.changes.push(column);
            }
            return;
        }
        const allConditions = this.conditionCollection.exportAllConditions();
        let editedColumnPosition = this.conditionCollection.getColumnStackPosition(column);
        if (editedColumnPosition === -1) {
            editedColumnPosition = this.latestEditedColumnPosition;
        }
        // Collection of all conditions defined before currently edited `column` (without edited one)
        const conditionsBefore = allConditions.slice(0, editedColumnPosition);
        // Collection of all conditions defined after currently edited `column` (with edited one)
        const conditionsAfter = allConditions.slice(editedColumnPosition);
        // Make sure that conditionAfter doesn't contain edited column conditions
        if (conditionsAfter.length && conditionsAfter[0].column === column) {
            conditionsAfter.shift();
        }
        const visibleDataFactory = curry((curriedConditionsBefore, curriedColumn, conditionsStack = [])=>{
            const splitConditionCollection = new ConditionCollection(this.hot, false);
            const curriedConditionsBeforeArray = [].concat(curriedConditionsBefore, conditionsStack);
            // Create new condition collection to determine what rows should be visible in "filter by value" box
            // in the next conditions in the chain
            splitConditionCollection.importAllConditions(curriedConditionsBeforeArray);
            const allRows = this.columnDataFactory(curriedColumn);
            let visibleRows;
            if (splitConditionCollection.isEmpty()) {
                visibleRows = allRows;
            } else {
                visibleRows = new DataFilter(splitConditionCollection, (columnData)=>this.columnDataFactory(columnData)).filter();
            }
            const rowIndexesToShow = arrayMap(visibleRows, (rowData)=>rowData.meta.row);
            const rowIndexesToShowAssertion = createArrayAssertion(rowIndexesToShow);
            splitConditionCollection.destroy();
            return arrayFilter(allRows, (rowData)=>rowIndexesToShowAssertion(rowData.meta.row));
        })(conditionsBefore);
        const editedConditions = [].concat(this.conditionCollection.getConditions(column));
        this.runLocalHooks('update', {
            editedConditionStack: {
                column,
                conditions: editedConditions
            },
            dependentConditionStacks: conditionsAfter,
            filteredRowsFactory: visibleDataFactory,
            conditionArgsChange
        });
    }
    /**
   * Destroy instance.
   */ destroy() {
        this.clearLocalHooks();
        objectEach(this, (value, property)=>{
            this[property] = null;
        });
    }
    constructor(hot, conditionCollection, columnDataFactory = ()=>[]){
        _class_private_method_init(this, _onConditionBeforeModify);
        _class_private_method_init(this, _onConditionBeforeClean);
        _class_private_method_init(this, _onConditionAfterClean);
        /**
   * Collected changes when grouping is enabled.
   *
   * @type {Array}
   * @default []
   */ this.changes = [];
        /**
   * Flag which determines if grouping events is enabled.
   *
   * @type {boolean}
   */ this.grouping = false;
        /**
   * The latest known position of edited conditions at specified column index.
   *
   * @type {number}
   * @default -1
   */ this.latestEditedColumnPosition = -1;
        /**
   * The latest known order of conditions stack.
   *
   * @type {Array}
   */ this.latestOrderStack = [];
        this.hot = hot;
        this.conditionCollection = conditionCollection;
        this.columnDataFactory = columnDataFactory;
        this.conditionCollection.addLocalHook('beforeRemove', (column)=>_class_private_method_get(this, _onConditionBeforeModify, onConditionBeforeModify).call(this, column));
        this.conditionCollection.addLocalHook('afterRemove', (column)=>this.updateStatesAtColumn(column));
        this.conditionCollection.addLocalHook('afterAdd', (column)=>this.updateStatesAtColumn(column));
        this.conditionCollection.addLocalHook('beforeClean', ()=>_class_private_method_get(this, _onConditionBeforeClean, onConditionBeforeClean).call(this));
        this.conditionCollection.addLocalHook('afterClean', ()=>_class_private_method_get(this, _onConditionAfterClean, onConditionAfterClean).call(this));
    }
}
function onConditionBeforeModify(column) {
    this.latestEditedColumnPosition = this.conditionCollection.getColumnStackPosition(column);
}
function onConditionBeforeClean() {
    this.latestOrderStack = this.conditionCollection.getFilteredColumns();
}
function onConditionAfterClean() {
    arrayEach(this.latestOrderStack, (column)=>{
        this.updateStatesAtColumn(column);
    });
}
mixin(ConditionUpdateObserver, localHooks);
export default ConditionUpdateObserver;
