import { WebGLRenderer } from 'three';
import { assert } from "../../../core/assert.js";

export class WebGLRendererPool {

    used = new Set();

    get() {
        const canvas = document.createElement('canvas');
        const context = canvas.getContext('webgl2', { antialias: true });

        const renderer = new WebGLRenderer({
            alpha: true,
            context,
            canvas
        });

        assert.equal(renderer.domElement, canvas, 'renderer.domElement !== canvas');

        this.used.add(renderer);
        return renderer;
    }

    /**
     *
     * @param {THREE.WebGLRenderer} renderer
     * @returns {boolean}
     */
    release(renderer) {
        if (!this.used.has(renderer)) {
            //not from this pool
            return false;
        }
        this.used.delete(renderer);

        renderer.forceContextLoss();
        renderer.dispose();
        renderer.domElement = null;

        return true;
    }

    /**
     * After callback returns, renderer is released back into the pool
     * @template T
     * @param {function(renderer:WebGLRenderer):T} callback
     * @param {*} [thisArg]
     */
    use(callback, thisArg) {

        const renderer = this.get();

        try {
            const result = callback.call(thisArg, renderer);

            if (result instanceof Promise) {
                console.warn("callback returned a promise, if you need async behavior - use 'useAsync' method instead");
            }

            return result;

        } finally {

            this.release(renderer);
        }

    }

    /**
     * @template T
     * @param {function(renderer:WebGLRenderer):Promise<T>} callback
     * @param {*} [thisArg]
     */
    async useAsync(callback, thisArg) {

        const renderer = this.get();

        try {
            await callback.call(thisArg, renderer);
        } finally {
            this.release(renderer);
        }
    }

    static global = new WebGLRendererPool();
}