"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 });
const Primitive_1 = require("../../../../Primitive");
const _ids_1 = require("../../../_ids");
class Loop extends Primitive_1.Primitive {
    constructor(system) {
        super({
            i: ['init', 'next', 'test'],
            o: ['local', 'current', 'final'],
        }, {}, system, _ids_1.ID_LOOP);
        this._current = undefined;
        this._next = undefined;
        this._test = undefined;
        this._looping = false;
        this._nexting = false;
        this.addListener('reset', this._reset);
    }
    _reset() {
        this._current = undefined;
        this._next = undefined;
        this._nexting = false;
        this._looping = false;
    }
    onDataInputData(name, data) {
        if (name === 'init') {
            this._current = data;
            this._output.local.push(data);
            this._forward_if_ready();
        }
        else if (name === 'next') {
            if (this._next === undefined &&
                this._current !== undefined &&
                this._input.test.peak()) {
                this._next = data;
                this._pull_next();
            }
        }
        else if (name === 'test') {
            this._test = data;
            if (this._current !== undefined) {
                this._forward_if_ready();
            }
        }
    }
    onDataInputDrop(name) {
        if (!this._backwarding) {
            if (name === 'next') {
                return;
            }
            if (name === 'test') {
                this._test = undefined;
                this._next = undefined;
                this._forward_empty('current');
                this._forward_empty('final');
                return;
            }
            if (name === 'init') {
                this._reset();
                this._forward_all_empty();
                return;
            }
        }
    }
    onDataOutputDrop(name) {
        if (!this._forwarding_empty) {
            if (name === 'current') {
                if (this._next !== undefined) {
                    this._loop();
                }
            }
            else if (name === 'local') {
                if (this._current === undefined) {
                    this._done();
                }
                else if (this._looping) {
                    this._looping = false;
                    this._output.local.push(this._current);
                    if (this._test !== undefined) {
                        this._forward_if_ready();
                    }
                }
            }
            else {
                this._current = undefined;
                this._backward('test');
                if (this._output.local.empty()) {
                    this._done();
                }
            }
        }
    }
    onDataInputInvalid(name) {
        if (name === 'init') {
            this._reset();
            this._output.local.invalidate();
        }
        else if (name === 'test') {
            this._nexting = false;
            this._next = undefined;
        }
        this._output.current.invalidate();
        this._output.final.invalidate();
    }
    _pull_next() {
        this._nexting = false;
        this._input.next.pull();
        if (this._output.current.empty()) {
            this._loop();
        }
    }
    _loop() {
        this._looping = true;
        this._current = this._next;
        this._next = undefined;
        this._backward('test');
        if (this._looping && this._output.local.empty()) {
            this._looping = false;
            this._output.local.push(this._current);
            this._forward_if_ready();
        }
    }
    _iterate() {
        this._forwarding = true;
        if (this._input.test.peak()) {
            this._nexting = true;
            this._output.current.push(this._current);
            const next = this._input.next.peak();
            if (this._next === undefined && next !== undefined) {
                this._next = next;
                this._pull_next();
            }
        }
        else {
            this._output.final.push(this._current);
            this._output.current.pull();
        }
        this._forwarding = false;
    }
    _done() {
        this._reset();
        this._backward('init');
    }
    _forward_if_ready() {
        while (!this._forwarding &&
            !this._looping &&
            !this._nexting &&
            this._current !== undefined &&
            !this._input.test.empty() &&
            !this._input.test.invalid() &&
            (this._output.current.empty() || this._output.current.invalid()) &&
            (this._output.final.empty() || this._output.final.invalid())) {
            this._iterate();
        }
    }
    snapshotSelf() {
        return Object.assign(Object.assign({}, super.snapshotSelf()), { _current: this._current, _next: this._next, _test: this._test, _nexting: this._nexting, _looping: this._looping });
    }
    restoreSelf(state) {
        const { _current, _next, _test, _nexting, _looping } = state, rest = __rest(state, ["_current", "_next", "_test", "_nexting", "_looping"]);
        super.restoreSelf(rest);
        this._current = _current;
        this._next = _next;
        this._test = _test;
        this._nexting = _nexting;
        this._looping = _looping;
    }
}
exports.default = Loop;
//# sourceMappingURL=index.js.map