import { __decorate } from "../tslib.es6.js";
import { serialize, serializeAsColor3 } from "../Misc/decorators.js";
import { Scene } from "../scene.js";
import { EffectLayer } from "./effectLayer.js";

import { RegisterClass } from "../Misc/typeStore.js";
import { SerializationHelper } from "../Misc/decorators.serialization.js";
import { ThinSelectionOutlineLayer } from "./thinSelectionOutlineLayer.js";
Scene.prototype.getSelectionOutlineLayerByName = function (name) {
    for (let index = 0; index < this.effectLayers?.length; index++) {
        if (this.effectLayers[index].name === name && this.effectLayers[index].getEffectName() === SelectionOutlineLayer.EffectName) {
            return this.effectLayers[index];
        }
    }
    return null;
};
/**
 * The selection outline layer Helps adding a outline effect around a mesh.
 *
 * Once instantiated in a scene, simply use the addMesh or removeMesh method to add or remove
 * outlined meshes to your scene.
 */
export class SelectionOutlineLayer extends EffectLayer {
    /**
     * Effect Name of the selection outline layer.
     */
    static get EffectName() {
        return ThinSelectionOutlineLayer.EffectName;
    }
    /**
     * The outline color (default (1, 0.5, 0))
     */
    get outlineColor() {
        return this._thinEffectLayer.outlineColor;
    }
    set outlineColor(value) {
        this._thinEffectLayer.outlineColor = value;
    }
    /**
     * The thickness of the edges (default: 2.0)
     */
    get outlineThickness() {
        return this._thinEffectLayer.outlineThickness;
    }
    set outlineThickness(value) {
        this._thinEffectLayer.outlineThickness = value;
    }
    /**
     * The strength of the occlusion effect (default: 0.8)
     */
    get occlusionStrength() {
        return this._thinEffectLayer.occlusionStrength;
    }
    set occlusionStrength(value) {
        this._thinEffectLayer.occlusionStrength = value;
    }
    /**
     * The occlusion threshold (default: 0.01)
     */
    get occlusionThreshold() {
        return this._thinEffectLayer.occlusionThreshold;
    }
    set occlusionThreshold(value) {
        this._thinEffectLayer.occlusionThreshold = value;
    }
    /**
     * Instantiates a new selection outline Layer and references it to the scene..
     * @param name The name of the layer
     * @param scene The scene to use the layer in
     * @param options Sets of none mandatory options to use with the layer (see ISelectionOutlineLayerOptions for more information)
     */
    constructor(name, scene, options) {
        super(name, scene, options !== undefined ? !!options.forceGLSL : false, new ThinSelectionOutlineLayer(name, scene, options));
        // Adapt options
        this._options = {
            mainTextureRatio: 1.0,
            mainTextureFixedSize: 0,
            alphaBlendingMode: 2,
            camera: null,
            mainTextureSamples: 1,
            renderingGroupId: -1,
            mainTextureType: 1,
            mainTextureFormat: 7,
            forceGLSL: false,
            storeCameraSpaceZ: false,
            outlineMethod: 0,
            ...options,
        };
        // Initialize the layer
        this._init(this._options);
        // Do not render as long as no meshes have been added
        this._shouldRender = false;
        this._scene.enableDepthRenderer();
    }
    /**
     * Get the effect name of the layer.
     * @returns The effect name
     */
    getEffectName() {
        return SelectionOutlineLayer.EffectName;
    }
    _numInternalDraws() {
        return 1; // draw depth mask on main pass and outline on merge pass
    }
    /**
     * Create the merge effect. This is the shader use to blit the information back
     * to the main canvas at the end of the scene rendering.
     * @returns The effect created
     */
    _createMergeEffect() {
        return this._thinEffectLayer._createMergeEffect();
    }
    /**
     * Creates the render target textures and post processes used in the selection outline layer.
     */
    _createTextureAndPostProcesses() {
        this._textures = [];
        this._thinEffectLayer.bindTexturesForCompose = (effect) => {
            effect.setTexture("maskSampler", this._mainTexture);
            const depthRenderer = this._scene.enableDepthRenderer();
            effect.setTexture("depthSampler", depthRenderer.getDepthMap());
            const mainTextureDesiredSize = this._mainTextureDesiredSize;
            this._thinEffectLayer.textureWidth = mainTextureDesiredSize.width;
            this._thinEffectLayer.textureHeight = mainTextureDesiredSize.height;
        };
        this._thinEffectLayer._createTextureAndPostProcesses();
        this._postProcesses = [];
        this._mainTexture.samples = this._options.mainTextureSamples;
        this._mainTexture.onAfterUnbindObservable.add(() => {
            // glow layer and highlight layer both call this._scene.postProcessManager.directRender
            // when you call this._scene.postProcessManager.directRender, it has 4 side effects:
            // 1. binds the framebuffer
            // 2. setAlphaMode(ALPHA_DISABLE)
            // 3. setDepthBuffer(true)
            // 4. setDepthWrite(true)
            // glow layer and highlight layer are restore framebuffer and depends on other side effects
            // but for now 3 and 4 are not needed to resolve the state management issue, so we just restore alpha mode
            this._scene.getEngine().setAlphaMode(0);
        });
    }
    /**
     * Creates the main texture for the effect layer.
     */
    _createMainTexture() {
        super._createMainTexture();
        // set the render list for selective rendering
        this._mainTexture.renderList = this._thinEffectLayer._selection;
    }
    /**
     * @returns whether or not the layer needs stencil enabled during the mesh rendering.
     */
    needStencil() {
        return this._thinEffectLayer.needStencil();
    }
    /**
     * Checks for the readiness of the element composing the layer.
     * @param subMesh the mesh to check for
     * @param useInstances specify whether or not to use instances to render the mesh
     * @returns true if ready otherwise, false
     */
    isReady(subMesh, useInstances) {
        return this._thinEffectLayer.isReady(subMesh, useInstances);
    }
    /**
     * Implementation specific of rendering the generating effect on the main canvas.
     * @param effect The effect used to render through
     * @param renderIndex
     */
    _internalRender(effect, renderIndex) {
        this._thinEffectLayer._internalCompose(effect, renderIndex);
    }
    /**
     * @returns true if the layer contains information to display, otherwise false.
     */
    shouldRender() {
        return this._thinEffectLayer.shouldRender();
    }
    /**
     * Returns true if the mesh should render, otherwise false.
     * @param mesh The mesh to render
     * @returns true if it should render otherwise false
     */
    _shouldRenderMesh(mesh) {
        return this._thinEffectLayer._shouldRenderMesh(mesh);
    }
    /**
     * Returns true if the mesh can be rendered, otherwise false.
     * @param mesh The mesh to render
     * @param material The material used on the mesh
     * @returns true if it can be rendered otherwise false
     */
    _canRenderMesh(mesh, material) {
        return this._thinEffectLayer._canRenderMesh(mesh, material);
    }
    /**
     * Adds specific effects defines.
     * @param defines The defines to add specifics to.
     */
    _addCustomEffectDefines(defines) {
        this._thinEffectLayer._addCustomEffectDefines(defines);
    }
    /**
     * Sets the required values for both the emissive texture and and the main color.
     * @param mesh
     * @param subMesh
     * @param material
     */
    _setEmissiveTextureAndColor(mesh, subMesh, material) {
        this._thinEffectLayer._setEmissiveTextureAndColor(mesh, subMesh, material);
    }
    /**
     * Determine if a given mesh will be highlighted by the current SelectionOutlineLayer
     * @param mesh mesh to test
     * @returns true if the mesh will be highlighted by the current SelectionOutlineLayer
     */
    hasMesh(mesh) {
        return this._thinEffectLayer.hasMesh(mesh);
    }
    /**
     * Remove all the meshes currently referenced in the selection outline layer
     */
    clearSelection() {
        this._thinEffectLayer.clearSelection();
        this._mainTexture.renderList = this._thinEffectLayer._selection; // update render list
    }
    /**
     * Adds mesh or group of mesh to the current selection
     *
     * If a group of meshes is provided, they will outline as a single unit
     * @param meshOrGroup Meshes to add to the selection
     */
    addSelection(meshOrGroup) {
        this._thinEffectLayer.addSelection(meshOrGroup);
    }
    /**
     * Adds mesh or group of mesh to the current selection
     * @param meshOrGroup Meshes to remove from the selection
     */
    removeSelection(meshOrGroup) {
        this._thinEffectLayer.removeSelection(meshOrGroup);
    }
    /**
     * Free any resources and references associated to a mesh.
     * Internal use
     * @param mesh The mesh to free.
     * @internal
     */
    _disposeMesh(mesh) {
        this._thinEffectLayer._disposeMesh(mesh);
    }
    /**
     * Gets the class name of the effect layer
     * @returns the string with the class name of the effect layer
     */
    getClassName() {
        return "SelectionOutlineLayer";
    }
    /**
     * Serializes this SelectionOutline layer
     * @returns a serialized SelectionOutline layer object
     */
    serialize() {
        const serializationObject = SerializationHelper.Serialize(this);
        serializationObject.customType = "BABYLON.SelectionOutlineLayer";
        // Selected meshes
        serializationObject.selection = [];
        const selection = this._thinEffectLayer._selection;
        if (selection) {
            const meshUniqueIdToSelectionId = this._thinEffectLayer._meshUniqueIdToSelectionId;
            // selection can be sparse since _removeMesh can remove entries
            const selectionMap = {};
            for (let i = 0; i < selection.length; ++i) {
                const mesh = selection[i];
                const selectionId = meshUniqueIdToSelectionId[mesh.uniqueId];
                if (!selectionMap[selectionId]) {
                    selectionMap[selectionId] = {
                        meshIds: [],
                    };
                }
                selectionMap[selectionId].meshIds.push(mesh.id);
            }
            serializationObject.selection = selectionMap;
        }
        return serializationObject;
    }
    /**
     * Creates a SelectionOutline layer from parsed SelectionOutline layer data
     * @param parsedSelectionOutlineLayer defines the SelectionOutline layer data
     * @param scene defines the current scene
     * @param rootUrl defines the root URL containing the SelectionOutline layer information
     * @returns a parsed SelectionOutline layer
     */
    static Parse(parsedSelectionOutlineLayer, scene, rootUrl) {
        const selectionOutlineLayer = SerializationHelper.Parse(() => new SelectionOutlineLayer(parsedSelectionOutlineLayer.name, scene, parsedSelectionOutlineLayer.options), parsedSelectionOutlineLayer, scene, rootUrl);
        const selectionMap = parsedSelectionOutlineLayer.selection;
        // Selected meshes
        for (const outlinedMeshes of Object.values(selectionMap)) {
            const meshes = [];
            for (let meshIndex = 0; meshIndex < outlinedMeshes.meshIds.length; meshIndex++) {
                const meshId = outlinedMeshes.meshIds[meshIndex];
                const mesh = scene.getMeshById(meshId);
                if (mesh) {
                    meshes.push(mesh);
                }
            }
            selectionOutlineLayer.addSelection(meshes);
        }
        return selectionOutlineLayer;
    }
}
__decorate([
    serializeAsColor3()
], SelectionOutlineLayer.prototype, "outlineColor", null);
__decorate([
    serialize()
], SelectionOutlineLayer.prototype, "outlineThickness", null);
__decorate([
    serialize()
], SelectionOutlineLayer.prototype, "occlusionStrength", null);
__decorate([
    serialize()
], SelectionOutlineLayer.prototype, "occlusionThreshold", null);
__decorate([
    serialize("options")
], SelectionOutlineLayer.prototype, "_options", void 0);
RegisterClass("BABYLON.SelectionOutlineLayer", SelectionOutlineLayer);
//# sourceMappingURL=selectionOutlineLayer.js.map