#include "core.hpp"
#include <cstdint>
#include <pch.hpp>

#include <opengl/opengl_vertex_array.hpp>

#include <glad/glad.h>

namespace OpenEngine {

    static GLenum ShaderDataTypeToOpenGLBaseType(ShaderDataType type)
    {
        switch (type)
        {
            case OpenEngine::ShaderDataType::Float: return GL_FLOAT;
            case OpenEngine::ShaderDataType::Float2: return GL_FLOAT;
            case OpenEngine::ShaderDataType::Float3: return GL_FLOAT;
            case OpenEngine::ShaderDataType::Float4: return GL_FLOAT;
            case OpenEngine::ShaderDataType::Mat3: return GL_FLOAT;
            case OpenEngine::ShaderDataType::Mat4: return GL_FLOAT;
            case OpenEngine::ShaderDataType::Int: return GL_INT;
            case OpenEngine::ShaderDataType::Int2: return GL_INT;
            case OpenEngine::ShaderDataType::Int3: return GL_INT;
            case OpenEngine::ShaderDataType::Int4: return GL_INT;
            case OpenEngine::ShaderDataType::Bool: return GL_BOOL;
        }

        OE_CORE_ASSERT(false, "Unknown ShaderDataType!");
        return 0;
    }

    OpenGLVertexArray::OpenGLVertexArray()
    {
        glCreateVertexArrays(1, &id);
    }

    OpenGLVertexArray::~OpenGLVertexArray()
    {
        glDeleteVertexArrays(1, &id);
    }

    void OpenGLVertexArray::Bind() const
    {
        glBindVertexArray(id);
    }
    void OpenGLVertexArray::UnBind() const
    {
        glBindVertexArray(0);
    }

    void OpenGLVertexArray::AddVertexBuffer(const std::shared_ptr<VertexBuffer>& vertex_buffer)
    {
        glBindVertexArray(id);
        vertex_buffer->Bind();

        OE_CORE_ASSERT(vertex_buffer->GetLayout().GetElements().size(), "Vertex Buffer has no layout!");

        const auto& layout = vertex_buffer->GetLayout();
        for (const auto& element : layout) {
            glEnableVertexAttribArray(index);
            glVertexAttribPointer(index,
                    element.GetComponentCount(),
                    ShaderDataTypeToOpenGLBaseType(element.type),
                    element.normalized? GL_TRUE : GL_FALSE,
                    layout.GetStride(),
                    (const void*)(intptr_t)element.offset);
            index++;
        }

        vertex_buffers.emplace_back(vertex_buffer);
    }

    void OpenGLVertexArray::SetIndexBuffer(const std::shared_ptr<IndexBuffer>& index_buffer)
    {
        glBindVertexArray(id);
        index_buffer->Bind();

        this->index_buffer = index_buffer;
    }
}
