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

#include <iostream>
#include <string.h>

#include "td/game/BaseGame.h"

namespace td {
namespace render {

namespace WorldLoader {

const static int VERTEX_SIZE = 3;

GL::VertexArray LoadMobModel() {
	std::vector<float> positions = {
		-0.5, 0, -0.5,
		0.5, 0, -0.5,
		-0.5, 0, 0.5,

		0.5, 0, -0.5,
		-0.5, 0, 0.5,
		0.5, 0, 0.5
	};

	float yellowFloat;
	int yellow = 255 << 24 | 255 << 16 | 255;
	memcpy(&yellowFloat, &yellow, sizeof(int));

	std::vector<float> colors = {
		yellowFloat,
		yellowFloat,
		yellowFloat,

		yellowFloat,
		yellowFloat,
		yellowFloat
	};

	GL::VertexBuffer positionVBO(positions, VERTEX_SIZE);
	positionVBO.AddVertexAttribPointer(0, VERTEX_SIZE, 0);
	GL::VertexBuffer colorVBO(colors, 1);
	colorVBO.AddVertexAttribPointer(1, 1, 0);

	GL::VertexArray mobVao(colors.size()); // each pos = 1 color
	mobVao.Bind();
	mobVao.BindVertexBuffer(positionVBO);
	mobVao.BindVertexBuffer(colorVBO);
	mobVao.Unbind();
	return mobVao;
}

GL::VertexArray LoadWorldModel(const td::game::World* world) {
	std::vector<float> positions;
	std::vector<float> colors;

	for (const auto& chunkInfo : world->GetChunks()) {
		const td::game::ChunkCoord& coords = chunkInfo.first;
		td::game::ChunkPtr chunk = chunkInfo.second;

		std::int32_t chunkX = coords.x * td::game::Chunk::ChunkWidth;
		std::int32_t chunkY = coords.y * td::game::Chunk::ChunkHeight;

		for (int tileY = 0; tileY < td::game::Chunk::ChunkHeight; tileY++) {
			for (int tileX = 0; tileX < td::game::Chunk::ChunkWidth; tileX++) {
				int tileNumber = tileY * td::game::Chunk::ChunkWidth + tileX;
				td::game::TileIndex tileIndex = chunk->GetTileIndex(tileNumber);
				td::game::TilePtr tile = world->GetTilePtr(tileIndex);

				if (tile == nullptr)
					continue;

				positions.insert(positions.end(), {
					static_cast<float>(chunkX + tileX),  0,    static_cast<float>(chunkY + tileY), 
					static_cast<float>(chunkX + tileX + 1),  0, static_cast<float>(chunkY + tileY), 
					static_cast<float>(chunkX + tileX),   0,    static_cast<float>(chunkY + tileY + 1), 

					static_cast<float>(chunkX + tileX + 1), 0,  static_cast<float>(chunkY + tileY), 
					static_cast<float>(chunkX + tileX),   0,    static_cast<float>(chunkY + tileY + 1), 
					static_cast<float>(chunkX + tileX + 1), 0,  static_cast<float>(chunkY + tileY + 1)
					});

				const td::Color* tileColor = world->GetTileColor(tile);

				for (int i = 0; i < 6; i++) {
					int color = 255;
					color |= tileColor->r << 24;
					color |= tileColor->g << 16;
					color |= tileColor->b << 8;

					int newColorIndex = colors.size();
					colors.push_back(0);

					memcpy(colors.data() + newColorIndex, &color, 1 * sizeof(int));
				}
			}
		}
	}

	for (int spawnColor = 0; spawnColor < 2; spawnColor++) {
		const game::Spawn& spawn = world->GetTeam(game::TeamColor(spawnColor)).GetSpawn();
		float fromX = spawn.GetTopLeft().GetX(), toX = spawn.GetBottomRight().GetX();
		float fromY = spawn.GetTopLeft().GetY(), toY = spawn.GetBottomRight().GetY();

		positions.insert(positions.end(), {
			fromX, 0, fromY,
			fromX, 0, toY,
			toX, 0, fromY,

			toX, 0, toY,
			fromX, 0, toY,
			toX, 0, fromY,
			});

		for (int i = 0; i < 6; i++) {
			int color = 255;
			color |= world->GetSpawnColor(game::TeamColor(spawnColor)).r << 24;
			color |= world->GetSpawnColor(game::TeamColor(spawnColor)).g << 16;
			color |= world->GetSpawnColor(game::TeamColor(spawnColor)).b << 8;

			int newColorIndex = colors.size();
			colors.push_back(0);

			memcpy(colors.data() + newColorIndex, &color, 1 * sizeof(int));
		}
	}

	for (int castleColor = 0; castleColor < 2; castleColor++) {
		const game::TeamCastle& castle = world->GetTeam(game::TeamColor(castleColor)).GetCastle();
		float fromX = castle.GetTopLeft().GetX(), toX = castle.GetBottomRight().GetX();
		float fromY = castle.GetTopLeft().GetY(), toY = castle.GetBottomRight().GetY();

		positions.insert(positions.end(), {
			fromX, 0, fromY,
			fromX, 0, toY,
			toX, 0, fromY,

			toX, 0, toY,
			fromX, 0, toY,
			toX, 0, fromY,
			});

		for (int i = 0; i < 6; i++) {
			int color = 255;
			color |= world->GetSpawnColor(game::TeamColor(castleColor)).r << 24;
			color |= world->GetSpawnColor(game::TeamColor(castleColor)).g << 16;
			color |= world->GetSpawnColor(game::TeamColor(castleColor)).b << 8;

			int newColorIndex = colors.size();
			colors.push_back(0);

			memcpy(colors.data() + newColorIndex, &color, 1 * sizeof(int));
		}
	}

	GL::VertexBuffer positionVBO(positions, VERTEX_SIZE);
	positionVBO.AddVertexAttribPointer(0, VERTEX_SIZE, 0);
	GL::VertexBuffer colorVBO(colors, 1);
	colorVBO.AddVertexAttribPointer(1, 1, 0);

	GL::VertexArray worldVao(positions.size() / VERTEX_SIZE); // each pos = 3 vertecies
	worldVao.Bind();
	worldVao.BindVertexBuffer(positionVBO);
	worldVao.BindVertexBuffer(colorVBO);
	worldVao.Unbind();
	return worldVao;
}

GL::VertexArray LoadTileSelectModel() {
	std::vector<float> positions = {
		0, 0, 0,
		1, 0, 0,
		0, 0, 1,

		1, 0, 0,
		0, 0, 1,
		1, 0, 1
	};

	int color = 255 << 24 | 255 << 16 | 255 << 8 | 150;
	float colorFloat;

	memcpy(reinterpret_cast<std::uint8_t*>(&colorFloat), &color, sizeof(float));

	std::vector<float> colors(6, colorFloat);

	GL::VertexBuffer positionVBO(positions, VERTEX_SIZE);
	positionVBO.AddVertexAttribPointer(0, VERTEX_SIZE, 0);
	GL::VertexBuffer colorVBO(colors, 1);
	colorVBO.AddVertexAttribPointer(1, 1, 0);

	GL::VertexArray tileSelectVao(positions.size() / 2); // each pos = 2 vertecies
	tileSelectVao.Bind();
	tileSelectVao.BindVertexBuffer(positionVBO);
	tileSelectVao.BindVertexBuffer(colorVBO);
	tileSelectVao.Unbind();

	return tileSelectVao;
}

RenderData LoadTowerModel(game::TowerPtr tower) {
	RenderData renderData;

	float towerX, towerDX;
	float towerY, towerDY;

	if (tower->GetSize() == game::TowerSize::Little) {
		towerX = tower->GetCenterX() - 1.5f;
		towerDX = tower->GetCenterX() + 1.5f;

		towerY = tower->GetCenterY() - 1.5f;
		towerDY = tower->GetCenterY() + 1.5f;
	} else {
		towerX = tower->GetCenterX() - 2.5f;
		towerDX = tower->GetCenterX() + 2.5f;

		towerY = tower->GetCenterY() - 2.5f;
		towerDY = tower->GetCenterY() + 2.5f;
	}
	std::vector<float> positions = {
		towerX, 0, towerY,
		towerDX, 0, towerY,
		towerX, 0, towerDY,

		towerDX, 0, towerY,
		towerX, 0, towerDY,
		towerDX, 0, towerDY
	};

	renderData.positions = positions;

	std::uint8_t towerType = static_cast<std::uint8_t>(tower->GetType());
	std::uint8_t r = 10 * towerType + 40, g = 5 * towerType + 30, b = 10 * towerType + 20;

	float colorFloat;
	int color = r << 24 | g << 16 | b << 8 | 255;
	memcpy(&colorFloat, &color, sizeof(int));

	std::vector<float> colors(6, colorFloat);
	renderData.colors = colors;

	return renderData;
}


} // namespace WorldLoader


} // namespace render
} // namespace td
