#include "Graphics/Shader.h"
#include "spdlog/spdlog.h"
Shader::Shader(const std::string vertexShaderPath, const std::string fragmentShaderPath)
{
    char infoLog[512];
    int succes;
    
    char* vertexCode = readFile(vertexShaderPath.c_str());
    //spdlog::info(vertexCode);
    unsigned int vertId = glCreateShader(GL_VERTEX_SHADER);
    
    glShaderSource(vertId, 1, &vertexCode, NULL);
    glCompileShader(vertId);

    glGetShaderiv(vertId, GL_COMPILE_STATUS, &succes);
    if(!succes){
        glGetShaderInfoLog(vertId, 512, NULL, infoLog);
        spdlog::error( "Vertex shader has compile error {}", infoLog);
        return;
    }


    char* fragmentCode = readFile(fragmentShaderPath.c_str());
    //spdlog::info(fragmentCode); 
    unsigned int fragId = glCreateShader(GL_FRAGMENT_SHADER);


    
    glShaderSource(fragId, 1, &fragmentCode, NULL);
    glCompileShader(fragId);

    
    glGetShaderiv(fragId, GL_COMPILE_STATUS, &succes);
    if(!succes){
        glGetShaderInfoLog(fragId, 512, NULL, infoLog);
        spdlog::error("Fragment shader has compile error {}", infoLog);
        return;
    }

    id = glCreateProgram();

    glAttachShader(id, vertId);
    glAttachShader(id, fragId);
    glLinkProgram(id);


    int success;
    glGetProgramiv(id, GL_LINK_STATUS, &success);
    if(!success) {
        glGetProgramInfoLog(id, 512, NULL, infoLog);
        printf("ERROR::SHADER_PROGRAM::LINKING_FAILED\n %s", infoLog);
    }


    delete vertexCode;
    delete fragmentCode;


}
char* Shader::readFile (const char* filePath){

    std::ifstream file ;
    file.open(filePath);

    if(file.is_open() == false){
         spdlog::info("File not found.");
         return nullptr;
    }


    // Determine the file size!
    file.seekg(0, std::ios::end);
    size_t filesize = file.tellg();

    // Undo previous seek.
    file.seekg(0, std::ios::beg);
    //spdlog::info("filesize: {}", filesize);


    // Create a big enough buffer for the file

    size_t bufferSize = filesize + 3;
    char* FileBuffer = new char[bufferSize];
 
    memset(FileBuffer, '\0', bufferSize);
   
    // read the whole file
    file.read(FileBuffer, filesize);
    
    return FileBuffer;
}

void Shader::Use()
{
    glUseProgram(id);
}


void Shader::setUniformMat4(std::string uniformName, glm::mat4 matrix4) const
{    
    glUniformMatrix4fv(glGetUniformLocation(id, uniformName.c_str()), 1, GL_FALSE, glm::value_ptr(matrix4));
}
void Shader::setUniformVec4(std::string uniformName, glm::vec4 vector4) const
{
    glUniform4fv(glGetUniformLocation(id, uniformName.c_str()), 1, glm::value_ptr(vector4));
}
void Shader::setUniformVec3(std::string uniformName, glm::vec3 vector3) const
{
    glUniform3fv(glGetUniformLocation(id, uniformName.c_str()), 1, glm::value_ptr(vector3));
}
void Shader::setUniformVec2(std::string uniformName, glm::vec2 vector2) const
{
    glUniform2fv(glGetUniformLocation(id, uniformName.c_str()),1, glm::value_ptr(vector2));
}

void Shader::setUniformFloat(std::string uniformName, float value) const
{
    glUniform1f(glGetUniformLocation(id, uniformName.c_str()), value);
}

void Shader::setUniformInt(std::string uniformName, int value) const
{
    glUniform1i(glGetUniformLocation(id, uniformName.c_str()), value);
}