#include "graphics/instancedraw.h"
#include "graphics/texture.h"

#include <glad/glad.h>
#include <iostream>

TileTextureInstance::TileTextureInstance(const char* texturePath)
{
	texture = new Texture();
	texture->loadTexture(texturePath);
	if (texture)
		setup();
}

void TileTextureInstance::setup()
{
	glGenVertexArrays(1, &VAO);
	glGenBuffers(1, &VBO);
	glGenBuffers(1, &EBO);

	glBindVertexArray(VAO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);

	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
	glEnableVertexAttribArray(0);
	glEnableVertexAttribArray(1);

	glBindBuffer(GL_ARRAY_BUFFER, 0);

	glGenBuffers(1, &instanceVBO);
	glBindBuffer(GL_ARRAY_BUFFER, instanceVBO);
	
	glBufferData(GL_ARRAY_BUFFER, sizeof(InstanceData) * MAX_INSTANCES, nullptr, GL_DYNAMIC_DRAW);

	glVertexAttribIPointer(2, 1, GL_INT, sizeof(InstanceData), (void*)(offsetof(InstanceData, tileIndex)));
	glEnableVertexAttribArray(2);
	glVertexAttribDivisor(2, 1);

	for (int i = 0; i < 4; i++)
	{
		glVertexAttribPointer(3 + i, 4, GL_FLOAT, GL_FALSE, sizeof(InstanceData), (void*)(offsetof(InstanceData, modelMatrix) + (sizeof(glm::vec4) * i)));
		glEnableVertexAttribArray(3 + i);
		glVertexAttribDivisor(3 + i, 1);
	}
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);
}

void TileTextureInstance::updateInstanceData(const std::vector<InstanceData>& instanceData)
{
	if (instanceData.empty())
		return;
	numOfInstances = instanceData.size();
	glBindBuffer(GL_ARRAY_BUFFER, instanceVBO);

	InstanceData* instances = (InstanceData*)glMapBufferRange(GL_ARRAY_BUFFER, 0, 
		sizeof(InstanceData) * instanceData.size(), 
		GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
	if (!instances)
		return;

	for (int i = 0; i < instanceData.size(); i++)
	{
		instances[i].modelMatrix = instanceData[i].modelMatrix;
		instances[i].tileIndex = instanceData[i].tileIndex;
	}

	glUnmapBuffer(GL_ARRAY_BUFFER);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
}

void TileTextureInstance::draw()
{
	texture->bind();
	glBindVertexArray(VAO);
	glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr, numOfInstances);
	glBindVertexArray(0);
}
