"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "ChangesObservable", {
    enumerable: true,
    get: function() {
        return ChangesObservable;
    }
});
const _observer = require("./observer");
const _utils = require("./utils");
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;
}
var /**
   * The list of registered ChangesObserver instances.
   *
   * @type {ChangesObserver[]}
   */ _observers = /*#__PURE__*/ new WeakMap(), /**
   * An array with default values that act as a base array that will be compared with
   * the last saved index state. The changes are generated and immediately send through
   * the newly created ChangesObserver object. Thanks to that, the observer initially has
   * all information about what indexes are currently changed.
   *
   * @type {Array}
   */ _indexMatrix = /*#__PURE__*/ new WeakMap(), /**
   * An array that holds the indexes state that is currently valid. The value is changed on every
   * index mapper cache update.
   *
   * @type {Array}
   */ _currentIndexState = /*#__PURE__*/ new WeakMap(), /**
   * The flag determines if the observable is initialized or not. Not initialized object creates
   * index matrix once while emitting new changes.
   *
   * @type {boolean}
   */ _isMatrixIndexesInitialized = /*#__PURE__*/ new WeakMap(), /**
   * The initial index value allows control from what value the index matrix array will be created.
   * Changing that value changes how the array diff generates the changes for the initial data
   * sent to the subscribers. For example, the changes can be triggered by detecting the changes
   * from `false` to `true` value or vice versa. Generally, it depends on which index map type
   * the Observable will work with. For "hiding" or "trimming" index types, it will be boolean
   * values. For various index maps, it can be anything, but I suspect that the most appropriate
   * initial value will be "undefined" in that case.
   *
   * @type {boolean}
   */ _initialIndexValue = /*#__PURE__*/ new WeakMap();
class ChangesObservable {
    /* eslint-disable jsdoc/require-description-complete-sentence */ /**
   * Creates and returns a new instance of the ChangesObserver object. The resource
   * allows subscribing to the index changes that during the code running may change.
   * Changes are emitted as an array of the index change. Each change is represented
   * separately as an object with `op`, `index`, `oldValue`, and `newValue` props.
   *
   * For example:
   * ```
   * [
   *   { op: 'replace', index: 1, oldValue: false, newValue: true },
   *   { op: 'replace', index: 3, oldValue: false, newValue: true },
   *   { op: 'insert', index: 4, oldValue: false, newValue: true },
   * ]
   * // or when the new index map changes have less indexes
   * [
   *   { op: 'replace', index: 1, oldValue: false, newValue: true },
   *   { op: 'remove', index: 4, oldValue: false, newValue: true },
   * ]
   * ```
   *
   * @returns {ChangesObserver}
   */ /* eslint-enable jsdoc/require-description-complete-sentence */ createObserver() {
        const observer = new _observer.ChangesObserver();
        _class_private_field_get(this, _observers).add(observer);
        observer.addLocalHook('unsubscribe', ()=>{
            _class_private_field_get(this, _observers).delete(observer);
        });
        observer._writeInitialChanges((0, _utils.arrayDiff)(_class_private_field_get(this, _indexMatrix), _class_private_field_get(this, _currentIndexState)));
        return observer;
    }
    /**
   * The method is an entry point for triggering new index map changes. Emitting the
   * changes triggers comparing algorithm which compares last saved state with a new
   * state. When there are some differences, the changes are sent to all subscribers.
   *
   * @param {Array} indexesState An array with index map state.
   */ emit(indexesState) {
        let currentIndexState = _class_private_field_get(this, _currentIndexState);
        if (!_class_private_field_get(this, _isMatrixIndexesInitialized) || _class_private_field_get(this, _indexMatrix).length !== indexesState.length) {
            if (indexesState.length === 0) {
                indexesState = new Array(currentIndexState.length).fill(_class_private_field_get(this, _initialIndexValue));
            } else {
                _class_private_field_set(this, _indexMatrix, new Array(indexesState.length).fill(_class_private_field_get(this, _initialIndexValue)));
            }
            if (!_class_private_field_get(this, _isMatrixIndexesInitialized)) {
                _class_private_field_set(this, _isMatrixIndexesInitialized, true);
                currentIndexState = _class_private_field_get(this, _indexMatrix);
            }
        }
        const changes = (0, _utils.arrayDiff)(currentIndexState, indexesState);
        _class_private_field_get(this, _observers).forEach((observer)=>observer._write(changes));
        _class_private_field_set(this, _currentIndexState, indexesState);
    }
    constructor({ initialIndexValue } = {}){
        _class_private_field_init(this, _observers, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _indexMatrix, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _currentIndexState, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _isMatrixIndexesInitialized, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _initialIndexValue, {
            writable: true,
            value: void 0
        });
        _class_private_field_set(this, _observers, new Set());
        _class_private_field_set(this, _indexMatrix, []);
        _class_private_field_set(this, _currentIndexState, []);
        _class_private_field_set(this, _isMatrixIndexesInitialized, false);
        _class_private_field_set(this, _initialIndexValue, false);
        _class_private_field_set(this, _initialIndexValue, initialIndexValue ?? false);
    }
}
