"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Subscriber = exports.Dependency = exports.Link = exports.System = void 0;
exports.startBatch = startBatch;
exports.endBatch = endBatch;
var System;
(function (System) {
    System.activeSub = undefined;
    System.activeEffectScope = undefined;
    System.activeTrackId = 0;
    System.activeEffectScopeTrackId = 0;
    System.batchDepth = 0;
    System.lastTrackId = 0;
    System.queuedEffects = undefined;
    System.queuedEffectsTail = undefined;
})(System || (exports.System = System = {}));
function startBatch() {
    System.batchDepth++;
}
function endBatch() {
    System.batchDepth--;
    if (System.batchDepth === 0) {
        while (System.queuedEffects !== undefined) {
            const effect = System.queuedEffects;
            const queuedNext = System.queuedEffects.nextNotify;
            if (queuedNext !== undefined) {
                System.queuedEffects.nextNotify = undefined;
                System.queuedEffects = queuedNext;
            }
            else {
                System.queuedEffects = undefined;
                System.queuedEffectsTail = undefined;
            }
            effect.notify();
        }
    }
}
var Link;
(function (Link) {
    Link.pool = undefined;
})(Link || (exports.Link = Link = {}));
var Dependency;
(function (Dependency) {
    const system = System;
    function linkSubscriber(dep, sub) {
        const depsTail = sub.depsTail;
        const old = depsTail !== undefined
            ? depsTail.nextDep
            : sub.deps;
        if (old === undefined || old.dep !== dep) {
            let newLink;
            if (Link.pool !== undefined) {
                newLink = Link.pool;
                Link.pool = newLink.nextDep;
                newLink.nextDep = old;
                newLink.dep = dep;
                newLink.sub = sub;
                newLink.trackId = sub.trackId;
            }
            else {
                newLink = {
                    dep,
                    sub,
                    trackId: sub.trackId,
                    nextDep: old,
                    prevSub: undefined,
                    nextSub: undefined,
                };
            }
            if (depsTail === undefined) {
                sub.deps = newLink;
            }
            else {
                depsTail.nextDep = newLink;
            }
            if (dep.subs === undefined) {
                dep.subs = newLink;
            }
            else {
                const oldTail = dep.subsTail;
                newLink.prevSub = oldTail;
                oldTail.nextSub = newLink;
            }
            sub.depsTail = newLink;
            dep.subsTail = newLink;
        }
        else {
            old.trackId = sub.trackId;
            sub.depsTail = old;
        }
    }
    Dependency.linkSubscriber = linkSubscriber;
    function propagate(subs) {
        let link = subs;
        let dep = subs.dep;
        let dirtyLevel = 3 /* DirtyLevels.Dirty */;
        let remainingQuantity = 0;
        do {
            if (link !== undefined) {
                const sub = link.sub;
                if (sub.trackId > 0) {
                    if (sub.trackId === link.trackId) {
                        const subDirtyLevel = sub.dirtyLevel;
                        if (subDirtyLevel < dirtyLevel) {
                            sub.dirtyLevel = dirtyLevel;
                            if (subDirtyLevel === 0 /* DirtyLevels.None */) {
                                sub.canPropagate = true;
                            }
                        }
                    }
                }
                else if (sub.trackId === -link.trackId) {
                    const subDirtyLevel = sub.dirtyLevel;
                    const notDirty = subDirtyLevel === 0 /* DirtyLevels.None */;
                    if (subDirtyLevel < dirtyLevel) {
                        sub.dirtyLevel = dirtyLevel;
                    }
                    if (notDirty || sub.canPropagate) {
                        if (!notDirty) {
                            sub.canPropagate = false;
                        }
                        const subIsEffect = 'notify' in sub;
                        if ('subs' in sub && sub.subs !== undefined) {
                            sub.depsTail.nextDep = link;
                            dep = sub;
                            link = sub.subs;
                            if (subIsEffect) {
                                dirtyLevel = 1 /* DirtyLevels.SideEffectsOnly */;
                            }
                            else {
                                dirtyLevel = 2 /* DirtyLevels.MaybeDirty */;
                            }
                            remainingQuantity++;
                            continue;
                        }
                        else if (subIsEffect) {
                            const queuedEffectsTail = system.queuedEffectsTail;
                            if (queuedEffectsTail !== undefined) {
                                queuedEffectsTail.nextNotify = sub;
                            }
                            else {
                                system.queuedEffects = sub;
                            }
                            system.queuedEffectsTail = sub;
                        }
                    }
                }
                link = link.nextSub;
                continue;
            }
            if (remainingQuantity !== 0) {
                const depsTail = dep.depsTail;
                const prevLink = depsTail.nextDep;
                const prevSub = prevLink.sub;
                depsTail.nextDep = undefined;
                dep = prevLink.dep;
                link = prevLink.nextSub;
                remainingQuantity--;
                if (remainingQuantity === 0) {
                    dirtyLevel = 3 /* DirtyLevels.Dirty */;
                }
                else if ('notify' in dep) {
                    dirtyLevel = 1 /* DirtyLevels.SideEffectsOnly */;
                }
                else {
                    dirtyLevel = 2 /* DirtyLevels.MaybeDirty */;
                }
                if ('notify' in prevSub) {
                    const queuedEffectsTail = system.queuedEffectsTail;
                    if (queuedEffectsTail !== undefined) {
                        queuedEffectsTail.nextNotify = prevSub;
                    }
                    else {
                        system.queuedEffects = prevSub;
                    }
                    system.queuedEffectsTail = prevSub;
                }
                continue;
            }
            break;
        } while (true);
    }
    Dependency.propagate = propagate;
})(Dependency || (exports.Dependency = Dependency = {}));
var Subscriber;
(function (Subscriber) {
    const system = System;
    function runInnerEffects(link) {
        while (link !== undefined) {
            const dep = link.dep;
            if ('notify' in dep) {
                dep.notify();
            }
            link = link.nextDep;
        }
    }
    Subscriber.runInnerEffects = runInnerEffects;
    function resolveMaybeDirty(sub, depth = 0) {
        let link = sub.deps;
        while (link !== undefined) {
            const dep = link.dep;
            if ('update' in dep) {
                const dirtyLevel = dep.dirtyLevel;
                if (dirtyLevel === 2 /* DirtyLevels.MaybeDirty */) {
                    if (depth >= 4) {
                        resolveMaybeDirtyNonRecursive(dep);
                    }
                    else {
                        resolveMaybeDirty(dep, depth + 1);
                    }
                    if (dep.dirtyLevel === 3 /* DirtyLevels.Dirty */) {
                        dep.update();
                        if (sub.dirtyLevel === 3 /* DirtyLevels.Dirty */) {
                            break;
                        }
                    }
                }
                else if (dirtyLevel === 3 /* DirtyLevels.Dirty */) {
                    dep.update();
                    if (sub.dirtyLevel === 3 /* DirtyLevels.Dirty */) {
                        break;
                    }
                }
            }
            link = link.nextDep;
        }
        if (sub.dirtyLevel === 2 /* DirtyLevels.MaybeDirty */) {
            sub.dirtyLevel = 0 /* DirtyLevels.None */;
        }
    }
    Subscriber.resolveMaybeDirty = resolveMaybeDirty;
    function resolveMaybeDirtyNonRecursive(sub) {
        let link = sub.deps;
        let remaining = 0;
        do {
            if (link !== undefined) {
                const dep = link.dep;
                if ('update' in dep) {
                    const depDirtyLevel = dep.dirtyLevel;
                    if (depDirtyLevel === 2 /* DirtyLevels.MaybeDirty */) {
                        dep.subs.prevSub = link;
                        sub = dep;
                        link = dep.deps;
                        remaining++;
                        continue;
                    }
                    else if (depDirtyLevel === 3 /* DirtyLevels.Dirty */) {
                        dep.update();
                        if (sub.dirtyLevel === 3 /* DirtyLevels.Dirty */) {
                            if (remaining !== 0) {
                                const subSubs = sub.subs;
                                const prevLink = subSubs.prevSub;
                                sub.update();
                                subSubs.prevSub = undefined;
                                sub = prevLink.sub;
                                link = prevLink.nextDep;
                                remaining--;
                                continue;
                            }
                            break;
                        }
                    }
                }
                link = link.nextDep;
                continue;
            }
            const dirtyLevel = sub.dirtyLevel;
            if (dirtyLevel === 2 /* DirtyLevels.MaybeDirty */) {
                sub.dirtyLevel = 0 /* DirtyLevels.None */;
                if (remaining !== 0) {
                    const subSubs = sub.subs;
                    const prevLink = subSubs.prevSub;
                    subSubs.prevSub = undefined;
                    sub = prevLink.sub;
                    link = prevLink.nextDep;
                    remaining--;
                    continue;
                }
            }
            else if (remaining !== 0) {
                if (dirtyLevel === 3 /* DirtyLevels.Dirty */) {
                    sub.update();
                }
                const subSubs = sub.subs;
                const prevLink = subSubs.prevSub;
                subSubs.prevSub = undefined;
                sub = prevLink.sub;
                link = prevLink.nextDep;
                remaining--;
                continue;
            }
            break;
        } while (true);
    }
    Subscriber.resolveMaybeDirtyNonRecursive = resolveMaybeDirtyNonRecursive;
    function startTrackDependencies(sub) {
        const newVersion = system.lastTrackId + 1;
        const prevSub = system.activeSub;
        system.activeSub = sub;
        system.activeTrackId = newVersion;
        system.lastTrackId = newVersion;
        sub.depsTail = undefined;
        sub.trackId = newVersion;
        sub.dirtyLevel = 0 /* DirtyLevels.None */;
        return prevSub;
    }
    Subscriber.startTrackDependencies = startTrackDependencies;
    function endTrackDependencies(sub, prevSub) {
        if (prevSub !== undefined) {
            system.activeSub = prevSub;
            system.activeTrackId = prevSub.trackId;
        }
        else {
            system.activeSub = undefined;
            system.activeTrackId = 0;
        }
        const depsTail = sub.depsTail;
        if (depsTail !== undefined) {
            if (depsTail.nextDep !== undefined) {
                clearTrack(depsTail.nextDep);
                depsTail.nextDep = undefined;
            }
        }
        else if (sub.deps !== undefined) {
            clearTrack(sub.deps);
            sub.deps = undefined;
        }
        sub.trackId = -sub.trackId;
    }
    Subscriber.endTrackDependencies = endTrackDependencies;
    function clearTrack(link) {
        do {
            const nextDep = link.nextDep;
            const dep = link.dep;
            const nextSub = link.nextSub;
            const prevSub = link.prevSub;
            if (nextSub !== undefined) {
                nextSub.prevSub = prevSub;
            }
            if (prevSub !== undefined) {
                prevSub.nextSub = nextSub;
            }
            if (nextSub === undefined) {
                dep.subsTail = prevSub;
            }
            if (prevSub === undefined) {
                dep.subs = nextSub;
            }
            // @ts-ignore
            link.dep = undefined;
            // @ts-ignore
            link.sub = undefined;
            link.prevSub = undefined;
            link.nextSub = undefined;
            link.nextDep = Link.pool;
            Link.pool = link;
            if (dep.subs === undefined && 'deps' in dep) {
                dep.dirtyLevel = 4 /* DirtyLevels.Released */;
                if (dep.deps !== undefined) {
                    link = dep.deps;
                    dep.depsTail.nextDep = nextDep;
                    dep.deps = undefined;
                    dep.depsTail = undefined;
                    continue;
                }
            }
            link = nextDep;
        } while (link !== undefined);
    }
    Subscriber.clearTrack = clearTrack;
    function startTrackEffects(sub) {
        const newVersion = system.lastTrackId + 1;
        const prevSub = system.activeEffectScope;
        system.activeEffectScope = sub;
        system.activeEffectScopeTrackId = newVersion;
        system.lastTrackId = newVersion;
        sub.depsTail = undefined;
        sub.trackId = newVersion;
        sub.dirtyLevel = 0 /* DirtyLevels.None */;
        return prevSub;
    }
    Subscriber.startTrackEffects = startTrackEffects;
    function endTrackEffects(sub, prevSub) {
        if (prevSub !== undefined) {
            system.activeEffectScope = prevSub;
            system.activeEffectScopeTrackId = prevSub.trackId;
        }
        else {
            system.activeEffectScope = undefined;
            system.activeEffectScopeTrackId = 0;
        }
        const depsTail = sub.depsTail;
        if (depsTail !== undefined) {
            if (depsTail.nextDep !== undefined) {
                clearTrack(depsTail.nextDep);
                depsTail.nextDep = undefined;
            }
        }
        else if (sub.deps !== undefined) {
            clearTrack(sub.deps);
            sub.deps = undefined;
        }
        sub.trackId = -sub.trackId;
    }
    Subscriber.endTrackEffects = endTrackEffects;
})(Subscriber || (exports.Subscriber = Subscriber = {}));
