package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader;

import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig;
import org.lwjgl.util.shaderc.Shaderc;
import org.tinylog.Logger;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;

public class ShaderCompiler {
    private ShaderCompiler(){}

    public static byte[] CompileShader(String ShaderCode, int ShaderType, boolean HLSLshader){
        long Compiler = 0;
        long Options = 0;
        byte[] CompiledShader;
        try{
            Compiler = Shaderc.shaderc_compiler_initialize();
            Options = Shaderc.shaderc_compile_options_initialize();
            if(EngineConfig.getInstance().DebugShaders()){
                Shaderc.shaderc_compile_options_set_generate_debug_info(Options);
                Shaderc.shaderc_compile_options_set_optimization_level(Options,0);
            }
            Shaderc.shaderc_compile_options_set_source_language(Options, HLSLshader ? Shaderc.shaderc_source_language_hlsl : Shaderc.shaderc_source_language_glsl);
            long CompilationResult = Shaderc.shaderc_compile_into_spv(
                    Compiler,
                    ShaderCode,
                    ShaderType,
                    HLSLshader ? "shader.hlsl" : "shader.glsl",
                    "main",
                    Options
            );
            if(Shaderc.shaderc_result_get_compilation_status(CompilationResult) !=
                    Shaderc.shaderc_compilation_status_success){
                throw new RuntimeException("Failed to Compile Shader: " + Shaderc.shaderc_result_get_error_message(CompilationResult));
            }
            ByteBuffer buffer = Shaderc.shaderc_result_get_bytes(CompilationResult);
            assert buffer != null;
            CompiledShader = new byte[buffer.remaining()];
            buffer.get(CompiledShader);
        } finally{
            Shaderc.shaderc_compile_options_release(Options);
            Shaderc.shaderc_compiler_release(Compiler);
        }
        return CompiledShader;
    }

    public static void CompileGLSLShaderOnChange(String ShaderFile, int ShaderType){
        byte[] CompiledShader;
        try{
            var glslFile = new File(ShaderFile);
            var spvFile = new File(ShaderFile + ".spv");
            if(!spvFile.exists() || glslFile.lastModified() > spvFile.lastModified()){
                Logger.debug("Compiling new [{}] shader from [{}] source",spvFile.getPath(),glslFile.getPath());
                var ShaderCode = new String(Files.readAllBytes(glslFile.toPath()));
                CompiledShader = CompileShader(ShaderCode, ShaderType, false);
                Files.write(spvFile.toPath(), CompiledShader);
            } else{
                Logger.debug("Loading Compiled Shader [{}]",spvFile.getPath());
            }
        } catch(IOException exception){
            Logger.error(exception);
            throw new RuntimeException(exception);
        }
    }
}
