#include "engine/buffers.hpp"

// Vertex Buffer Constructor
VertexBuffer::VertexBuffer() {
    glGenBuffers(1, &bufferID);
}

// Vertex Buffer Destructor
VertexBuffer::~VertexBuffer() {
    glDeleteBuffers(1, &bufferID);
}

// Bind Vertex Buffer
void VertexBuffer::bind() const {
    glBindBuffer(GL_ARRAY_BUFFER, bufferID);
}

// Set Data for Vertex Buffer
void VertexBuffer::setData(const void* data, GLsizeiptr size) {
    bind();
    glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
}

// Set Attribute Pointer for Vertex Buffer
void VertexBuffer::setAttributePointer(GLuint index, std::size_t size, GLenum type, bool normalized, std::size_t stride, std::size_t pointer) {
    bind();
    glVertexAttribPointer(index, size, type, normalized ? GL_TRUE : GL_FALSE, stride, (void*)pointer);
    glEnableVertexAttribArray(index);
}

// Element Buffer Constructor
ElementBuffer::ElementBuffer(): elementCount(0), drawMode(GL_TRIANGLES) {
    glGenBuffers(1, &bufferID);
}

// Element Buffer Destructor
ElementBuffer::~ElementBuffer() {
    glDeleteBuffers(1, &bufferID);
}

// Bind Element Buffer
void ElementBuffer::bind() const {
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferID);
}

// Set Data for Element Buffer
void ElementBuffer::setData(const void* data, GLsizeiptr size) {
    bind();
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
    elementCount = size / sizeof(GLuint); // Assuming GLuint indices
}

// Draw Elements using Element Buffer
void ElementBuffer::draw() const {
    bind();
    glDrawElements(drawMode, static_cast<GLsizei>(elementCount), GL_UNSIGNED_INT, 0);
}

// Vertex Array Object Constructor
VertexArrayObject::VertexArrayObject() {
    glGenVertexArrays(1, &arrayID);
    glBindVertexArray(arrayID);

    // Initialize associated buffers
    vertexBuffer = std::make_unique<VertexBuffer>();
    elementBuffer = std::make_unique<ElementBuffer>();
    vertexBuffer->bind();
    elementBuffer->bind();
}

// Vertex Array Object Destructor
VertexArrayObject::~VertexArrayObject() {
    glDeleteVertexArrays(1, &arrayID);
}

// Bind Vertex Array Object
void VertexArrayObject::bind() const {
    glBindVertexArray(arrayID);
}

// Draw using Vertex Array Object
void VertexArrayObject::draw() const {
    bind();
    elementBuffer->draw();
}

// Buffer Vertex Data
void VertexArrayObject::bufferVertexData(const void* data, GLsizeiptr size) {
    bind();
    vertexBuffer->setData(data, size);
}

// Buffer Element Data
void VertexArrayObject::bufferElementData(const void* data, GLsizeiptr size) {
    bind();
    elementBuffer->setData(data, size);
}

// Set Attribute Pointer
void VertexArrayObject::setAttributePointer(GLuint index, std::size_t size, GLenum type, bool normalized, std::size_t stride, std::size_t pointer) {
    bind();
    vertexBuffer->setAttributePointer(index, size, type, normalized, stride, pointer);
}