import type { EffectComposer } from "postprocessing";
import { Camera, Mesh, Object3D, Texture, WebGLRenderer, WebGLRenderTarget } from "three";
import type { EffectComposer as ThreeEffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";

import { findResourceUsers } from "./engine_assetdatabase.js";


const _prevVisible = Symbol("previous-visibility");

/** 
 * A RenderTexture can be used to render a scene to a texture automatically by assigning it to the `Camera` component's `targetTexture` property.
 * You can then assign the `RenderTexture.texture` to materials to be displayed
 * @example Create a new RenderTexture and assign it to a camera and material
 * ```typescript
 * // create new RenderTexture with a resolution
 * const rt = new RenderTexture(256, 256);
 * // assign to a camera
 * myCameraComponent.targetTexture = rt;
 * // assign to a material
 * myMaterial.map = rt.texture;
 * ```
 */
export class RenderTexture extends WebGLRenderTarget {

    /**
     * Render the scene to the texture
     * @param scene The scene to render
     * @param camera The camera to render from
     * @param renderer The renderer or effectcomposer to use
     */
    render(scene: Object3D, camera: Camera, renderer: WebGLRenderer | EffectComposer | ThreeEffectComposer) {

        const composer = renderer as (EffectComposer | ThreeEffectComposer);

        if ("addPass" in composer) {
            if (!this["_unsupported_effectcomposer_warning"]) {
                console.warn("RenderTexture.render() does not yet support EffectComposer");
                this["_unsupported_effectcomposer_warning"] = true;
            }
        }
        else if (renderer instanceof WebGLRenderer) {
            this.onBeforeRender();
            const prev = renderer.getRenderTarget();
            const xr = renderer.xr.enabled;
            renderer.xr.enabled = false;
            renderer.setRenderTarget(this);
            renderer.clear(true, true, true);
            renderer.render(scene, camera);
            renderer.setRenderTarget(prev);
            renderer.xr.enabled = xr;
            this.onAfterRender();
        }
    }


    private static _userSet: Set<object> = new Set();

    private onBeforeRender() {
        RenderTexture._userSet.clear();
        const users = findResourceUsers(this.texture, true, null, RenderTexture._userSet);
        for (const user of users) {
            if (user instanceof Mesh) {
                user[_prevVisible] = user.visible;
                user.visible = false;
            }
        }
    }

    private onAfterRender() {
        for (const user of RenderTexture._userSet) {
            if (user instanceof Mesh) {
                user.visible = user[_prevVisible];
            }
        }
        RenderTexture._userSet.clear();
    }
}





