#include "client/render/loader/GLLoader.h"

#include "blitz/misc/Log.h"
#include <glbinding/gl/gl.h>

using namespace gl;

namespace blitz {
namespace GL {

VertexArray::~VertexArray() {
	if (m_ID != 0)
		glDeleteVertexArrays(1, &m_ID);
}

VertexArray::VertexArray(ElementBuffer&& indicies) : m_ElementBuffer(std::move(indicies)) {
	glGenVertexArrays(1, &m_ID);
	Bind();
	BindElementArrayBuffer();
	Unbind();
}

void VertexArray::Bind() const {
	glBindVertexArray(m_ID);
}

void VertexArray::Unbind() const {
	glBindVertexArray(0);
}

void VertexArray::BindVertexBuffer(VertexBuffer& VertexBuffer) {
	VertexBuffer.Bind();
	VertexBuffer.BindVertexAttribs();
	m_VertexBuffers.push_back(std::move(VertexBuffer));
}

void VertexArray::BindElementArrayBuffer() {
	m_ElementBuffer.Bind();
}

VertexBuffer::~VertexBuffer() {
	if (m_ID != 0)
		glDeleteBuffers(1, &m_ID);
}

VertexBuffer::VertexBuffer(const std::vector<float>& data, unsigned int stride) : m_DataStride(stride) {
	glGenBuffers(1, &m_ID);
	Bind();
	glBufferData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(data.size() * sizeof(float)), nullptr, GL_STATIC_DRAW);
	glBufferSubData(GL_ARRAY_BUFFER, 0, static_cast<GLsizeiptr>(data.size() * sizeof(float)), data.data());
	Unbind();
}

void VertexBuffer::Bind() const {
	glBindBuffer(GL_ARRAY_BUFFER, m_ID);
}

void VertexBuffer::Unbind() const {
	glBindBuffer(GL_ARRAY_BUFFER, 0);
}

void VertexBuffer::AddVertexAttribPointer(unsigned int index, unsigned int coordinateSize, unsigned int offset) {
	VertexAttribPointer pointer;
	pointer.m_Index = index;
	pointer.m_Size = coordinateSize;
	pointer.m_Offset = offset;
	m_VertexAttribs.push_back(pointer);
}

void VertexBuffer::BindVertexAttribs() const {
	for (const VertexAttribPointer& pointer : m_VertexAttribs) {
		glEnableVertexAttribArray(pointer.m_Index);
		glVertexAttribPointer(pointer.m_Index, static_cast<GLint>(pointer.m_Size), GL_FLOAT, false, m_DataStride * sizeof(float),
			reinterpret_cast<void*>(pointer.m_Offset));
	}
}

ElementBuffer::ElementBuffer(const std::vector<unsigned int>& indicies) {
	m_TriangleCount = indicies.size();
	glGenBuffers(1, &m_ID);
	Bind();
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, static_cast<GLsizeiptr>(indicies.size() * sizeof(unsigned int)), nullptr, GL_STATIC_DRAW);
	glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, static_cast<GLsizeiptr>(indicies.size() * sizeof(unsigned int)), indicies.data());
	Unbind();
}

ElementBuffer::~ElementBuffer() {
	if (m_ID != 0)
		glDeleteBuffers(1, &m_ID);
}

void ElementBuffer::Bind() const {
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ID);
}

void ElementBuffer::Unbind() const {
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}

}  // namespace GL
}  // namespace blitz