#include "EntityProgram.hpp"

#include "../Renderer.hpp"

using namespace pass;

inline void createBuffer(GLuint *id, const GLfloat *data, size_t count) {
    glGenBuffers(1, id);
    glBindBuffer(GL_ARRAY_BUFFER, *id);
    glBufferData(GL_ARRAY_BUFFER, sizeof(glm::mat4) * count, data, GL_STREAM_DRAW);
}

EntityProgram::EntityProgram(const EntityProgram::options &opts) : VoxelProgram(opts, {"INSTANCED"}) {
    constexpr auto INIT_SIZE = 100;
    createBuffer(&ModelsBufferID, NULL, INIT_SIZE);
    ModelBufferSize = INIT_SIZE;
}
EntityProgram::~EntityProgram() { }

void EntityProgram::setup(const std::vector<glm::mat4>& modelsMatrices) {
    setModels(&modelsMatrices[0][0][0], modelsMatrices.size());
    VoxelProgram::setup(glm::vec4(0), 0);
}

void EntityProgram::setModels(const GLfloat *matrices, size_t count) {
    if (count > ModelBufferSize) {
        glDeleteBuffers(1, &ModelsBufferID); // Replace buffer
        createBuffer(&ModelsBufferID, matrices, count);
        ModelBufferSize = count;
    } else {
        glBindBuffer(GL_ARRAY_BUFFER, ModelsBufferID);
        glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(glm::mat4) * count, matrices);
    }

    glEnableVertexAttribArray(LOCATION + 0);
    glVertexAttribPointer(LOCATION + 0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(glm::vec4), (void *)0);
    glVertexAttribDivisor(LOCATION + 0, 1);
    glEnableVertexAttribArray(LOCATION + 1);
    glVertexAttribPointer(LOCATION + 1, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(glm::vec4), (void *)(1 * sizeof(glm::vec4)));
    glVertexAttribDivisor(LOCATION + 1, 1);
    glEnableVertexAttribArray(LOCATION + 2);
    glVertexAttribPointer(LOCATION + 2, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(glm::vec4), (void *)(2 * sizeof(glm::vec4)));
    glVertexAttribDivisor(LOCATION + 2, 1);
    glEnableVertexAttribArray(LOCATION + 3);
    glVertexAttribPointer(LOCATION + 3, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(glm::vec4), (void *)(3 * sizeof(glm::vec4)));
    glVertexAttribDivisor(LOCATION + 3, 1);
}
void EntityProgram::disable() {
    glDisableVertexAttribArray(LOCATION + 0);
    glDisableVertexAttribArray(LOCATION + 1);
    glDisableVertexAttribArray(LOCATION + 2);
    glDisableVertexAttribArray(LOCATION + 3);
}