export class A3DAppLifecycle {
    disposables = new Set();
    animationFrames = new Set();
    eventListeners = [];
    disposed = false;
    disposeCalls = 0;
    addDisposable(disposable) {
        this.assertAlive("addDisposable");
        this.disposables.add(disposable);
        return disposable;
    }
    addEventListener(target, type, listener, options) {
        this.assertAlive("addEventListener");
        target.addEventListener(type, listener, options);
        this.eventListeners.push({ target, type, listener, options });
    }
    requestAnimationFrame(callback) {
        this.assertAlive("requestAnimationFrame");
        const handle = window.requestAnimationFrame((time) => {
            this.animationFrames.delete(handle);
            if (!this.disposed)
                callback(time);
        });
        this.animationFrames.add(handle);
        return handle;
    }
    cancelAnimationFrame(handle) {
        if (this.animationFrames.delete(handle)) {
            window.cancelAnimationFrame(handle);
        }
    }
    bindPageTeardown(target = window) {
        this.addEventListener(target, "pagehide", () => this.dispose(), { once: true });
        this.addEventListener(target, "beforeunload", () => this.dispose(), { once: true });
    }
    dispose() {
        this.disposeCalls += 1;
        if (this.disposed)
            return;
        this.disposed = true;
        for (const handle of this.animationFrames) {
            window.cancelAnimationFrame(handle);
        }
        this.animationFrames.clear();
        for (const entry of this.eventListeners.splice(0)) {
            entry.target.removeEventListener(entry.type, entry.listener, entry.options);
        }
        const errors = [];
        for (const disposable of [...this.disposables].reverse()) {
            try {
                disposable.dispose();
            }
            catch (error) {
                errors.push(error);
            }
        }
        this.disposables.clear();
        if (errors.length > 0) {
            throw new AggregateError(errors, "A3D app lifecycle disposal failed.");
        }
    }
    snapshot() {
        return {
            disposed: this.disposed,
            animationFrames: this.animationFrames.size,
            eventListeners: this.eventListeners.length,
            disposables: this.disposables.size,
            disposeCalls: this.disposeCalls
        };
    }
    assertAlive(operation) {
        if (this.disposed) {
            throw new Error(`Cannot ${operation} on a disposed A3D app lifecycle.`);
        }
    }
}
//# sourceMappingURL=A3DAppLifecycle.js.map