import { __decorate } from "../../../tslib.es6.js";

import { editableInPropertyPage } from "../../../Decorators/nodeDecorator.js";
import { RegisterClass } from "../../../Misc/typeStore.js";
import { Vector2, Vector3 } from "../../../Maths/math.vector.js";
import { Color3, Color4 } from "../../../Maths/math.color.js";
import { BaseParticleSystem } from "../../baseParticleSystem.js";
import { NodeParticleBlock } from "../nodeParticleBlock.js";
import { _TriggerSubEmitter } from "./Triggers/triggerTools.js";
import { NodeParticleBlockConnectionPointTypes } from "../Enums/nodeParticleBlockConnectionPointTypes.js";
import { ParticleGradientBlock } from "./particleGradientBlock.js";
/**
 * Block used to get a system of particles
 */
export class SystemBlock extends NodeParticleBlock {
    /**
     * Create a new SystemBlock
     * @param name defines the block name
     */
    constructor(name) {
        super(name);
        /**
         * Gets or sets the blend mode for the particle system
         */
        this.blendMode = BaseParticleSystem.BLENDMODE_ONEONE;
        /**
         * Gets or sets the epsilon value used for comparison
         */
        this.capacity = 1000;
        /**
         * Gets or sets the manual emit count
         */
        this.manualEmitCount = -1;
        /**
         * Gets or sets the target stop duration for the particle system
         */
        this.startDelay = 0;
        /**
         * Gets or sets the target stop duration for the particle system
         */
        this.updateSpeed = 0.0167;
        /**
         * Gets or sets the number of pre-warm cycles before rendering the particle system
         */
        this.preWarmCycles = 0;
        /**
         * Gets or sets the time step multiplier used for pre-warm
         */
        this.preWarmStepOffset = 0;
        /**
         * Gets or sets a boolean indicating if the system is billboard based
         */
        this.isBillboardBased = true;
        /**
         * Gets or sets the billboard mode for the particle system
         */
        this.billBoardMode = 7;
        /**
         * Gets or sets a boolean indicating if the system coordinate space is local or global
         */
        this.isLocal = false;
        /**
         * Gets or sets a boolean indicating if the system should be disposed when stopped
         */
        this.disposeOnStop = false;
        /**
         * Gets or sets a boolean indicating if the system should not start automatically
         */
        this.doNoStart = false;
        /** @internal */
        this._internalId = SystemBlock._IdCounter++;
        /**
         * Gets or sets the custom shader configuration used to render the particles.
         * This can be used to set your own shader to render the particle system.
         */
        this.customShader = null;
        this._isSystem = true;
        this.registerInput("particle", NodeParticleBlockConnectionPointTypes.Particle);
        this.registerInput("emitRate", NodeParticleBlockConnectionPointTypes.Int, true, 10, 0);
        this.registerInput("texture", NodeParticleBlockConnectionPointTypes.Texture);
        this.registerInput("translationPivot", NodeParticleBlockConnectionPointTypes.Vector2, true);
        this.registerInput("textureMask", NodeParticleBlockConnectionPointTypes.Color4, true);
        this.registerInput("targetStopDuration", NodeParticleBlockConnectionPointTypes.Float, true, 0, 0);
        this.registerInput("onStart", NodeParticleBlockConnectionPointTypes.System, true);
        this.registerInput("onEnd", NodeParticleBlockConnectionPointTypes.System, true);
        this.registerInput("rampGradient", NodeParticleBlockConnectionPointTypes.Color4, true);
        this.registerInput("emitterPosition", NodeParticleBlockConnectionPointTypes.Vector3, true, Vector3.Zero());
        this.registerOutput("system", NodeParticleBlockConnectionPointTypes.System);
    }
    /**
     * Gets the current class name
     * @returns the class name
     */
    getClassName() {
        return "SystemBlock";
    }
    /**
     * Gets the particle input component
     */
    get particle() {
        return this._inputs[0];
    }
    /**
     * Gets the emitRate input component
     */
    get emitRate() {
        return this._inputs[1];
    }
    /**
     * Gets the texture input component
     */
    get texture() {
        return this._inputs[2];
    }
    /**
     * Gets the translationPivot input component
     */
    get translationPivot() {
        return this._inputs[3];
    }
    /**
     * Gets the textureMask input component
     */
    get textureMask() {
        return this._inputs[4];
    }
    /**
     * Gets the targetStopDuration input component
     */
    get targetStopDuration() {
        return this._inputs[5];
    }
    /**
     * Gets the onStart input component
     */
    get onStart() {
        return this._inputs[6];
    }
    /**
     * Gets the onEnd input component
     */
    get onEnd() {
        return this._inputs[7];
    }
    /**
     * Gets the rampGradient input component
     */
    get rampGradient() {
        return this._inputs[8];
    }
    /**
     * Gets the emitterPosition input component
     */
    get emitterPosition() {
        return this._inputs[9];
    }
    /**
     * Gets the system output component
     */
    get system() {
        return this._outputs[0];
    }
    /**
     * Builds the block and return a functional particle system
     * @param state defines the building state
     * @returns the built particle system
     */
    createSystem(state) {
        state.capacity = this.capacity;
        state.buildId = this._buildId++;
        this.build(state);
        const particleSystem = this.particle.getConnectedValue(state);
        particleSystem.particleTexture = this.texture.getConnectedValue(state);
        particleSystem.emitRate = this.emitRate.getConnectedValue(state);
        particleSystem.manualEmitCount = this.manualEmitCount;
        particleSystem.updateSpeed = this.updateSpeed;
        particleSystem.preWarmCycles = this.preWarmCycles;
        particleSystem.preWarmStepOffset = this.preWarmStepOffset;
        particleSystem.blendMode = this.blendMode;
        particleSystem.name = this.name;
        particleSystem._targetStopDuration = this.targetStopDuration.getConnectedValue(state) ?? 0;
        particleSystem.startDelay = this.startDelay;
        particleSystem.isBillboardBased = this.isBillboardBased;
        particleSystem.billboardMode = this.billBoardMode;
        particleSystem.translationPivot = this.translationPivot.getConnectedValue(state) || Vector2.Zero();
        particleSystem.textureMask = this.textureMask.getConnectedValue(state) ?? new Color4(1, 1, 1, 1);
        particleSystem.isLocal = this.isLocal;
        particleSystem.disposeOnStop = this.disposeOnStop;
        particleSystem.emitter = this.emitterPosition.getConnectedValue(state) ?? Vector3.Zero();
        // Apply custom shader if defined
        if (this.customShader) {
            const engine = particleSystem.getScene()?.getEngine();
            if (engine?.createEffectForParticles) {
                const defines = this.customShader.shaderOptions.defines.length > 0 ? this.customShader.shaderOptions.defines.join("\n") : "";
                const effect = engine.createEffectForParticles(this.customShader.shaderPath.fragmentElement, this.customShader.shaderOptions.uniforms, this.customShader.shaderOptions.samplers, defines);
                particleSystem.setCustomEffect(effect, 0);
                particleSystem.customShader = this.customShader;
            }
        }
        // The emit rate can vary if it is connected to another block like a gradient
        particleSystem._calculateEmitRate = () => {
            state.systemContext = particleSystem;
            return this.emitRate.getConnectedValue(state);
        };
        // Get the ramp gradients
        particleSystem.useRampGradients = false;
        if (this.rampGradient.isConnected) {
            if (this.rampGradient.connectedPoint?.ownerBlock instanceof ParticleGradientBlock) {
                // We have a possible gradient, loop through its entries
                const gradientInputs = this.rampGradient.connectedPoint?.ownerBlock.inputs;
                // Skip the first input which is the gradient selector, and we only care about the gradient values
                for (let i = 1; i < gradientInputs.length; i++) {
                    if (gradientInputs[i].isConnected) {
                        const rampEntry = gradientInputs[i].connectedPoint?.ownerBlock;
                        const color = rampEntry._inputs[0].getConnectedValue(state);
                        particleSystem.addRampGradient(rampEntry.reference, new Color3(color.r, color.g, color.b));
                        particleSystem.useRampGradients = true;
                    }
                }
            }
            else {
                // We have a single value, add it as ramp gradient
                const color = this.rampGradient.getConnectedValue(state);
                particleSystem.addRampGradient(0, new Color3(color.r, color.g, color.b));
                particleSystem.useRampGradients = true;
            }
        }
        this.system._storedValue = this;
        particleSystem.canStart = () => {
            return !this.doNoStart;
        };
        particleSystem.onStartedObservable.add((system) => {
            // Triggers
            const onStartSystem = this.onStart.getConnectedValue(state);
            if (onStartSystem) {
                system.onStartedObservable.addOnce(() => {
                    state.systemContext = particleSystem;
                    const clone = _TriggerSubEmitter(onStartSystem, state.scene, state.emitterPosition);
                    this.onDisposeObservable.addOnce(() => {
                        // Clean up the cloned system when the original system is disposed
                        clone.dispose();
                    });
                });
            }
            const onEndSystem = this.onEnd.getConnectedValue(state);
            if (onEndSystem) {
                system.onStoppedObservable.addOnce(() => {
                    state.systemContext = particleSystem;
                    const clone = _TriggerSubEmitter(onEndSystem, state.scene, state.emitterPosition);
                    this.onDisposeObservable.addOnce(() => {
                        // Clean up the cloned system when the original system is disposed
                        clone.dispose();
                    });
                });
            }
        });
        this.onDisposeObservable.addOnce(() => {
            particleSystem.dispose();
        });
        // Return
        return particleSystem;
    }
    /**
     * Serializes the system block
     * @returns The serialized object
     */
    serialize() {
        const serializationObject = super.serialize();
        serializationObject.capacity = this.capacity;
        serializationObject.manualEmitCount = this.manualEmitCount;
        serializationObject.blendMode = this.blendMode;
        serializationObject.updateSpeed = this.updateSpeed;
        serializationObject.preWarmCycles = this.preWarmCycles;
        serializationObject.preWarmStepOffset = this.preWarmStepOffset;
        serializationObject.isBillboardBased = this.isBillboardBased;
        serializationObject.billBoardMode = this.billBoardMode;
        serializationObject.isLocal = this.isLocal;
        serializationObject.disposeOnStop = this.disposeOnStop;
        serializationObject.doNoStart = this.doNoStart;
        serializationObject.startDelay = this.startDelay;
        serializationObject.customShader = this.customShader;
        return serializationObject;
    }
    /**
     * Deserializes the system block
     * @param serializationObject The serialized system
     */
    _deserialize(serializationObject) {
        super._deserialize(serializationObject);
        this.capacity = serializationObject.capacity;
        this.manualEmitCount = serializationObject.manualEmitCount ?? -1;
        this.updateSpeed = serializationObject.updateSpeed ?? 0.0167;
        this.preWarmCycles = serializationObject.preWarmCycles ?? 0;
        this.preWarmStepOffset = serializationObject.preWarmStepOffset ?? 0;
        this.isBillboardBased = serializationObject.isBillboardBased ?? true;
        this.billBoardMode = serializationObject.billBoardMode ?? 7;
        this.isLocal = serializationObject.isLocal ?? false;
        this.disposeOnStop = serializationObject.disposeOnStop ?? false;
        this.doNoStart = !!serializationObject.doNoStart;
        if (serializationObject.emitRate !== undefined) {
            this.emitRate.value = serializationObject.emitRate;
        }
        if (serializationObject.blendMode !== undefined) {
            this.blendMode = serializationObject.blendMode;
        }
        if (serializationObject.startDelay !== undefined) {
            this.startDelay = serializationObject.startDelay;
        }
        if (serializationObject.customShader !== undefined) {
            this.customShader = serializationObject.customShader;
        }
    }
}
SystemBlock._IdCounter = 0;
__decorate([
    editableInPropertyPage("Blend mode", 5 /* PropertyTypeForEdition.List */, "ADVANCED", {
        notifiers: { rebuild: true },
        embedded: true,
        options: [
            { label: "Blend Mode OneOne", value: BaseParticleSystem.BLENDMODE_ONEONE },
            { label: "Blend Mode Standard", value: BaseParticleSystem.BLENDMODE_STANDARD },
            { label: "Blend Mode Add", value: BaseParticleSystem.BLENDMODE_ADD },
            { label: "Blend Mode Multiply", value: BaseParticleSystem.BLENDMODE_MULTIPLY },
            { label: "Blend Mode MultiplyAdd", value: BaseParticleSystem.BLENDMODE_MULTIPLYADD },
        ],
    })
], SystemBlock.prototype, "blendMode", void 0);
__decorate([
    editableInPropertyPage("Capacity", 2 /* PropertyTypeForEdition.Int */, "ADVANCED", { embedded: true, notifiers: { rebuild: true }, min: 0, max: 10000 })
], SystemBlock.prototype, "capacity", void 0);
__decorate([
    editableInPropertyPage("Manual emit count", 2 /* PropertyTypeForEdition.Int */, "ADVANCED", { embedded: true, notifiers: { rebuild: true }, min: -1 })
], SystemBlock.prototype, "manualEmitCount", void 0);
__decorate([
    editableInPropertyPage("Delay start(ms)", 1 /* PropertyTypeForEdition.Float */, "ADVANCED", { embedded: true, notifiers: { rebuild: true }, min: 0 })
], SystemBlock.prototype, "startDelay", void 0);
__decorate([
    editableInPropertyPage("updateSpeed", 1 /* PropertyTypeForEdition.Float */, "ADVANCED", { embedded: true, notifiers: { rebuild: true }, min: 0, max: 0.1 })
], SystemBlock.prototype, "updateSpeed", void 0);
__decorate([
    editableInPropertyPage("Pre-warm cycles", 1 /* PropertyTypeForEdition.Float */, "ADVANCED", { embedded: true, notifiers: { rebuild: true }, min: 0 })
], SystemBlock.prototype, "preWarmCycles", void 0);
__decorate([
    editableInPropertyPage("Pre-warm step multiplier", 1 /* PropertyTypeForEdition.Float */, "ADVANCED", { embedded: true, notifiers: { rebuild: true }, min: 0 })
], SystemBlock.prototype, "preWarmStepOffset", void 0);
__decorate([
    editableInPropertyPage("Is billboard based", 0 /* PropertyTypeForEdition.Boolean */, "ADVANCED", { embedded: true, notifiers: { rebuild: true } })
], SystemBlock.prototype, "isBillboardBased", void 0);
__decorate([
    editableInPropertyPage("Billboard mode", 5 /* PropertyTypeForEdition.List */, "ADVANCED", {
        notifiers: { rebuild: true },
        embedded: true,
        options: [
            { label: "Billboard Mode All", value: 7 },
            { label: "Billboard Mode Y", value: 2 },
            { label: "Billboard Mode Stretched", value: 8 },
            { label: "Billboard Mode Stretched Local", value: 9 },
        ],
    })
], SystemBlock.prototype, "billBoardMode", void 0);
__decorate([
    editableInPropertyPage("Is local", 0 /* PropertyTypeForEdition.Boolean */, "ADVANCED", { embedded: true, notifiers: { rebuild: true } })
], SystemBlock.prototype, "isLocal", void 0);
__decorate([
    editableInPropertyPage("Dispose on stop", 0 /* PropertyTypeForEdition.Boolean */, "ADVANCED", { embedded: true, notifiers: { rebuild: true } })
], SystemBlock.prototype, "disposeOnStop", void 0);
__decorate([
    editableInPropertyPage("Do no start", 0 /* PropertyTypeForEdition.Boolean */, "ADVANCED", { embedded: true, notifiers: { rebuild: true } })
], SystemBlock.prototype, "doNoStart", void 0);
RegisterClass("BABYLON.SystemBlock", SystemBlock);
//# sourceMappingURL=systemBlock.js.map