#include "map_layer.h"
#include "TextureMap.h"
#include <tmxlite/Layer.hpp>
#include <tmxlite/TileLayer.hpp>

MapLayer::MapLayer() {}

MapLayer::~MapLayer() { m_subsets.clear(); }

bool MapLayer::create(const tmx::Map & map, std::uint32_t layerIndex,
					  const std::vector<TextureMap *> & textures) {
	const auto & layers = map.getLayers();
	assert(layers[layerIndex]->getType() == tmx::Layer::Type::Tile);

	const auto & layer = layers[layerIndex]->getLayerAs<tmx::TileLayer>();
	const auto mapSize = map.getTileCount();
	const auto mapTileSize = map.getTileSize();
	const auto & tileSets = map.getTilesets();

	const auto tintColour = layer.getTintColour();
	const SDL_Colour vertColour = {tintColour.r, tintColour.g, tintColour.b, tintColour.a};

	for (auto i = 0u; i < tileSets.size(); ++i) {
		//check tile ID to see if it falls within the current tile set
		const auto & ts = tileSets[i];
		const auto & tileIDs = layer.getTiles();

		const auto texSize = textures[i]->getSize();
		const auto tileCountX = texSize.x / mapTileSize.x;
		const auto tileCountY = texSize.y / mapTileSize.y;

		const float uNorm = static_cast<float>(mapTileSize.x) / texSize.x;
		const float vNorm = static_cast<float>(mapTileSize.y) / texSize.y;

		std::vector<SDL_Vertex> verts;
		for (auto y = 0u; y < mapSize.y; ++y) {
			for (auto x = 0u; x < mapSize.x; ++x) {
				const auto idx = y * mapSize.x + x;
				if (idx < tileIDs.size() && tileIDs[idx].ID >= ts.getFirstGID()
					&& tileIDs[idx].ID < (ts.getFirstGID() + ts.getTileCount())) {
					//tex coords
					auto idIndex = (tileIDs[idx].ID - ts.getFirstGID());
					float u = static_cast<float>(idIndex % tileCountX);
					float v = static_cast<float>(idIndex / tileCountY);
					u *= mapTileSize
							 .x; //TODO we should be using the tile set size, as this may be different from the map's grid size
					v *= mapTileSize.y;

					//normalise the UV
					u /= textures[i]->getSize().x;
					v /= textures[i]->getSize().y;

					//vert pos
					const float tilePosX = static_cast<float>(x) * mapTileSize.x;
					const float tilePosY = (static_cast<float>(y) * mapTileSize.y);

					//push back to vert array
					SDL_Vertex vert = {{tilePosX, tilePosY}, vertColour, {u, v}};
					verts.emplace_back(vert);
					vert = {{tilePosX + mapTileSize.x, tilePosY}, vertColour, {u + uNorm, v}};
					verts.emplace_back(vert);
					vert = {{tilePosX, tilePosY + mapTileSize.y}, vertColour, {u, v + vNorm}};
					verts.emplace_back(vert);

					vert = {{tilePosX, tilePosY + mapTileSize.y}, vertColour, {u, v + vNorm}};
					verts.emplace_back(vert);
					vert = {{tilePosX + mapTileSize.x, tilePosY}, vertColour, {u + uNorm, v}};
					verts.emplace_back(vert);
					vert = {{tilePosX + mapTileSize.x, tilePosY + mapTileSize.y},
							vertColour,
							{u + uNorm, v + vNorm}};
					verts.emplace_back(vert);
				}
			}
		}

		if (!verts.empty()) {
			m_subsets.emplace_back();
			m_subsets.back().texture = *textures[i];
			m_subsets.back().vertexData.swap(verts);
		}
	}

	return true;
}

void MapLayer::draw(SDL_Renderer * renderer) const {
	assert(renderer);
	for (const auto & s : m_subsets) {
		SDL_RenderGeometry(renderer, s.texture, s.vertexData.data(),
						   static_cast<std::int32_t>(s.vertexData.size()), nullptr, 0);
	}
}