#include "GLProgram.h"
#include <stdint.h>
#include <iostream>

using namespace std;

namespace jes
{
    GLProgram::GLProgram()
    {
        m_id = glCreateProgram();
        if (m_id == 0u)
        {
            cerr << "Error allocating GL program object" << endl;
        }
    }

    GLProgram::~GLProgram()
    {
        if (m_id > 0u)
        {
            glDeleteProgram(m_id);
        }
    }

    GLProgram & GLProgram::attach_shader(GLShaderRef shader)
    {
        if (m_id > 0u)
        {
            glAttachShader(m_id, shader->get_id());
        }
        return *this;
    }

    GLProgram & GLProgram::bind_attribute(const char * attribute, GLuint index)
    {
        if (m_id > 0u)
        {
            glBindAttribLocation(m_id, index, attribute);
        }
        return *this;
    }

    bool GLProgram::link()
    {
        if (m_id == 0u)
            return false;

        glLinkProgram(m_id);

        GLint link_status;
        glGetProgramiv(m_id, GL_LINK_STATUS, &link_status);
        if (link_status != GL_TRUE)
        {
            GLint log_length;
            glGetProgramiv(m_id, GL_INFO_LOG_LENGTH, &log_length);
            if (log_length > 0)
            {
                char *log = new char[log_length];
                glGetProgramInfoLog(m_id, log_length, &log_length, log);
                cerr << "Program log:" << endl << log << endl;
                delete[] log;
            }
            return false;
        }

        return true;
    }

    GLint GLProgram::get_uniform(const std::string & uniform_name)
    {
        if (m_id > 0u)
        {
            auto it = m_uniforms.find(uniform_name);
            if (it != m_uniforms.end())
            {
                return it->second;
            }
            GLint loc = glGetUniformLocation(m_id, uniform_name.c_str());
            m_uniforms[uniform_name] = loc;
            return loc;
        }
        return -1;
    }
}
