"use strict";
var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ID_SYSTEM_MERGE = void 0;
const Primitive_1 = require("../Primitive");
const primitive_1 = require("../spec/primitive");
const f_1 = require("../system/core/object/ForEachKeyValue/f");
const object_1 = require("../util/object");
exports.ID_SYSTEM_MERGE = '_d2c4b19b-aa58-438b-8541-9df478b80aa3';
class Merge extends Primitive_1.Primitive {
    constructor(system) {
        super({}, {}, system, exports.ID_SYSTEM_MERGE);
        this._current = undefined;
        this._loop_invalid_o_count = 0;
        this._loop_invalid_o = new Set();
        this._reset = () => {
            this._current = undefined;
        };
        this._play = () => {
            this._forward_if_ready();
        };
        this.addListener('reset', this._reset);
        this.addListener('play', this._play);
        this.play();
    }
    onInputRemoved(name, input) {
        super.onInputRemoved(name, input);
        if (name === this._current) {
            this._current = undefined;
        }
    }
    onInputRenamed(name, newName, opt, newOpt) {
        super.onInputRenamed(name, newName, opt, newOpt);
        const input = this.getInput(newName);
        if (!input.empty() || name === this._current) {
            this._current = newName;
        }
    }
    onOutputRenamed(name, newName, opt, newOpt) {
        super.onOutputRenamed(name, newName, opt, newOpt);
    }
    onInputSet(name, input, opt, propagate) {
        super.onInputSet(name, input, opt, propagate);
        if (!input.empty()) {
            this._current = name;
        }
    }
    onOutputSet(name, output, opt, propagate) {
        super.onOutputSet(name, output, opt, propagate);
        if (this._current !== undefined) {
            const data = this._i[this._current];
            if (propagate && data !== undefined) {
                this._forward_(output, data);
                this._backward_if_ready();
            }
        }
    }
    onOutputRemoved(name) { }
    _on_input_data(name) {
        // console.log('Merge', '_on_input_data', name)
        const current = this._current;
        const invalid = this._i_invalid[current];
        const override = current !== undefined && name !== current && !invalid;
        const invalidate = override || this._active_o_count - this._loop_invalid_o_count > 0;
        if (invalidate) {
            this._invalidate();
        }
        this._current = name;
        this._forward_if_ready();
        if (override) {
            // this._loop_invalid_o_count = 0
            // this._loop_invalid_o = new Set()
            this._backward(current);
        }
    }
    onDataInputData(name) {
        this._on_input_data(name);
    }
    onRefInputData(name) {
        this._on_input_data(name);
    }
    onDataInputDrop(name) {
        if (name === this._current) {
            this._current = undefined;
            if (!this._backwarding && this._i_start_count === 0) {
                this._forward_all_valid_empty();
            }
        }
    }
    _on_output_drop(name) {
        if (this._loop_invalid_o.has(name)) {
            this._loop_invalid_o.delete(name);
            this._loop_invalid_o_count--;
        }
        this._backward_if_ready();
        this._forward_if_ready();
    }
    onOutputInvalid(name) {
        if (this._current !== undefined && !this._forwarding) {
            this._loop_invalid_o_count++;
            this._loop_invalid_o.add(name);
        }
    }
    onDataOutputData(name) {
        this._backward_if_ready();
    }
    onDataOutputDrop(name) {
        this._on_output_drop(name);
    }
    onRefOutputDrop(name) {
        this._on_output_drop(name);
    }
    onDataInputStart(name) {
        if (this._i_start_count === 1) {
            this._start();
        }
    }
    onDataInputInvalid(name) {
        if (name === this._current) {
            this._invalidate();
        }
    }
    onInpuRemoved(name) {
        if (name === this._current) {
            this._current = undefined;
        }
    }
    onDataInputEnd(name) {
        if (this._paused) {
            return;
        }
        if (this._i_start_count === 0) {
            if (this._current !== undefined) {
                this._forward_all_valid_empty();
            }
            this._end();
        }
    }
    _forward_all_valid_empty() {
        this._forwarding_empty = true;
        (0, f_1.default)(this._output, (o, name) => (!this._loop_invalid_o.has(name) || this._o_invalid[name]) && o.take());
        this._forwarding_empty = false;
        this._loop_invalid_o_count = 0;
        this._loop_invalid_o = new Set();
    }
    _forward_if_ready() {
        if (!this._backwarding &&
            !this._forwarding &&
            this._active_o_count - this._o_invalid_count === 0 &&
            this._o_count > 0 &&
            this._current !== undefined &&
            !this._i_invalid[this._current]) {
            this._loop_invalid_o_count = 0;
            this._loop_invalid_o = new Set();
            this._run();
        }
    }
    _backward_if_ready() {
        if (!this._forwarding &&
            this._current !== undefined &&
            this._active_o_count - this._loop_invalid_o_count === 0) {
            this._loop_invalid_o_count = 0;
            this._loop_invalid_o = new Set();
            this._backward(this._current);
            this._forward_if_ready();
        }
    }
    _run() {
        const data = this._i[this._current];
        const output_empty = (0, object_1.filterObj)(this._output, (o) => o.empty());
        const output_not_empty = (0, object_1.filterObj)(this._output, (o) => !o.empty());
        this._forwarding = true;
        (0, f_1.default)(output_empty, (o) => o.push(data));
        (0, f_1.default)(output_not_empty, (o) => o.push(data));
        this._forwarding = false;
        this._backward_if_ready();
    }
    snapshotSelf() {
        return Object.assign(Object.assign({}, super.snapshotSelf()), { _current: (0, primitive_1.isPrimitive)(this._current) ? this._current : undefined, _loop_invalided_o_count: this._loop_invalid_o_count });
    }
    restoreSelf(state) {
        const { _current, _loop_invalided_o_count } = state, rest = __rest(state, ["_current", "_loop_invalided_o_count"]);
        super.restoreSelf(rest);
        this._current = _current;
        this._loop_invalid_o_count = _loop_invalided_o_count;
    }
    getData() {
        if (this._current) {
            return this._i[this._current];
        }
        return undefined;
    }
}
exports.default = Merge;
//# sourceMappingURL=Merge.js.map