#include "graphics/sprite.h"
#include "graphics/texture.h"
#include "graphics/quad.h"

bool Sprite::loaded() const { return (texture != nullptr); }

SpriteStatic::SpriteStatic(const char* texturePath)
{
	texture = new Texture();
	texture->loadTexture(texturePath);
	if (texture)
		setupSprite();
	else
		delete texture;
}

void SpriteStatic::setupSprite()
{
	quad = new UnitQuad();
}

void Sprite::bind()
{
	if (texture)
		texture->bind();
}

void SpriteStatic::draw()
{
	quad->draw();
}

SpriteStatic::~SpriteStatic()
{
	delete texture;
	delete quad;
}

SpriteAtlas::SpriteAtlas(const char* textureAtlasPath, float frameSize, bool isDirectional) : isDirectional(isDirectional)
{
	EBO = 0;
	texture = new Texture();
	texture->loadTexture(textureAtlasPath);
	if (texture)
	{
		if (!isDirectional)
			Setup(frameSize);
		else
			SetupDirectional(frameSize);
	}
	else
		delete texture;
}

void SpriteAtlas::Setup(float frameSize)
{
	int width  = texture->getWidth();
	int height = texture->getHeight();
	float frameRows = height / frameSize;
	float frameCols = width  / frameSize;

	glGenBuffers(1, &EBO);

	for (int row = 0; row < frameRows; row++)
	{
		for (int column = 0; column < frameCols; column++)
		{
			float left    = (column) * (frameSize / width);
			float right   = (column + 1) * (frameSize / width);
			float bottom  = (row) * (frameSize / height);
			float top	  = (row + 1) * (frameSize / height);
			float vertices[] = {
				-0.5f, -0.5f, 0.0f, left, bottom,		  // bottom left
				 0.5f, -0.5f, 0.0f, right, bottom, // bottom right
				 0.5f,  0.5f, 0.0f, right, top, // top right
				-0.5f,  0.5f, 0.0f, left, top		  // top left
			};

			unsigned VAO, VBO;
			glGenVertexArrays(1, &VAO);
			glGenBuffers(1, &VBO);

			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);
			glBindVertexArray(0);

			singleIDs.push_back({ VAO, VBO });
		}
	}
}

void SpriteAtlas::bindFrame(VertexIDs* f)
{
	glBindVertexArray(f->VAO);
}

void SpriteAtlas::draw()
{
	glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
	glBindVertexArray(0);
}

SpriteAtlas::~SpriteAtlas()
{
	delete texture;
	glDeleteBuffers(1, &EBO);
	for (VertexIDs idSet : singleIDs)
	{
		glDeleteBuffers(1, &idSet.VBO);
		glDeleteVertexArrays(1, &idSet.VAO);
	}
	auto deleteBufferList = [](std::pair <Direction, std::vector<VertexIDs>> items) {
		for (VertexIDs v : items.second) {
			glDeleteBuffers(1, &v.VBO);
			glDeleteVertexArrays(1, &v.VAO);
		}
	};
	std::for_each(directionalIDs.begin(), directionalIDs.end(), deleteBufferList);
	singleIDs.clear();
	directionalIDs.clear();
}

void SpriteAtlas::SetupDirectional(float frameSize)
{
	int width = texture->getWidth();
	int height = texture->getHeight();
	int frameRows = static_cast<int>(height / frameSize);
	int frameCols = static_cast<int>(width / frameSize);

	glGenBuffers(1, &EBO);

	for (int row = 0; row < frameRows; row++)
	{
		for (int col = 0; col < frameCols; col++)
		{
			Direction dir;
			// Directional spritesheets are stored, with an animation for each row,
			// on the first row we are looking down, then right etc...
			// refer to utility/direction.h to each index
			dir = (Direction)(row + 1); // note we add one, since dir: 0 is None, this is for non-directional sprites

			float left		= (col) * (frameSize / width);
			float right		= (col + 1) * (frameSize / width);
			float bottom	= (row) * (frameSize / height);
			float top		= (row + 1) * (frameSize / height);
			float vertices[] = {
				-0.5f, -0.5f, 0.0f, left, bottom,		  // bottom left
				 0.5f, -0.5f, 0.0f, right, bottom, // bottom right
				 0.5f,  0.5f, 0.0f, right, top, // top right
				-0.5f,  0.5f, 0.0f, left, top		  // top left
			};

			unsigned VAO, VBO;
			glGenVertexArrays(1, &VAO);
			glGenBuffers(1, &VBO);

			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);
			glBindVertexArray(0);

			directionalIDs[dir].push_back({ VAO, VBO });
		}
	}
}
