import { Vector3 } from "../../Maths/math.vector.js";
import { Matrix } from "../../Maths/math.vector.js";
import { RenderTargetTexture } from "../../Materials/Textures/renderTargetTexture.js";

import { Texture } from "../../Materials/Textures/texture.js";
/**
 * Class used to generate realtime reflection / refraction cube textures.
 * This is a simplified version of ReflectionProbe that can be used directly as a RenderTargetTexture.
 * @see https://doc.babylonjs.com/features/featuresDeepDive/environment/reflectionProbes
 */
export class ReflectionProbeTexture extends RenderTargetTexture {
    /**
     * Creates a new reflection probe texture
     * @param name defines the name of the probe
     * @param options defines the options for creating the texture
     * @param scene defines the hosting scene
     */
    constructor(
    /** defines the name of the probe */
    name, options, scene) {
        const size = typeof options === "number" ? options : options.size;
        const generateMipMaps = typeof options === "number" ? true : (options.generateMipMaps ?? true);
        const useFloat = typeof options === "number" ? false : (options.useFloat ?? false);
        const linearSpace = typeof options === "number" ? false : (options.linearSpace ?? false);
        let textureType = 0;
        if (useFloat) {
            const caps = scene.getEngine().getCaps();
            if (caps.textureHalfFloatRender) {
                textureType = 2;
            }
            else if (caps.textureFloatRender) {
                textureType = 1;
            }
        }
        super(name, size, scene, generateMipMaps, true, textureType, true, undefined, undefined, undefined, undefined, undefined, linearSpace);
        this._viewMatrix = Matrix.Identity();
        this._target = Vector3.Zero();
        this._add = Vector3.Zero();
        this._invertYAxis = false;
        /** Gets or sets probe position (center of the cube map) */
        this.position = Vector3.Zero();
        /** @internal */
        this._parentContainer = null;
        this._options = options;
        if (scene.getEngine().supportsUniformBuffers) {
            this._sceneUBOs = [];
            for (let i = 0; i < 6; ++i) {
                this._sceneUBOs.push(scene.createSceneUniformBuffer(`Scene for Reflection Probe (name "${name}") face #${i}`, { forceMono: true }));
            }
        }
        this.gammaSpace = !linearSpace;
        this.invertZ = scene.useRightHandedSystem;
        const useReverseDepthBuffer = scene.getEngine().useReverseDepthBuffer;
        this.onBeforeRenderObservable.add((faceIndex) => {
            if (this._sceneUBOs) {
                scene.setSceneUniformBuffer(this._sceneUBOs[faceIndex]);
                scene.getSceneUniformBuffer().unbindEffect();
            }
            switch (faceIndex) {
                case 0:
                    this._add.copyFromFloats(1, 0, 0);
                    break;
                case 1:
                    this._add.copyFromFloats(-1, 0, 0);
                    break;
                case 2:
                    this._add.copyFromFloats(0, this._invertYAxis ? 1 : -1, 0);
                    break;
                case 3:
                    this._add.copyFromFloats(0, this._invertYAxis ? -1 : 1, 0);
                    break;
                case 4:
                    this._add.copyFromFloats(0, 0, scene.useRightHandedSystem ? -1 : 1);
                    break;
                case 5:
                    this._add.copyFromFloats(0, 0, scene.useRightHandedSystem ? 1 : -1);
                    break;
            }
            if (this._attachedMesh) {
                this.position.copyFrom(this._attachedMesh.getAbsolutePosition());
            }
            this.position.addToRef(this._add, this._target);
            const lookAtFunction = scene.useRightHandedSystem ? Matrix.LookAtRHToRef : Matrix.LookAtLHToRef;
            const perspectiveFunction = scene.useRightHandedSystem ? Matrix.PerspectiveFovRH : Matrix.PerspectiveFovLH;
            lookAtFunction(this.position, this._target, Vector3.Up(), this._viewMatrix);
            if (scene.activeCamera) {
                this._projectionMatrix = perspectiveFunction(Math.PI / 2, 1, useReverseDepthBuffer ? scene.activeCamera.maxZ : scene.activeCamera.minZ, useReverseDepthBuffer ? scene.activeCamera.minZ : scene.activeCamera.maxZ, this._getEngine().isNDCHalfZRange);
                scene.setTransformMatrix(this._viewMatrix, this._projectionMatrix);
                if (scene.activeCamera.isRigCamera && !this.activeCamera) {
                    this.activeCamera = scene.activeCamera.rigParent || null;
                }
            }
            if (this._sceneUBOs) {
                scene.finalizeSceneUbo();
            }
            scene._forcedViewPosition = this.position;
        });
        let currentApplyByPostProcess;
        this.onBeforeBindObservable.add(() => {
            const engine = scene.getEngine();
            this._currentSceneUBO = scene.getSceneUniformBuffer();
            if (engine._enableGPUDebugMarkers) {
                engine._debugPushGroup?.(`reflection probe generation for ${name}`);
            }
            currentApplyByPostProcess = scene.imageProcessingConfiguration.applyByPostProcess;
            if (linearSpace) {
                scene.imageProcessingConfiguration.applyByPostProcess = true;
            }
        });
        this.onAfterUnbindObservable.add(() => {
            const engine = scene.getEngine();
            scene.imageProcessingConfiguration.applyByPostProcess = currentApplyByPostProcess;
            scene._forcedViewPosition = null;
            if (this._sceneUBOs) {
                scene.setSceneUniformBuffer(this._currentSceneUBO);
            }
            scene.updateTransformMatrix(true);
            if (engine._enableGPUDebugMarkers) {
                engine._debugPopGroup?.();
            }
        });
    }
    /**
     * Attach the probe to a specific mesh (Rendering will be done from attached mesh's position)
     * @param mesh defines the mesh to attach to
     */
    attachToMesh(mesh) {
        this._attachedMesh = mesh;
    }
    /**
     * Gets the mesh attached to this probe
     * @returns the attached mesh or null
     */
    get attachedMesh() {
        return this._attachedMesh;
    }
    /**
     * Gets the class name of the reflection probe texture
     * @returns "ReflectionProbeTexture"
     */
    getClassName() {
        return "ReflectionProbeTexture";
    }
    /**
     * Converts the reflection probe texture information to a readable string for debug purpose
     * @param fullDetails Supports for multiple levels of logging within scene loading
     * @returns the human readable reflection probe texture info
     */
    toString(fullDetails) {
        let ret = "Name: " + this.name;
        if (fullDetails) {
            ret += ", position: " + this.position.toString();
            if (this._attachedMesh) {
                ret += ", attached mesh: " + this._attachedMesh.name;
            }
        }
        return ret;
    }
    /**
     * Used for serializing the probe texture
     * @returns any
     */
    serialize() {
        const serializationObject = super.serialize();
        if (serializationObject) {
            serializationObject.isReflectionProbe = true;
            if (this._attachedMesh) {
                serializationObject.attachedMesh = this._attachedMesh.id;
            }
            serializationObject.position = this.position.asArray();
            serializationObject.options = structuredClone(this._options);
            return serializationObject;
        }
        return null;
    }
    /**
     * Clean all associated resources
     */
    dispose() {
        if (this._sceneUBOs) {
            for (const ubo of this._sceneUBOs) {
                ubo.dispose();
            }
            this._sceneUBOs = [];
        }
        super.dispose();
    }
}
Texture._CreateReflectionProbeTexture = function (name, options, scene, position, attachedMesh) {
    const texture = new ReflectionProbeTexture(name, options, scene);
    texture.position.fromArray(position);
    if (attachedMesh) {
        texture.attachToMesh(scene.getMeshById(attachedMesh) ?? null);
    }
    return texture;
};
//# sourceMappingURL=reflectionProbeTexture.js.map