#include "square.hpp"
#include "GL/glew.h"

/*
    Constructor: Run when square is created
    inputs:     The shader program for this square
    returns:    None
   */
Square::Square(Shader* shader)
{
    initVAO();
    initVBO();
    this->shader = shader;
    return;
}

/*
 Constructor: Run when Square is created, uses default shader since none is provided
 inputs:     None
 returns:    None
*/
Square::Square()
{
    initVAO();
    initVBO();
    
    this->shader = Shader::default_shader;
    GLuint program = this->shader->getProgram();

    this->vVertex_location = glGetAttribLocation(program, "vVertex");
    this->vColor_location = glGetAttribLocation(program, "vColor");

    this->mView_location = glGetUniformLocation(program, "mView");
    this->mProjection_location = glGetUniformLocation(program, "mProjection");
    this->mModel_location = glGetUniformLocation(program, "mModel");

    //Create the model matrix
    model_matrix = glm::mat4(1.0f);

    glm::mat4 rot = glm::toMat4(this->rotation);
    glm::mat4 trans = glm::translate(glm::mat4(1.0f), this->position);
    glm::mat4 scale = glm::scale(glm::mat4(1.0f), this->scale);
    model_matrix = trans * rot * scale;
}

void Square::initVBO()
{
    glGenBuffers(1, &(this->vbo));
    glBindBuffer(GL_ARRAY_BUFFER, this->vbo);
    colorcube();

    glBufferData(GL_ARRAY_BUFFER, sizeof(vec_points) + sizeof(vec_colors), NULL, GL_STATIC_DRAW);
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vec_points), vec_points);
    glBufferSubData(GL_ARRAY_BUFFER, sizeof(vec_points), sizeof(vec_colors), vec_colors);
}

/*
 initVBO:    Initialize the Vertex Array Object
 inputs:     None
 returns:    None
*/
void Square::initVAO()
{
    glGenVertexArrays(1, &this->vao);
    glBindVertexArray(this->vao);
}

glm::vec3 Square::GetPosition()
{
    return this->position;
}

void Square::UpdatePosition(glm::vec3 _position)
{
    this->position = _position;

    glm::mat4 rot = glm::toMat4(this->rotation);
    glm::mat4 trans = glm::translate(glm::mat4(1.0f), this->position);
    glm::mat4 scale = glm::scale(glm::mat4(1.0f), this->scale);
    model_matrix = trans * rot * scale;
}


/*
    initVBO:    Draw the Square
    inputs:     View Projection matrix for Camera
    returns:    None
   */
void Square::draw(glm::mat4 vp_matrix)
{
    //Calculate out the Model View Projection Matrix

    glUniformMatrix4fv(mvp_location, 1, GL_FALSE, &vp_matrix[0][0]);
    //Draw the Square
    uint32_t program = this->shader->getProgram();
    glUseProgram(program);
    glBindVertexArray(this->vao);
    glBindBuffer(GL_ARRAY_BUFFER, this->vbo);
    glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
    glEnableVertexAttribArray(0);
    glDrawArrays(GL_TRIANGLES, 0, 12 * 3);
    glDisableVertexAttribArray(0);
}

void Square::draw_frame(glm::mat4 v_matrix, glm::mat4 p_matrix)
{
    
    glUniformMatrix4fv(this->mModel_location, 1, GL_FALSE, &this->model_matrix[0][0]);
    glUniformMatrix4fv(this->mView_location, 1, GL_FALSE, &v_matrix[0][0]);
    glUniformMatrix4fv(this->mProjection_location, 1, GL_FALSE, &p_matrix[0][0]);
    //Draw the Square
    uint32_t program = this->shader->getProgram();
    glUseProgram(program);
    glBindVertexArray(this->vao);
    glBindBuffer(GL_ARRAY_BUFFER, this->vbo);

    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0)); //Vertex
    glEnableVertexAttribArray(0);

    glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(sizeof(vec_points)));
    glEnableVertexAttribArray(1);

    glDrawArrays(GL_TRIANGLES, 0, 12 * 3);
    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1); 
}