package electrosphere.renderer.shader;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.management.RuntimeErrorException;

import org.lwjgl.opengl.GL40;

import electrosphere.engine.Globals;
import electrosphere.engine.assetmanager.AssetDataStrings;
import electrosphere.logger.LoggerInterface;
import electrosphere.renderer.OpenGLState;
import electrosphere.renderer.RenderingEngine;
import electrosphere.util.FileUtils;

/**
 * A visual shader program
 */
public class VisualShader implements Shader {

    /**
     * The vertex shader location
     */
    int vertexShader;

    /**
     * The fragment shader location
     */
    int fragmentShader;

    /**
     * The shader's ID
     */
    int shaderId;
    
    
    
    /**
     * The map of uniform location -> current value of uniform
     */
    public Map<Integer,Object> uniformMap = new HashMap<Integer,Object>();

    /**
     * The map of path -> already compiled shader
     */
    static Map<String,VisualShader> alreadyCompiledMap = new HashMap<String,VisualShader>();


    /**
     * Recursively preprocesses a file
     * @param currentFile The file to preprocess
     * @return The contents of the file
     */
    private static String recursivelyPreprocessFile(String input){
        return VisualShader.recursivelyPreprocessFile(FileUtils.getAssetFile(input), new LinkedList<String>());
    }

    /**
     * Recursively preprocesses a file
     * @param currentFile The file to preprocess
     * @return The contents of the file
     */
    private static String recursivelyPreprocessFile(File currentFile, List<String> includes){
        String contents = null;
        try {
            contents = Files.readString(currentFile.toPath());
        } catch (IOException e) {
            LoggerInterface.loggerRenderer.ERROR(e);
        }
        Pattern includePattern = Pattern.compile("#include \"(.*)\"");
        Matcher matcher = includePattern.matcher(contents);
        int i = 1;
        while(matcher.find()){
            String group = matcher.group(i);
            if(!includes.contains(group)){
                File directory = currentFile.getParentFile();
                File newFile = new File(directory.getPath() + "/" + group);
                String includeContent = VisualShader.recursivelyPreprocessFile(newFile, includes);
                contents = contents.replace("#include \"" + group + "\"", includeContent);
            }
            i++;
        }
        //remove strings that we don't want to include
        contents = contents.replace("#extension GL_ARB_shading_language_include : require","");
        return contents;
    }
    
    /**
     * Smart assembles a shader
     * @param ContainsBones true if the mesh contains bones
     * @param apply_lighting true if lighting should be applied
     * @return The visual shader
     */
    public static VisualShader smartAssembleShader(boolean ContainsBones, boolean apply_lighting){

        //return shader if it has already been compiled
        String shaderKey = ContainsBones + "-" + apply_lighting;
        if(alreadyCompiledMap.containsKey(shaderKey)){
            return alreadyCompiledMap.get(shaderKey);
        }
        
        String vertex_shader_path = "";
        if(ContainsBones){
            vertex_shader_path = "/Shaders/VertexShader.vs";
        } else {
            vertex_shader_path = "/Shaders/VertexShaderNoBones.vs";
        }
        
        String fragment_shader_path = "/Shaders/FragmentShader.fs";
        //
        //Create ShaderProgram object
        //
        VisualShader rVal = new VisualShader();
        //
        //Read in shader programs
        //
        String vertexShaderSource = VisualShader.recursivelyPreprocessFile(vertex_shader_path);
        String fragmentShaderSource = VisualShader.recursivelyPreprocessFile(fragment_shader_path);
        //Creates a new shader object and assigns its 'pointer' to the integer "vertexShader"
        rVal.vertexShader = GL40.glCreateShader(GL40.GL_VERTEX_SHADER);
        //This alerts openGL to the presence of a vertex shader and points the shader at its source
        GL40.glShaderSource(rVal.vertexShader, vertexShaderSource);
        //Compiles the source for the vertex shader object
        GL40.glCompileShader(rVal.vertexShader);
        //The following tests if the vertex shader compiles successfully
        int success;
        success = GL40.glGetShaderi(rVal.vertexShader, GL40.GL_COMPILE_STATUS);
        if (success != GL40.GL_TRUE) {
            LoggerInterface.loggerRenderer.WARNING("Vertex Shader failed to compile!");
            LoggerInterface.loggerRenderer.WARNING("Source is: ");
            LoggerInterface.loggerRenderer.WARNING(GL40.glGetShaderSource(rVal.vertexShader));
            LoggerInterface.loggerRenderer.ERROR("Runtime Exception", new RuntimeException(GL40.glGetShaderInfoLog(rVal.vertexShader)));
        }
        //Creates and opengl object for a fragment shader and assigns its 'pointer' to the integer fragmentShader
        rVal.fragmentShader = GL40.glCreateShader(GL40.GL_FRAGMENT_SHADER);
        //This points the opengl shadder object to its proper source
        GL40.glShaderSource(rVal.fragmentShader, fragmentShaderSource);
        //This compiles the shader object
        GL40.glCompileShader(rVal.fragmentShader);
        //This tests for the success of the compile attempt
        success = GL40.glGetShaderi(rVal.fragmentShader, GL40.GL_COMPILE_STATUS);
        if (success != GL40.GL_TRUE) {
            LoggerInterface.loggerRenderer.WARNING("Fragment Shader failed to compile!");
            LoggerInterface.loggerRenderer.WARNING("Source is: ");
            LoggerInterface.loggerRenderer.WARNING(GL40.glGetShaderSource(rVal.fragmentShader));
            LoggerInterface.loggerRenderer.ERROR("Runtime Exception", new RuntimeException(GL40.glGetShaderInfoLog(rVal.fragmentShader)));
        }
        //This creates a shader program opengl object and assigns its 'pointer' to the integer shaderProgram
        rVal.shaderId = GL40.glCreateProgram();
        //This attaches the vertex and fragment shaders to the program
        GL40.glAttachShader(rVal.shaderId, rVal.vertexShader);
        GL40.glAttachShader(rVal.shaderId, rVal.fragmentShader);
        //This links the program to the GPU (I think its to the GPU anyway)
        GL40.glLinkProgram(rVal.shaderId);
        //Tests for the success of the shader program creation
        success = GL40.glGetProgrami(rVal.shaderId, GL40.GL_LINK_STATUS);
        if (success != GL40.GL_TRUE) {
            throw new RuntimeException(GL40.glGetProgramInfoLog(rVal.shaderId));
        }        
        
        //Deletes the individual shader objects to free up memory
        GL40.glDeleteShader(rVal.vertexShader);
        GL40.glDeleteShader(rVal.fragmentShader);
        
        
        
        alreadyCompiledMap.put(shaderKey,rVal);
        
        return rVal;
    }


    /**
     * Intelligently assembles a shader for use in OIT part of render pipeline
     * @param ContainsBones True if contains bones
     * @param apply_lighting True if should apply lighting
     * @return The int-pointer to the shader compiled
     */
    public static VisualShader smartAssembleOITProgram(boolean ContainsBones, boolean apply_lighting){

        //return shader if it has already been compiled
        String shaderKey = "oit" + ContainsBones + "-" + apply_lighting;
        if(alreadyCompiledMap.containsKey(shaderKey)){
            return alreadyCompiledMap.get(shaderKey);
        }
        
        String vertex_shader_path = "";
        if(ContainsBones){
            vertex_shader_path = "/Shaders/core/oit/general/VertexShader.vs";
        } else {
            vertex_shader_path = "/Shaders/core/oit/general/VertexShaderNoBones.vs";
        }
        
        String fragment_shader_path = "/Shaders/core/oit/general/FragmentShader.fs";
        //
        //Create ShaderProgram object
        //
        VisualShader rVal = new VisualShader();
        //
        //Read in shader programs
        //
        String vertexShaderSource = VisualShader.recursivelyPreprocessFile(vertex_shader_path);
        String fragmentShaderSource = VisualShader.recursivelyPreprocessFile(fragment_shader_path);
        //Creates a new shader object and assigns its 'pointer' to the integer "vertexShader"
        rVal.vertexShader = GL40.glCreateShader(GL40.GL_VERTEX_SHADER);
        //This alerts openGL to the presence of a vertex shader and points the shader at its source
        GL40.glShaderSource(rVal.vertexShader, vertexShaderSource);
        //Compiles the source for the vertex shader object
        GL40. glCompileShader(rVal.vertexShader);
        //The following tests if the vertex shader compiles successfully
        int success;
        success = GL40.glGetShaderi(rVal.vertexShader, GL40.GL_COMPILE_STATUS);
        if (success != GL40.GL_TRUE) {
            LoggerInterface.loggerRenderer.WARNING("Vertex Shader failed to compile!");
            LoggerInterface.loggerRenderer.WARNING("Source is: ");
            LoggerInterface.loggerRenderer.WARNING(GL40.glGetShaderSource(rVal.vertexShader));
            LoggerInterface.loggerRenderer.ERROR("Runtime Exception", new RuntimeException(GL40.glGetShaderInfoLog(rVal.vertexShader)));
        }
        //Creates and opengl object for a fragment shader and assigns its 'pointer' to the integer fragmentShader
        rVal.fragmentShader = GL40.glCreateShader(GL40.GL_FRAGMENT_SHADER);
        //This points the opengl shadder object to its proper source
        GL40.glShaderSource(rVal.fragmentShader, fragmentShaderSource);
        //This compiles the shader object
        GL40.glCompileShader(rVal.fragmentShader);
        //This tests for the success of the compile attempt
        success = GL40.glGetShaderi(rVal.fragmentShader, GL40.GL_COMPILE_STATUS);
        if (success != GL40.GL_TRUE) {
            LoggerInterface.loggerRenderer.WARNING("Fragment Shader failed to compile!");
            LoggerInterface.loggerRenderer.WARNING("Source is: ");
            LoggerInterface.loggerRenderer.WARNING(GL40.glGetShaderSource(rVal.fragmentShader));
            LoggerInterface.loggerRenderer.ERROR("Runtime Exception", new RuntimeException(GL40.glGetShaderInfoLog(rVal.fragmentShader)));
        }
        //This creates a shader program opengl object and assigns its 'pointer' to the integer shaderProgram
        rVal.shaderId = GL40.glCreateProgram();
        //This attaches the vertex and fragment shaders to the program
        GL40.glAttachShader(rVal.shaderId, rVal.vertexShader);
        GL40.glAttachShader(rVal.shaderId, rVal.fragmentShader);
        //This links the program to the GPU (I think its to the GPU anyway)
        GL40. glLinkProgram(rVal.shaderId);
        //Tests for the success of the shader program creation
        success = GL40.glGetProgrami(rVal.shaderId, GL40.GL_LINK_STATUS);
        if (success != GL40.GL_TRUE) {
            throw new RuntimeException(GL40.glGetProgramInfoLog(rVal.shaderId));
        }        
        
        //Deletes the individual shader objects to free up memory
        GL40.glDeleteShader(rVal.vertexShader);
        GL40.glDeleteShader(rVal.fragmentShader);
        
        
        
        alreadyCompiledMap.put(shaderKey,rVal);
        
        return rVal;
    }


    /**
     * Loads the default shader program
     * @return The default shader
     */
    public static VisualShader loadDefaultShaderProgram(){
        
        
        //
        //Create ShaderProgram object
        //
        VisualShader rVal = new VisualShader();
        //
        //Read in shader programs
        //
        String vertexShaderSource = VisualShader.recursivelyPreprocessFile(AssetDataStrings.SHADER_DEFAULT_VERT);
        String fragmentShaderSource = VisualShader.recursivelyPreprocessFile(AssetDataStrings.SHADER_DEFAULT_FRAG);
        //Creates a new shader object and assigns its 'pointer' to the integer "vertexShader"
        rVal.vertexShader = GL40.glCreateShader(GL40.GL_VERTEX_SHADER);
        //This alerts openGL to the presence of a vertex shader and points the shader at its source
        GL40.glShaderSource(rVal.vertexShader, vertexShaderSource);
        //Compiles the source for the vertex shader object
        GL40.glCompileShader(rVal.vertexShader);
        //The following tests if the vertex shader compiles successfully
        int success;
        success = GL40.glGetShaderi(rVal.vertexShader, GL40.GL_COMPILE_STATUS);
        if (success != GL40.GL_TRUE) {
            LoggerInterface.loggerRenderer.WARNING("Vertex Shader failed to compile!");
            LoggerInterface.loggerRenderer.WARNING("Source is: ");
            LoggerInterface.loggerRenderer.WARNING(GL40.glGetShaderSource(rVal.vertexShader));
            LoggerInterface.loggerRenderer.ERROR("Runtime Exception", new RuntimeException(GL40.glGetShaderInfoLog(rVal.vertexShader)));
        }
        //Creates and opengl object for a fragment shader and assigns its 'pointer' to the integer fragmentShader
        rVal.fragmentShader = GL40.glCreateShader(GL40.GL_FRAGMENT_SHADER);
        //This points the opengl shadder object to its proper source
        GL40.glShaderSource(rVal.fragmentShader, fragmentShaderSource);
        //This compiles the shader object
        GL40.glCompileShader(rVal.fragmentShader);
        //This tests for the success of the compile attempt
        success = GL40.glGetShaderi(rVal.fragmentShader, GL40.GL_COMPILE_STATUS);
        if (success != GL40.GL_TRUE) {
            LoggerInterface.loggerRenderer.WARNING("Fragment Shader failed to compile!");
            LoggerInterface.loggerRenderer.WARNING("Source is: ");
            LoggerInterface.loggerRenderer.WARNING(GL40.glGetShaderSource(rVal.fragmentShader));
            LoggerInterface.loggerRenderer.ERROR("Runtime Exception", new RuntimeException(GL40.glGetShaderInfoLog(rVal.fragmentShader)));
        }
        //This creates a shader program opengl object and assigns its 'pointer' to the integer shaderProgram
        rVal.shaderId = GL40.glCreateProgram();
        //This attaches the vertex and fragment shaders to the program
        GL40.glAttachShader(rVal.shaderId, rVal.vertexShader);
        GL40.glAttachShader(rVal.shaderId, rVal.fragmentShader);
        //This links the program to the GPU (I think its to the GPU anyway)
        GL40.glLinkProgram(rVal.shaderId);
        //Tests for the success of the shader program creation
        success = GL40.glGetProgrami(rVal.shaderId, GL40.GL_LINK_STATUS);
        if (success != GL40.GL_TRUE) {
            throw new RuntimeException(GL40.glGetProgramInfoLog(rVal.shaderId));
        }        
        
        //Deletes the individual shader objects to free up memory
        GL40.glDeleteShader(rVal.vertexShader);
        GL40.glDeleteShader(rVal.fragmentShader);
        
        
        
        return rVal;
    }
    
    /**
     * Loads a specific shader
     * @param vertexPath The vertex shader's path
     * @param fragmentPath The fragment shader's path
     * @return The visual shader
     */
    public static VisualShader loadSpecificShader(String vertexPath, String fragmentPath){
        VisualShader rVal = new VisualShader();
        
        //
        //Read in shader programs
        //
        String vertexShaderSource = VisualShader.recursivelyPreprocessFile(vertexPath);
        String fragmentShaderSource = VisualShader.recursivelyPreprocessFile(fragmentPath);
        //Creates a new shader object and assigns its 'pointer' to the integer "vertexShader"
        rVal.vertexShader = GL40.glCreateShader(GL40.GL_VERTEX_SHADER);
        //This alerts openGL to the presence of a vertex shader and points the shader at its source
        GL40.glShaderSource(rVal.vertexShader, vertexShaderSource);
        //Compiles the source for the vertex shader object
        GL40.glCompileShader(rVal.vertexShader);
        //The following tests if the vertex shader compiles successfully
        int success;
        success = GL40.glGetShaderi(rVal.vertexShader, GL40.GL_COMPILE_STATUS);
        if (success != GL40.GL_TRUE) {
            List<Object> errorLines = new LinkedList<Object>();
            LoggerInterface.loggerRenderer.WARNING("Failed to load " + vertexPath + " ... attempting alternatives");
            //report failed to load shader
            errorLines.add("Vertex Shader failed to compile!");
            errorLines.add("Source File is: " + vertexPath);
            errorLines.add("Source is: ");
            errorLines.add(GL40.glGetShaderSource(rVal.vertexShader));
            errorLines.add(new RuntimeException(GL40.glGetShaderInfoLog(rVal.vertexShader)));
            //attempt loading alternative shaders
            List<String> availableAlternatives = Globals.shaderOptionMap.getAlternativesForFile(vertexPath);
            int alternativesAttempted = 0;
            if(availableAlternatives != null){
                for(String alternative : availableAlternatives){
                    alternativesAttempted++;
                    //load file
                    try {
                        vertexShaderSource = FileUtils.getAssetFileAsString(alternative);
                    } catch (IOException e) {
                        LoggerInterface.loggerEngine.ERROR("Failed to load shader alternative " + alternative, e);
                    }
                    //Creates a new shader object and assigns its 'pointer' to the integer "vertexShader"
                    rVal.vertexShader = GL40.glCreateShader(GL40.GL_VERTEX_SHADER);
                    //This alerts openGL to the presence of a vertex shader and points the shader at its source
                    GL40.glShaderSource(rVal.vertexShader, vertexShaderSource);
                    //Compiles the source for the vertex shader object
                    GL40.glCompileShader(rVal.vertexShader);
                    //The following tests if the vertex shader compiles successfully
                    success = GL40.glGetShaderi(rVal.vertexShader, GL40.GL_COMPILE_STATUS);
                    if (success == GL40.GL_TRUE) {
                        LoggerInterface.loggerRenderer.WARNING("Successfully loaded alternative shader " + alternative);
                        break;
                    } else {
                        errorLines.add("Vertex Shader failed to compile!");
                        errorLines.add("Source File is: " + vertexPath);
                        errorLines.add("Source is: ");
                        errorLines.add(GL40.glGetShaderSource(rVal.vertexShader));
                        errorLines.add(new RuntimeException(GL40.glGetShaderInfoLog(rVal.vertexShader)));
                    }
                }
                if(success != GL40.GL_TRUE){
                    for(Object object : errorLines){
                        if(object instanceof String){
                            LoggerInterface.loggerRenderer.WARNING((String)object);
                        } else if(object instanceof RuntimeErrorException){
                            LoggerInterface.loggerRenderer.ERROR("Runtime Exception", (RuntimeErrorException)object);
                        }
                    }
                }
                LoggerInterface.loggerRenderer.WARNING("Attempted " + alternativesAttempted + " alternative shaders");
            }
        }
        //Creates and opengl object for a fragment shader and assigns its 'pointer' to the integer fragmentShader
        rVal.fragmentShader = GL40.glCreateShader(GL40.GL_FRAGMENT_SHADER);
        //This points the opengl shadder object to its proper source
        GL40.glShaderSource(rVal.fragmentShader, fragmentShaderSource);
        //This compiles the shader object
        GL40.glCompileShader(rVal.fragmentShader);
        //This tests for the success of the compile attempt
        success = GL40.glGetShaderi(rVal.fragmentShader, GL40.GL_COMPILE_STATUS);
        if (success != GL40.GL_TRUE) {
            List<Object> errorLines = new LinkedList<Object>();
            LoggerInterface.loggerRenderer.WARNING("Failed to load " + fragmentPath + " ... attempting alternatives");
            //report failed to load shader
            errorLines.add("Fragment Shader failed to compile!");
            errorLines.add("Source File is: " + fragmentPath);
            errorLines.add("Source is: ");
            errorLines.add(GL40.glGetShaderSource(rVal.fragmentShader));
            errorLines.add(new RuntimeException(GL40.glGetShaderInfoLog(rVal.fragmentShader)));
            //attempt loading alternative shaders
            List<String> availableAlternatives = Globals.shaderOptionMap.getAlternativesForFile(fragmentPath);
            int alternativesAttempted = 0;
            if(availableAlternatives != null){
                for(String alternative : availableAlternatives){
                    alternativesAttempted++;
                    //load file
                    try {
                        fragmentShaderSource = FileUtils.getAssetFileAsString(alternative);
                    } catch (IOException e) {
                        LoggerInterface.loggerEngine.ERROR("Failed to load shader alternative " + alternative, e);
                    }
                    //Creates a new shader object and assigns its 'pointer' to the integer "vertexShader"
                    rVal.fragmentShader = GL40.glCreateShader(GL40.GL_FRAGMENT_SHADER);
                    //This alerts openGL to the presence of a vertex shader and points the shader at its source
                    GL40.glShaderSource(rVal.fragmentShader, fragmentShaderSource);
                    //Compiles the source for the vertex shader object
                    GL40.glCompileShader(rVal.fragmentShader);
                    //The following tests if the vertex shader compiles successfully
                    success = GL40.glGetShaderi(rVal.fragmentShader, GL40.GL_COMPILE_STATUS);
                    if (success == GL40.GL_TRUE) {
                        LoggerInterface.loggerRenderer.WARNING("Successfully loaded alternative shader " + alternative);
                        break;
                    } else {
                        errorLines.add("Fragment Shader failed to compile!");
                        errorLines.add("Source File is: " + fragmentPath);
                        errorLines.add("Source is: ");
                        errorLines.add(GL40.glGetShaderSource(rVal.fragmentShader));
                        errorLines.add(new RuntimeException(GL40.glGetShaderInfoLog(rVal.fragmentShader)));
                    }
                }
                if(success != GL40.GL_TRUE){
                    for(Object object : errorLines){
                        if(object instanceof String){
                            LoggerInterface.loggerRenderer.WARNING((String)object);
                        } else if(object instanceof RuntimeErrorException){
                            LoggerInterface.loggerRenderer.ERROR("Runtime Exception", (RuntimeErrorException)object);
                        }
                    }
                }
                LoggerInterface.loggerRenderer.WARNING("Attempted " + alternativesAttempted + " alternative shaders");
            }
        }
        //This creates a shader program opengl object and assigns its 'pointer' to the integer shaderProgram
        rVal.shaderId = GL40.glCreateProgram();
        //This attaches the vertex and fragment shaders to the program
        GL40.glAttachShader(rVal.shaderId, rVal.vertexShader);
        GL40.glAttachShader(rVal.shaderId, rVal.fragmentShader);
        //This links the program to the GPU (I think its to the GPU anyway)
        GL40.glLinkProgram(rVal.shaderId);
        //Tests for the success of the shader program creation
        success = GL40.glGetProgrami(rVal.shaderId, GL40.GL_LINK_STATUS);
        if (success != GL40.GL_TRUE) {
            LoggerInterface.loggerRenderer.ERROR(GL40.glGetProgramInfoLog(rVal.shaderId), new RuntimeException(GL40.glGetProgramInfoLog(rVal.shaderId)));
            LoggerInterface.loggerRenderer.WARNING("Shader sources: " + vertexPath + " " + fragmentPath);
            return Globals.defaultMeshShader;
            // throw new RuntimeException(glGetProgramInfoLog(rVal.shaderProgram));
        }        
        
        //Deletes the individual shader objects to free up memory
        GL40.glDeleteShader(rVal.vertexShader);
        GL40.glDeleteShader(rVal.fragmentShader);
        Globals.renderingEngine.checkError();
        
        
        
        return rVal;
    }
    

    /**
     * Tries to set a uniform
     * @param uniformName The name of the uniform
     * @param value The value to set the uniform to
     */
    public void setUniform(OpenGLState openGLState, String uniformName, Object value){
        //
        //Error checking
        if(uniformName == null || uniformName.equals("")){
            throw new IllegalArgumentException("Trying to set invalid uniform name");
        }
        if(this.getId() != openGLState.getActiveShader().getId()){
            throw new IllegalStateException("Trying to set uniform on shader that is not active");
        }

        //
        //get uniform location
        int uniformLocation = GL40.glGetUniformLocation(this.getId(), uniformName);
        int glErrorCode = Globals.renderingEngine.getError();
        if(glErrorCode != 0){
            LoggerInterface.loggerRenderer.DEBUG_LOOP(RenderingEngine.getErrorInEnglish(glErrorCode));
            LoggerInterface.loggerRenderer.DEBUG_LOOP("Shader id: " + this.getId());
        }

        //
        //set the uniform
        if(uniformLocation == INVALID_UNIFORM_NAME){
            LoggerInterface.loggerRenderer.DEBUG_LOOP("Searched for uniform in a shader that does not contain it. Uniform name: \"" + uniformName + "\"");
        } else {
            ShaderUtils.setUniform(openGLState, this.uniformMap, uniformLocation, value);
        }
    }

    /**
     * Gets the location of a given uniform
     * @param uniformName The name of the uniform
     * @return The location of the uniform
     */
    public int getUniformLocation(String uniformName){
        return GL40.glGetUniformLocation(this.getId(), uniformName);
    }


    @Override
    public int getId() {
        return this.shaderId;
    }
    
}
