#include "gfx/gpu_ssbo.h"

namespace fn::gfx {

Ssbo ssbo_create(std::size_t bytes, const void* initial_data, GLenum usage) {
    Ssbo s{};
    if (bytes == 0) return s;
    GLuint id = 0;
    glGenBuffers(1, &id);
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, id);
    glBufferData(GL_SHADER_STORAGE_BUFFER,
                 static_cast<GLsizeiptr>(bytes),
                 initial_data, usage);
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
    s.id = id;
    s.bytes = bytes;
    return s;
}

void ssbo_bind(const Ssbo& s, unsigned int binding) {
    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, s.id);
}

void ssbo_upload(const Ssbo& s, std::size_t offset,
                 std::size_t bytes, const void* data) {
    if (bytes == 0 || s.id == 0) return;
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, s.id);
    glBufferSubData(GL_SHADER_STORAGE_BUFFER,
                    static_cast<GLintptr>(offset),
                    static_cast<GLsizeiptr>(bytes),
                    data);
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
}

void ssbo_readback(const Ssbo& s, std::size_t offset,
                   std::size_t bytes, void* out) {
    if (bytes == 0 || s.id == 0) return;
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, s.id);
    glGetBufferSubData(GL_SHADER_STORAGE_BUFFER,
                       static_cast<GLintptr>(offset),
                       static_cast<GLsizeiptr>(bytes),
                       out);
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
}

void ssbo_destroy(Ssbo& s) {
    if (s.id != 0) {
        GLuint id = s.id;
        glDeleteBuffers(1, &id);
    }
    s.id = 0;
    s.bytes = 0;
}

} // namespace fn::gfx
