#include "ogl4_vao.h"
#include <memory.h>
#include <GL/gl.h>

namespace sleek
{
    static GLuint ogl4_call_mode[] =
    {
        GL_STATIC_DRAW,
        GL_DYNAMIC_DRAW,
        GL_STREAM_DRAW
    };

    namespace driver
    {
        template<>
        ogl4_vao_identifer<false>::ogl4_vao_identifer(MeshBuffer *o, VAO_ALIGNMENT t, VAO_ALIGNMENT v) noexcept
            : identifier(o), vert(t), element(v)
        {
            glGenVertexArrays(1, &gl);
            glBindVertexArray(gl);

            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
            glBufferSubData(
                GL_ELEMENT_ARRAY_BUFFER, 0,
                sizeof(*o->indices.data()) * o->indices.size(),
                o->indices.data()
            );

            glBindBuffer(GL_ARRAY_BUFFER, vbo);
            glBufferSubData(
                GL_ARRAY_BUFFER, 0,
                sizeof(*o->vertices.data()) * o->vertices.size(),
                o->vertices.data()
            );

            glBindVertexArray(0);
        }

        template<>
        ogl4_vao_identifer<true>::ogl4_vao_identifer(MeshBuffer *o, VAO_ALIGNMENT t, VAO_ALIGNMENT v) noexcept
            : identifier(o), vert(t), element(v)
        {
            glGenVertexArrays(1, &gl);
            glBindVertexArray(gl);

            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
            glBufferSubData(
                GL_ELEMENT_ARRAY_BUFFER, 0,
                sizeof(*o->indices.data()) * o->indices.size(),
                o->indices.data()
            );

            glBindBuffer(GL_ARRAY_BUFFER, vbo);
            glBufferSubData(
                GL_ARRAY_BUFFER, 0,
                sizeof(*o->vertices.data()) * o->vertices.size(),
                o->vertices.data()
            );

            glBindVertexArray(0);
        }

        template<bool dsa>
        ogl4_vao_identifer<dsa>::~ogl4_vao_identifer() noexcept
        {
            glDeleteBuffers(1, &vbo);
            glDeleteBuffers(1, &ebo);
            glDeleteBuffers(1, &gl);
        }

        template<bool dsa>
        void* ogl4_vao_identifer<dsa>::getHardwareLink() const noexcept
        {
            GLuint *tmp = (GLuint*)&gl;
            return tmp;
        }

        template<bool dsa>
        void ogl4_vao_identifer<dsa>::bind() noexcept
        {
            glBindVertexArray(gl);
        }

        template<bool dsa>
        void ogl4_vao_identifer<dsa>::unbind() noexcept
        {
            glBindVertexArray(0);
        }

        template<>
        void ogl4_vao_identifer<false>::update() noexcept
        {
            MeshBuffer *tmp = (MeshBuffer*)owner;

            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
            glBufferSubData(
                GL_ELEMENT_ARRAY_BUFFER, 0,
                sizeof(*tmp->indices.data()) * tmp->indices.size(),
                tmp->indices.data()
            );

            glBindBuffer(GL_ARRAY_BUFFER, vbo);
            glBufferSubData(
                GL_ARRAY_BUFFER, 0,
                sizeof(*tmp->vertices.data()) * tmp->vertices.size(),
                tmp->vertices.data()
            );
        }

        template<>
        void ogl4_vao_identifer<true>::update() noexcept
        {
            MeshBuffer *tmp = (MeshBuffer*)owner;

            glNamedBufferSubDataEXT(
                ebo, 0,
                sizeof(*tmp->indices.data()) * tmp->indices.size(),
                tmp->indices.data()
            );

            glNamedBufferSubDataEXT(
                vbo, 0,
                sizeof(*tmp->vertices.data()) * tmp->vertices.size(),
                tmp->vertices.data()
            );
        }
    }
}