#include "pch.h"

#include "SolidColorShader.h"

#include <stdexcept>

static const std::string SOLID_COLOR_VERTEX_SHADER_SRC = R"""(
#version 330 core
layout(location = 0) in vec2 aPosition;
layout(location = 1) in vec2 aTexCoord;
            
uniform mat3 uModelMatrix;
uniform mat3 uViewMatrix;
uniform mat3 uProjectionMatrix;
            
void main() {
    vec3 p = uProjectionMatrix * uViewMatrix * uModelMatrix * vec3(aPosition, 1.0);
    gl_Position = vec4(p.xy, 0.0, 1.0);
}
)""";

static const std::string SOLID_COLOR_FRAGMENT_SHADER_SRC = R"""(
#version 330 core
out vec4 FragColor;
            
uniform vec4 uColor;
uniform float uOpacity;
            
void main() {
    vec4 finalColor = uColor;
    finalColor.a *= uOpacity;
    if (finalColor.a <= 0.001) discard;
    FragColor = finalColor;
}
)""";

class SolidColorShader::SolidColorVertexShader final : public Shader {
public:
    virtual ~SolidColorVertexShader() = default;
    std::string getShaderCode() const override {
        return SOLID_COLOR_VERTEX_SHADER_SRC;
    }
    std::string getShaderName() const override {
        return "Solid Color Vertex Shader";
    }
};

class SolidColorShader::SolidColorFragmentShader final : public Shader {
public:
    virtual ~SolidColorFragmentShader() = default;
    std::string getShaderCode() const override {
        return SOLID_COLOR_FRAGMENT_SHADER_SRC;
    }
    std::string getShaderName() const override {
        return "Solid Color Fragment Shader";
    }
};

const SolidColorShader::SolidColorVertexShader SolidColorShader::s_vertexShader;
const SolidColorShader::SolidColorFragmentShader SolidColorShader::s_fragmentShader;

SolidColorShader::SolidColorShader()
{
}

const Shader& SolidColorShader::getVertexShader() const
{
    return s_vertexShader;
}

const Shader& SolidColorShader::getFragmentShader() const
{
    return s_fragmentShader;
}

void SolidColorShader::setDefaultUniforms(ShaderProgram* program) const
{
    if (!program) return;
    program->use();
    int colorLoc = program->getUniformLocation("uColor");
    if (colorLoc != -1) {
        program->setUniform4f(colorLoc, 1.0f, 1.0f, 1.0f, 1.0f);
    }

    int opacityLoc = program->getUniformLocation("uOpacity");
    if (opacityLoc != -1) {
        program->setUniform1f(opacityLoc, 1.0f);
    }

    program->stop();
}

void SolidColorShader::setColor(ShaderProgram* program, float r, float g, float b, float a)
{
    if (!program) return;
    program->use();

    int colorLoc = program->getUniformLocation("uColor");
    if (colorLoc != -1) {
        program->setUniform4f(colorLoc, r, g, b, a);
    }

    program->stop();
}

void SolidColorShader::setOpacity(ShaderProgram* program, float opacity)
{
    if (!program) return;

    program->use();

    int opacityLoc = program->getUniformLocation("uOpacity");
    if (opacityLoc != -1) {
        program->setUniform1f(opacityLoc, opacity);
    }

    program->stop();
}