//
// Created by sebastian on 24.12.25.
//

#include "ShaderProgram.h"

#include <fstream>
#include <iostream>
#include <sstream>

#include "glad/glad.h"
#include <glm/gtc/type_ptr.hpp>

ShaderProgram::ShaderProgram(std::string vert, std::string frag) {
    vertexShaderID = loadShader(vert, GL_VERTEX_SHADER);
    fragmentShaderID = loadShader(frag, GL_FRAGMENT_SHADER);

    programID = glCreateProgram();
    glAttachShader(programID, vertexShaderID);
    glAttachShader(programID, fragmentShaderID);
    glLinkProgram(programID);
    glValidateProgram(programID);
}

void ShaderProgram::start(){
    glUseProgram(programID);
}

void ShaderProgram::stop(){
    glUseProgram(0);
}

void ShaderProgram::cleanUp() {
    stop();
    glDetachShader(programID, vertexShaderID);
    glDetachShader(programID, fragmentShaderID);
    glDeleteShader(vertexShaderID);
    glDeleteShader(fragmentShaderID);
    glDeleteProgram(programID);
}


void ShaderProgram::bindAttribute(int attribute, const std::string& variableName) const {
    glBindAttribLocation(programID, attribute, variableName.c_str());
}

int ShaderProgram::getUniformLocation(const std::string &uniformName) const {
    return glGetUniformLocation(programID, uniformName.c_str());
}

void ShaderProgram::loadFloat(int location, float value) {
    glUniform1f(location, value);
}

void ShaderProgram::loadVector(const int location, const glm::vec3 vector) {
    glUniform3f(location, vector.x, vector.y, vector.z);
}

void ShaderProgram::loadBoolean(int location, bool value) {
    glUniform1i(location, value ? 1: 0);
}

void ShaderProgram::loadMatrix(int location, glm::mat4 matrix) {
    glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(matrix));
}


int ShaderProgram::loadShader(const std::string& file, int type) {
    std::ifstream shaderFile(file);
    if (!shaderFile.is_open())
    {
        std::cerr << "Failed to open shader file: " << file  << std::endl;
        return 0;
    }

    std::stringstream buffer;
    buffer << shaderFile.rdbuf();
    std::string sourceStr = buffer.str();
    const char* source = sourceStr.c_str();

    GLuint shaderID = glCreateShader(type);
    glShaderSource(shaderID, 1, &source, nullptr);
    glCompileShader(shaderID);

    // 3. Kompilierungsstatus prüfen
    GLint success;
    glGetShaderiv(shaderID, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        char infoLog[512];
        glGetShaderInfoLog(shaderID, 512, nullptr, infoLog);
        std::cerr << "ERROR: Shader compilation failed ("
                  << file << ")\n"
                  << infoLog << std::endl;
        return 0;
    }

    return shaderID;
}
