#include "StoneObject.h"

#include "utils/Utils.h"
#include <random>
#include <cmath>
#include "render/Renderer.h"
#include "PlanetData.h"

namespace ZL {

#if defined EMSCRIPTEN || defined __ANDROID__
	using std::min;
	using std::max;
#endif



		const float StoneParams::BASE_SCALE = 10.0f;          // Общий размер камня
		const float StoneParams::MIN_AXIS_SCALE = 1.0f;      // Минимальное растяжение/сжатие по оси
		const float StoneParams::MAX_AXIS_SCALE = 1.0f;      // Максимальное растяжение/сжатие по оси
		const float StoneParams::MIN_PERTURBATION = 0.0f;   // Минимальное радиальное возмущение вершины
		const float StoneParams::MAX_PERTURBATION = 0.0f;   // Максимальное радиальное возмущение вершины
		const int StoneParams::STONES_PER_TRIANGLE = 40;



	// Вспомогательная функция для получения случайного числа в диапазоне [min, max]
	float getRandomFloat(std::mt19937& gen, float min, float max) {
		std::uniform_real_distribution<> distrib(min, max);
		return static_cast<float>(distrib(gen));
	}

	// Вспомогательная функция для генерации случайной точки на треугольнике
	// Использует барицентрические координаты
	Vector3f GetRandomPointOnTriangle(const Triangle& t, std::mt19937& engine) {
		std::uniform_real_distribution<> distrib(0.0f, 1.0f);

		float r1 = getRandomFloat(engine, 0.0f, 1.0f);
		float r2 = getRandomFloat(engine, 0.0f, 1.0f);

		// Преобразование r1, r2 для получения равномерного распределения
		float a = 1.0f - std::sqrt(r1);
		float b = std::sqrt(r1) * r2;
		float c = 1.0f - a - b; // c = sqrt(r1) * (1 - r2)

		// Барицентрические координаты
		// P = a*p1 + b*p2 + c*p3
		Vector3f p1_term = t.data[0] * a;
		Vector3f p2_term = t.data[1] * b;
		Vector3f p3_term = t.data[2] * c;

		return p1_term + p2_term + p3_term;
	}


	// Икосаэдр (на основе золотого сечения phi)
	// Координаты могут быть вычислены заранее для константного икосаэдра.
	// Здесь только объявление, чтобы показать идею.

	VertexDataStruct CreateBaseConvexPolyhedron(uint64_t seed) {

		// const size_t SUBDIVISION_LEVEL = 1;  // Уровень подразделения (для более круглого камня, пока опустим)

		std::mt19937 engine(static_cast<unsigned int>(seed));

		// Золотое сечение
		const float t = (1.0f + std::sqrt(5.0f)) / 2.0f;

		// 12 вершин икосаэдра
		std::vector<Vector3f> initialVertices = {
			{ -1,  t,  0 }, {  1,  t,  0 }, { -1, -t,  0 }, {  1, -t,  0 },
			{  0, -1,  t }, {  0,  1,  t }, {  0, -1, -t }, {  0,  1, -t },
			{  t,  0, -1 }, {  t,  0,  1 }, { -t,  0, -1 }, { -t,  0,  1 }
		};

		// 20 треугольных граней (индексы вершин)
		std::vector<std::array<int, 3>> faces = {
			// 5 треугольников вокруг вершины 0
			{0, 11, 5}, {0, 5, 1}, {0, 1, 7}, {0, 7, 10}, {0, 10, 11},
			// 5 смежных полос
			{1, 5, 9}, {5, 11, 4}, {11, 10, 2}, {10, 7, 6}, {7, 1, 8},
			// 5 треугольников вокруг вершины 3
			{3, 9, 4}, {3, 4, 2}, {3, 2, 6}, {3, 6, 8}, {3, 8, 9},
			// 5 смежных полос
			{4, 9, 5}, {2, 4, 11}, {6, 2, 10}, {8, 6, 7}, {9, 8, 1}
		};

		// 1. Нормализация и Возмущение (Perturbation)
		for (Vector3f& v : initialVertices) {
			v = v.normalized() * StoneParams::BASE_SCALE; // Нормализация к сфере радиуса BASE_SCALE

			// Радиальное возмущение:
			float perturbation = getRandomFloat(engine, StoneParams::MIN_PERTURBATION, StoneParams::MAX_PERTURBATION);
			v = v * (1.0f + perturbation);
		}

		// 2. Трансформация (Масштабирование и Поворот)

		// Случайные масштабы по осям
		Vector3f scaleFactors = {
			getRandomFloat(engine, StoneParams::MIN_AXIS_SCALE, StoneParams::MAX_AXIS_SCALE),
			getRandomFloat(engine, StoneParams::MIN_AXIS_SCALE, StoneParams::MAX_AXIS_SCALE),
			getRandomFloat(engine, StoneParams::MIN_AXIS_SCALE, StoneParams::MAX_AXIS_SCALE)
		};

		// Применяем масштабирование
		for (Vector3f& v : initialVertices) {
			v(0) *= scaleFactors(0);
			v(1) *= scaleFactors(1);
			v(2) *= scaleFactors(2);
		}

		/*
		// Случайный поворот (например, вокруг трех осей)
		Vector4f qx = Eigen::Quaternionf(Eigen::AngleAxisf(getRandomFloat(engine, 0.0f, 360.0f), Eigen::Vector3f::UnitX()));//QuatFromRotateAroundX(getRandomFloat(engine, 0.0f, 360.0f));
		Vector4f qy = Eigen::Quaternionf(Eigen::AngleAxisf(getRandomFloat(engine, 0.0f, 360.0f), Eigen::Vector3f::UnitY()));//QuatFromRotateAroundY(getRandomFloat(engine, 0.0f, 360.0f));
		Vector4f qz = Eigen::Quaternionf(Eigen::AngleAxisf(getRandomFloat(engine, 0.0f, 360.0f), Eigen::Vector3f::UnitZ()));//QuatFromRotateAroundZ(getRandomFloat(engine, 0.0f, 360.0f));
		Vector4f qFinal = slerp(qx, qy, 0.5f); // Простой пример комбинирования
		qFinal = slerp(qFinal, qz, 0.5f).normalized();
		Matrix3f rotationMatrix = QuatToMatrix(qFinal);

		for (Vector3f& v : initialVertices) {
			v = MultMatrixVector(rotationMatrix, v);
		}*/

		// 3. Сглаженные Нормали и Формирование Mesh
		VertexDataStruct result;
		// Карта для накопления нормалей по уникальным позициям вершин
		// (Требует определенного оператора < для Vector3f в ZLMath.h, который у вас есть)
		std::map<Vector3f, Vector3f, Vector3fComparator> smoothNormalsMap;

		// Предварительное заполнение карты нормалями
		for (const auto& face : faces) {
			Vector3f p1 = initialVertices[face[0]];
			Vector3f p2 = initialVertices[face[1]];
			Vector3f p3 = initialVertices[face[2]];

			// Нормаль грани: (p2 - p1) x (p3 - p1)
			Vector3f faceNormal = (p2 - p1).cross(p3 - p1).normalized();

			smoothNormalsMap[p1] = smoothNormalsMap[p1] + faceNormal;
			smoothNormalsMap[p2] = smoothNormalsMap[p2] + faceNormal;
			smoothNormalsMap[p3] = smoothNormalsMap[p3] + faceNormal;
		}

		// Нормализация накопленных нормалей
		for (auto& pair : smoothNormalsMap) {
			pair.second = pair.second.normalized();
		}


		// 4. Финальное заполнение VertexDataStruct и Текстурные Координаты
		for (const auto& face : faces) {
			Vector3f p1 = initialVertices[face[0]];
			Vector3f p2 = initialVertices[face[1]];
			Vector3f p3 = initialVertices[face[2]];

			// Позиции
			result.PositionData.push_back(p1);
			result.PositionData.push_back(p2);
			result.PositionData.push_back(p3);

			// Сглаженные Нормали (из карты)
			result.NormalData.push_back(smoothNormalsMap[p1]);
			result.NormalData.push_back(smoothNormalsMap[p2]);
			result.NormalData.push_back(smoothNormalsMap[p3]);

			// Текстурные Координаты (Планарная проекция на плоскость грани)
			// p1 -> (0, 0), p2 -> (L_12, 0), p3 -> (L_13 * cos(angle), L_13 * sin(angle))
			// Где L_xy - длина вектора, angle - угол между p2-p1 и p3-p1

			Vector3f uAxis = (p2 - p1).normalized();
			Vector3f vRaw = p3 - p1;

			// Проекция vRaw на uAxis
			float uProjLen = vRaw.dot(uAxis);

			// Вектор V перпендикулярный U: vRaw - uProj
			Vector3f vAxisRaw = vRaw - (uAxis * uProjLen);

			// Длина оси V
			float vLen = vAxisRaw.norm();

			// Нормализованная ось V
			Vector3f vAxis = vAxisRaw.normalized();

			// Координаты (u, v) для p1, p2, p3 относительно p1
			Vector2f uv1 = { 0.0f, 0.0f };
			Vector2f uv2 = { (p2 - p1).norm(), 0.0f }; // p2-p1 вдоль оси U
			Vector2f uv3 = { uProjLen, vLen };          // p3-p1: u-компонента = uProjLen, v-компонента = vLen

			// Находим максимальный размер, чтобы масштабировать в [0, 1]
			float maxUV = max(uv2(0), max(uv3(0), uv3(1)));

			if (maxUV > 0.000001f) {
				// Масштабируем:
				result.TexCoordData.push_back(uv1);
				result.TexCoordData.push_back(uv2 * (1.f / maxUV));
				result.TexCoordData.push_back(uv3 * (1.f / maxUV));
			}
			else {
				// Предотвращение деления на ноль для вырожденных граней
				result.TexCoordData.push_back({ 0.0f, 0.0f });
				result.TexCoordData.push_back({ 0.0f, 0.0f });
				result.TexCoordData.push_back({ 0.0f, 0.0f });
			}
		}

		return result;
	}

	Triangle createLocalTriangle(const Triangle& sampleTri)
	{
		// Находим центр в 3D
		Vector3f center = (sampleTri.data[0] + sampleTri.data[1] + sampleTri.data[2]) * (1.0f / 3.0f);

		// Строим базис самого треугольника
		// vY направляем на 0-ю вершину (как в вашем Special расчете)
		Vector3f vY = (sampleTri.data[0] - center).normalized();
		// Временный X для расчета нормали
		Vector3f vX_temp = (sampleTri.data[1] - sampleTri.data[2]).normalized();
		// Чистая нормаль
		Vector3f vZ = vX_temp.cross(vY).normalized();
		// Чистый X, перпендикулярный Y и Z
		Vector3f vX = vY.cross(vZ).normalized();

		// Переводим 3D точки в этот 2D базис (Z зануляется сам собой)
		auto toLocal = [&](const Vector3f& p) {
			Vector3f d = p - center;
			return Vector3f{ d.dot(vX), d.dot(vY), 0.0f };
			};

		Triangle local;
		local.data[0] = toLocal(sampleTri.data[0]);
		local.data[1] = toLocal(sampleTri.data[1]);
		local.data[2] = toLocal(sampleTri.data[2]);
		return local;
	}

	StoneGroup CreateStoneGroupData(uint64_t globalSeed, const LodLevel& planetLodLevel) {
		StoneGroup group;
		group.allInstances.resize(planetLodLevel.triangles.size());

		for (size_t tIdx = 0; tIdx < planetLodLevel.triangles.size(); ++tIdx) {
			const Triangle& tri = planetLodLevel.triangles[tIdx];
			std::mt19937 engine(static_cast<unsigned int>(globalSeed));

			for (int i = 0; i < StoneParams::STONES_PER_TRIANGLE; ++i) {
				StoneInstance instance;
				instance.seed = globalSeed;// + tIdx * 1000 + i; 
				instance.position = GetRandomPointOnTriangle(tri, engine);

				float SCALE_MIN = 0.75f;
				float SCALE_MAX = 2.5f;
				
				instance.scale = {
					getRandomFloat(engine, SCALE_MIN, SCALE_MAX),
					getRandomFloat(engine, SCALE_MIN, SCALE_MAX),
					getRandomFloat(engine, SCALE_MIN, SCALE_MAX)
				};

				/*
				if (tIdx == 0)
				{
					instance.scale =  instance.scale * 0.7f;
				}*/

				/*
				Vector4f qx = QuatFromRotateAroundX(getRandomFloat(engine, 0.0f, 360.0f));
				Vector4f qy = QuatFromRotateAroundY(getRandomFloat(engine, 0.0f, 360.0f));
				Vector4f qz = QuatFromRotateAroundZ(getRandomFloat(engine, 0.0f, 360.0f));
				instance.rotation = slerp(slerp(qx, qy, 0.5f), qz, 0.5f).normalized();
				*/
				instance.rotation = Vector4f(0.f, 0.f, 0.f, 1.f);
				group.allInstances[tIdx].push_back(instance);
			}
		}
		return group;
	}

	std::vector<VertexRenderStruct> StoneGroup::inflate(int count)
	{
		std::vector<VertexRenderStruct> result;
		result.resize(count);

		for (int tIdx = 0; tIdx < count; tIdx++)
		{
			result[tIdx] = inflateOne(tIdx, 1.0f);
		}

		return result;
	}

	VertexRenderStruct StoneGroup::inflateOne(int index, float scaleModifier)
	{
		static VertexDataStruct baseStone = CreateBaseConvexPolyhedron(1337);

		VertexRenderStruct result;


			for (const auto& inst : allInstances[index]) {
				Matrix3f rotMat = inst.rotation.toRotationMatrix();

				for (size_t j = 0; j < baseStone.PositionData.size(); ++j) {
					Vector3f p = baseStone.PositionData[j];
					Vector3f n = baseStone.NormalData[j];

					p(0) *= inst.scale(0) * scaleModifier;
					p(1) *= inst.scale(1) * scaleModifier;
					p(2) *= inst.scale(2) * scaleModifier;

					result.data.PositionData.push_back(rotMat * p + inst.position);
					result.data.NormalData.push_back(rotMat * n);
					result.data.TexCoordData.push_back(baseStone.TexCoordData[j]);

				}

				result.RefreshVBO();
			}
		

		return result;
	}

	VertexDataStruct StoneGroup::inflateOneDataOnly(int index, float scaleModifier)
	{
		static VertexDataStruct baseStone = CreateBaseConvexPolyhedron(1337);

		VertexDataStruct result;


		for (const auto& inst : allInstances[index]) {
			Matrix3f rotMat = inst.rotation.toRotationMatrix();

			for (size_t j = 0; j < baseStone.PositionData.size(); ++j) {
				Vector3f p = baseStone.PositionData[j];
				Vector3f n = baseStone.NormalData[j];

				p(0) *= inst.scale(0) * scaleModifier;
				p(1) *= inst.scale(1) * scaleModifier;
				p(2) *= inst.scale(2) * scaleModifier;

				result.PositionData.push_back(rotMat * p + inst.position);
				result.NormalData.push_back(rotMat * n);
				result.TexCoordData.push_back(baseStone.TexCoordData[j]);

			}
		}


		return result;
	}

} // namespace ZL
