"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
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 = __importDefault(require("../system/core/object/ForEachKeyValue/f"));
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 = new Set();
        this._reset = () => {
            this._current = undefined;
        };
        this.addListener('reset', this._reset);
    }
    onInputRemoved(name, input, propagate) {
        super.onInputRemoved(name, input, propagate);
        if (name === this._current) {
            this._current = undefined;
            if (propagate) {
                this._forward_all_empty();
            }
        }
    }
    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, output, propagate) {
        if (propagate) {
            this._backward_if_ready();
        }
    }
    _on_input_data(name) {
        // console.log('Merge', '_on_input_data', name)
        const current = this._current;
        const invalid = this._i_invalid.has(current);
        const override = current !== undefined && name !== current && !invalid;
        const invalidate = override || this._o_active.size - this._loop_invalid_o.size > 0;
        if (invalidate) {
            this._invalidate();
        }
        this._current = name;
        if (!this._backwarding &&
            !this._forwarding &&
            this._o_active.size - this._o_invalid.size === 0 &&
            this._o_count > 0) {
            this._loop_invalid_o = new Set();
            this._run();
        }
        if (override) {
            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.size === 0) {
                this._forward_all_valid_empty();
            }
        }
    }
    _on_output_drop(name) {
        if (this._loop_invalid_o.has(name)) {
            this._loop_invalid_o.delete(name);
        }
        this._backward_if_ready();
        this._forward_if_ready();
    }
    onOutputInvalid(name) {
        if (this._current !== undefined && !this._forwarding) {
            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.size === 1) {
            this._start();
        }
    }
    onDataInputInvalid(name) {
        if (name === this._current) {
            this._invalidate();
        }
    }
    onDataInputEnd(name) {
        if (this._i_start.size === 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.has(name)) &&
            o.take());
        this._forwarding_empty = false;
        this._loop_invalid_o = new Set();
    }
    _forward_if_ready() {
        if (!this._backwarding &&
            !this._forwarding &&
            this._o_active.size - this._o_invalid.size === 0 &&
            this._current !== undefined) {
            this._loop_invalid_o = new Set();
            this._run();
        }
    }
    _backward_if_ready() {
        if (!this._forwarding &&
            this._current !== undefined &&
            this._o_count > 0 &&
            this._o_active.size - this._loop_invalid_o.size === 0) {
            this._loop_invalid_o = new Set();
            this._backward(this._current);
            if (!this._forwarding &&
                this._o_count > 0 &&
                this._current !== undefined) {
                this._loop_invalid_o = new Set();
                this._run();
            }
        }
    }
    _run() {
        const data = this._i[this._current];
        this._forwarding = true;
        for (const name in this._output) {
            const output = this._output[name];
            output.push(data);
        }
        this._forwarding = false;
        this._backward_if_ready();
    }
    snapshotSelf() {
        return {
            ...super.snapshotSelf(),
            ...(this._current !== undefined
                ? { _current: (0, primitive_1.isPrimitive)(this._current) ? this._current : undefined }
                : {}),
        };
    }
    restoreSelf(state) {
        const { _current, ...rest } = state;
        super.restoreSelf(rest);
        this._current = _current;
    }
    getData() {
        if (this._current) {
            return this._i[this._current];
        }
        return undefined;
    }
}
exports.default = Merge;
//# sourceMappingURL=Merge.js.map