#pragma once

#include "../Utils.hpp"
#include <string>
#include <array>
#include <vector>
#include <map>
#include <Ogre.h>

/**
 * @brief Information about a named rectangle in the texture atlas
 */
struct TextureRectInfo {
	std::string name;
	float u1, v1; // Top-left UV (0-1 range)
	float u2, v2; // Bottom-right UV (0-1 range)

	TextureRectInfo()
		: u1(0)
		, v1(0)
		, u2(1)
		, v2(1)
	{
	}
	TextureRectInfo(const std::string &n, float left, float top,
			float right, float bottom)
		: name(n)
		, u1(left)
		, v1(top)
		, u2(right)
		, v2(bottom)
	{
	}
};

/**
 * @brief Component for procedural texture generation with 10x10 colored rectangles
 * 
 * Uses OgreProcedural to generate a texture with a grid of colored rectangles.
 * Each rectangle color is individually selectable.
 * Also supports named rectangles for texture atlas/UV mapping.
 */
struct ProceduralTextureComponent {
	// Unique identifier for persistent referencing across scene loads
	std::string textureId;

	// Texture name for Ogre resource
	std::string textureName;

	// Grid dimensions (default 10x10)
	static constexpr int GRID_SIZE = 10;
	static constexpr int RECT_COUNT = GRID_SIZE * GRID_SIZE;

	// Colors for each rectangle (stored as float4: r, g, b, a)
	std::array<float, RECT_COUNT * 4> colors;

	// Named rectangles for texture atlas (name -> rect info)
	std::map<std::string, TextureRectInfo> namedRects;

	// Texture size (default 512x512)
	int textureSize = 512;

	// UV margin for texture mapping (default 0.01, range 0.01-0.025)
	// Used to prevent color bleeding by adding padding around UV coordinates
	float uvMargin = 0.01f;

	// Whether the texture needs regeneration
	bool dirty = true;

	// Whether the texture has been generated
	bool generated = false;

	// Version counter - increments each time texture is regenerated
	// Used by dependent systems to detect texture changes
	unsigned int version = 0;

	// Pointer to the generated Ogre texture
	Ogre::TexturePtr ogreTexture;

	ProceduralTextureComponent()
	{
		// Unique persistent ID for referencing this texture across saves.
		textureId = generatePersistentId();

		// Initialize with default colors (checkerboard pattern)
		for (int i = 0; i < RECT_COUNT; ++i) {
			int row = i / GRID_SIZE;
			int col = i % GRID_SIZE;
			bool isWhite = (row + col) % 2 == 0;

			colors[i * 4 + 0] = isWhite ? 1.0f : 0.0f; // R
			colors[i * 4 + 1] = isWhite ? 1.0f : 0.0f; // G
			colors[i * 4 + 2] = isWhite ? 1.0f : 0.0f; // B
			colors[i * 4 + 3] = 1.0f; // A
		}
	}

	// Get color for a specific rectangle
	Ogre::ColourValue getColor(int index) const
	{
		if (index < 0 || index >= RECT_COUNT)
			return Ogre::ColourValue::White;
		return Ogre::ColourValue(colors[index * 4 + 0],
					 colors[index * 4 + 1],
					 colors[index * 4 + 2],
					 colors[index * 4 + 3]);
	}

	// Set color for a specific rectangle
	void setColor(int index, const Ogre::ColourValue &color)
	{
		if (index < 0 || index >= RECT_COUNT)
			return;
		colors[index * 4 + 0] = color.r;
		colors[index * 4 + 1] = color.g;
		colors[index * 4 + 2] = color.b;
		colors[index * 4 + 3] = color.a;
		dirty = true;
	}

	// Calculate UV coordinates for a rectangle index
	void getRectUVs(int index, float &u1, float &v1, float &u2,
			float &v2) const
	{
		int row = index / GRID_SIZE;
		int col = index % GRID_SIZE;
		float cellSize = 1.0f / GRID_SIZE;

		u1 = col * cellSize;
		v1 = row * cellSize;
		u2 = (col + 1) * cellSize;
		v2 = (row + 1) * cellSize;
	}

	// Add a named rectangle
	bool addNamedRect(const std::string &name, int rectIndex)
	{
		if (name.empty() || rectIndex < 0 || rectIndex >= RECT_COUNT)
			return false;

		float u1, v1, u2, v2;
		getRectUVs(rectIndex, u1, v1, u2, v2);

		namedRects[name] = TextureRectInfo(name, u1, v1, u2, v2);
		dirty = true; // Mark texture as dirty since rect atlas changed
		return true;
	}

	// Remove a named rectangle
	bool removeNamedRect(const std::string &name)
	{
		auto it = namedRects.find(name);
		if (it != namedRects.end()) {
			namedRects.erase(it);
			dirty = true; // Mark texture as dirty since rect atlas changed
			return true;
		}
		return false;
	}

	// Get named rectangle info
	const TextureRectInfo *getNamedRect(const std::string &name) const
	{
		auto it = namedRects.find(name);
		if (it != namedRects.end()) {
			return &it->second;
		}
		return nullptr;
	}

	// Check if a name exists
	bool hasNamedRect(const std::string &name) const
	{
		return namedRects.find(name) != namedRects.end();
	}

	// Get all named rectangles
	const std::map<std::string, TextureRectInfo> &getAllNamedRects() const
	{
		return namedRects;
	}

	void markDirty()
	{
		dirty = true;
	}
};
