import { ShaderLanguage } from "@babylonjs/core/Materials/shaderLanguage";
import { MmdBufferKind } from "./mmdBufferKind";
import { sdefDeclaration } from "./Shader/sdefDeclaration";
import { sdefVertex } from "./Shader/sdefVertex";
/**
 * Sdef injector
 *
 * This class is used to inject SDEF code into the vertex shader
 */
export class SdefInjector {
    /**
     * Override engine create effect
     *
     * Inject the code so that all GLSL fragment shaders support Spherical Defromation(SDEF)
     *
     * If you want shadow map or postprocessing shaders to support SDEF call this method before creating the shadow map or postprocess render pipeline
     * @param engine Engine
     */
    static OverrideEngineCreateEffect(engine) {
        const originalCreateEffect = engine.createEffect.bind(engine);
        engine.createEffect = function (baseName, attributesNamesOrOptions, uniformsNamesOrEngine, samplers, defines, fallbacks, onCompiled, onError, indexParameters, shaderLanguage = ShaderLanguage.GLSL) {
            let effectCreationOptions;
            if (attributesNamesOrOptions.attributes) {
                effectCreationOptions = attributesNamesOrOptions;
            }
            else {
                effectCreationOptions = {
                    attributes: attributesNamesOrOptions,
                    uniformsNames: uniformsNamesOrEngine,
                    uniformBuffersNames: [],
                    samplers: samplers ?? [],
                    defines: defines ?? "",
                    fallbacks: fallbacks ?? null,
                    onCompiled: onCompiled ?? null,
                    onError: onError ?? null,
                    indexParameters: indexParameters ?? null,
                    shaderLanguage: shaderLanguage
                };
            }
            if (effectCreationOptions.shaderLanguage === ShaderLanguage.GLSL || effectCreationOptions.shaderLanguage === undefined) {
                if (effectCreationOptions.uniformsNames.includes("mBones") || effectCreationOptions.samplers.includes("boneSampler")) {
                    if (effectCreationOptions.defines.indexOf("#define SDEF") === -1) {
                        effectCreationOptions.attributes.push(MmdBufferKind.MatricesSdefCKind);
                        effectCreationOptions.attributes.push(MmdBufferKind.MatricesSdefR0Kind);
                        effectCreationOptions.attributes.push(MmdBufferKind.MatricesSdefR1Kind);
                        effectCreationOptions.defines += "\n#define SDEF";
                        const originalProcessCodeAfterIncludes = effectCreationOptions.processCodeAfterIncludes;
                        effectCreationOptions.processCodeAfterIncludes = originalProcessCodeAfterIncludes
                            ? function (shaderType, code) {
                                code = originalProcessCodeAfterIncludes(shaderType, code);
                                return SdefInjector.ProcessSdefCode(shaderType, code);
                            }
                            : SdefInjector.ProcessSdefCode;
                    }
                }
            }
            return originalCreateEffect(baseName, effectCreationOptions, this);
        };
    }
    /**
     * Returns the result of putting the SDEF code in the shader
     *
     * You can use this method if you want to manually inject shader
     *
     * This method is usually not used directly
     * @param shaderType Shader type
     * @param code Shader code
     * @returns Shader code that SDEF is injected
     */
    static ProcessSdefCode(shaderType, code) {
        if (shaderType !== "vertex")
            return code;
        const vertexDefInjectionPoint = "#define CUSTOM_VERTEX_DEFINITIONS";
        if (code.includes(vertexDefInjectionPoint)) {
            code = code.replace(vertexDefInjectionPoint, `${vertexDefInjectionPoint}\n${sdefDeclaration}`);
        }
        else {
            const fallbackVertexDefInjectionPoint = "void main() {";
            code = code.replace(fallbackVertexDefInjectionPoint, `${sdefDeclaration}\nvoid main() {`);
        }
        const sdefVertexInjectionPoint = new RegExp("finalWorld=finalWorld\\*influence;", "g");
        code = code.replace(sdefVertexInjectionPoint, `${sdefVertex}\nfinalWorld=finalWorld*influence;`);
        return code;
    }
}
