#include "pw/visual/vertex_array.hpp"

#include "pw/core/mesh.hpp"
#include "pw/core/size.hpp"
#include "pw/core/debug.hpp"

#include "glad/glad.h"

#include <algorithm>


namespace pw {

struct vertex_array::impl {

	GLuint _vao = 0;


	std::vector<GLuint> _vbos;

	GLint _mesh_elements = {0};

	impl()
	{
	}

	~impl()
	{
		release();
	}

	bool ready() const
	{
		return GL_TRUE == glIsVertexArray(_vao);
	}

	void create(const geometry& m)
	{
        // reset if the renderer already in use
		if (ready()) {
			release();
		}

		glGenVertexArrays(1,&_vao);


		glBindVertexArray(_vao);

		size_t arrays_needed = 0;

		// should bail out here
		if (!m.vertices().empty()) arrays_needed++;
		if (!m.indices().empty()) arrays_needed++;


		_mesh_elements = m.indices().size();

		// TODO: implement the other arrays

		_vbos.resize(arrays_needed);

		glGenBuffers(_vbos.size(), _vbos.data());

		// vertices
		glBindBuffer(GL_ARRAY_BUFFER, _vbos[0]);
		glBufferData(GL_ARRAY_BUFFER,
					 sizeof(m.vertices().front()) * m.vertices().size(),
					 m.vertices().data(),
					 GL_STATIC_DRAW);

		glEnableVertexAttribArray(0);
		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);

		// indices
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbos[1]);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(m.indices().front()) * m.indices().size(), m.indices().data(),
				GL_STATIC_DRAW);

		// stop binding
		glBindVertexArray(0);

        auto error = glGetError();
        if (error != GL_NO_ERROR) {
            debug::e() << "GL error: " << error;
        }

        debug::d() << "Vertices: " << m.vertices().size();
        debug::d() << "Indices   " << m.indices().size();

	}


	void release()
	{
		glDeleteVertexArrays(1,&_vao);
		for (auto vbo : _vbos)
			glDeleteBuffers(1,&vbo);

		_vbos.clear();
	}

    void draw()
    {
		glBindVertexArray(_vao);
		glDrawElements(GL_TRIANGLES, _mesh_elements, GL_UNSIGNED_INT, nullptr);
		glBindVertexArray(0);

        auto error = glGetError();
        if (error != GL_NO_ERROR) {
            debug::e() << "GL error: " << error;
        }

    }

//	GLint get_mode(vertex_array::)

};

//
//
//

vertex_array::vertex_array()
	: _impl(std::make_unique<vertex_array::impl>())
{
}

vertex_array::vertex_array(const geometry &m)
{
	vertex_array();
	_impl->create(m);
}

vertex_array::~vertex_array()
{
}

bool vertex_array::ready() const
{
	return _impl->ready();
}

void vertex_array::create(const geometry &m)
{
	_impl->create(m);
}

void vertex_array::release()
{
	_impl->release();
}

void vertex_array::draw()
{
	_impl->draw();
}


}
