#include "BoneAnimatedModel.h"
#include <regex>
#include <string>
#include <fstream>
#include <iostream>
#include <sstream>
#include <cstring>

namespace ZL
{

	int getIndexByValue(const std::string& name, const std::vector<std::string>& words)
	{
		for (int i = 0; i < words.size(); i++)
		{
			if (words[i] == name)
			{
				return i;
			}
		}
		

		std::cout << "Bone name not found: " << name << std::endl;
		throw std::runtime_error("Bone name not found: " + name);

		return -1;
	}


	void BoneSystem::LoadFromFile(const std::string& fileName, const std::string& ZIPFileName)
	{
		std::ifstream filestream;
		std::istringstream zipStream;
	
		if (!ZIPFileName.empty())
		{
			std::vector<char> fileData = readFileFromZIP(fileName, ZIPFileName);
			std::string fileContents(fileData.begin(), fileData.end());
			zipStream.str(fileContents);
		}
		else
		{
			filestream.open(fileName);
		}
	
		std::istream& f = (!ZIPFileName.empty()) ? static_cast<std::istream&>(zipStream) : static_cast<std::istream&>(filestream);

		//Skip first 5 lines
		std::string tempLine;
		for (int i = 0; i < 5; i++)
		{
			std::getline(f, tempLine);
		}
		std::getline(f, tempLine);

		static const std::regex pattern_count(R"(\d+)");
		static const std::regex pattern_float(R"([-]?\d+\.\d+)");
		static const std::regex pattern_int(R"([-]?\d+)");
		static const std::regex pattern_boneChildren(R"(\'([^\']+)\')");
		static const std::regex pattern_bone_weight(R"(\'([^\']+)\'.*?([-]?\d+\.\d+))");
		
		std::smatch match;

		int numberBones;

		if (std::regex_search(tempLine, match, pattern_count)) {
			std::string number_str = match.str();
			numberBones = std::stoi(number_str);
		}
		else {
			throw std::runtime_error("No number found in the input string.");
		}

		std::vector<Bone> bones;
		std::vector<std::string> boneNames;
		std::vector<std::string> boneParentNames;
		std::unordered_map<std::string, std::vector<std::string>> boneChildren;

		bones.resize(numberBones);
		boneNames.resize(numberBones);
		boneParentNames.resize(numberBones);

		for (int i = 0; i < numberBones; i++)
		{
			std::getline(f, tempLine);
			std::string boneName = tempLine.substr(6);

			boneNames[i] = boneName;

			std::getline(f, tempLine);

			std::vector<float> floatValues;

			auto b = tempLine.cbegin();
			auto e = tempLine.cend();
			while (std::regex_search(b, e, match, pattern_float)) {
				floatValues.push_back(std::stof(match.str()));
				b = match.suffix().first;
			}

			bones[i].boneStartWorld = Vector3f{ floatValues[0], floatValues[1], floatValues[2] };
			
			
			std::getline(f, tempLine); //skip tail

			std::getline(f, tempLine); //len

			if (std::regex_search(tempLine, match, pattern_float)) {
				std::string len_str = match.str();
				bones[i].boneLength = std::stof(len_str);
			}
			else {
				throw std::runtime_error("No number found in the input string.");
			}
			//---------- matrix begin

			std::getline(f, tempLine);

			b = tempLine.cbegin();
			e = tempLine.cend();
			floatValues.clear();
			while (std::regex_search(b, e, match, pattern_float)) {
				floatValues.push_back(std::stof(match.str()));
				b = match.suffix().first;
			}

			bones[i].boneMatrixWorld.data()[0] = floatValues[0];
			bones[i].boneMatrixWorld.data()[0 + 1 * 3] = floatValues[1];
			bones[i].boneMatrixWorld.data()[0 + 2 * 3] = floatValues[2];


			std::getline(f, tempLine);

			b = tempLine.cbegin();
			e = tempLine.cend();
			floatValues.clear();
			while (std::regex_search(b, e, match, pattern_float)) {
				floatValues.push_back(std::stof(match.str()));
				b = match.suffix().first;
			}

			bones[i].boneMatrixWorld.data()[1] = floatValues[0];
			bones[i].boneMatrixWorld.data()[1 + 1 * 3] = floatValues[1];
			bones[i].boneMatrixWorld.data()[1 + 2 * 3] = floatValues[2];


			std::getline(f, tempLine);

			b = tempLine.cbegin();
			e = tempLine.cend();
			floatValues.clear();
			while (std::regex_search(b, e, match, pattern_float)) {
				floatValues.push_back(std::stof(match.str()));
				b = match.suffix().first;
			}

			bones[i].boneMatrixWorld.data()[2] = floatValues[0];
			bones[i].boneMatrixWorld.data()[2 + 1 * 3] = floatValues[1];
			bones[i].boneMatrixWorld.data()[2 + 2 * 3] = floatValues[2];

			//----------- matrix end
			std::getline(f, tempLine); //parent

			if (tempLine == "  Parent: None")
			{
				bones[i].parent = -1;
			}
			else
			{
				std::string boneParent = tempLine.substr(10);
				boneParentNames[i] = boneParent;
			}

			std::getline(f, tempLine); //children

			b = tempLine.cbegin();
			e = tempLine.cend();
			while (std::regex_search(b, e, match, pattern_boneChildren)) {
				
				boneChildren[boneName].push_back(match.str(1));
				b = match.suffix().first; 
			}

		}

		std::cout << "bone names count : " << boneNames.size() << std::endl;

		//Now process all the bones:
		for (int i = 0; i < numberBones; i++)
		{
			std::string boneName = boneNames[i];
			std::string boneParent = boneParentNames[i];
			if (boneParent == "" || boneParent == "None")
			{
				bones[i].parent = -1;
			}
			else
			{
				/*std::cout << "-Processing bone: " << boneName << ", parent: " << boneParent << " " << "sizeof:" << boneParent.size() << " " << strlen("None") <<
					int(boneParent[0])<<","<<
					int(boneParent[1]) << ","<<
					int(boneParent[2]) << ","<<
					int(boneParent[3]) << ","<<
					int(boneParent[4]) << "," <<
					int(boneParent[5]) << ","
					<< std::endl;*/
				bones[i].parent = getIndexByValue(boneParent, boneNames);
			}

			for (int j = 0; j < boneChildren[boneName].size(); j++)
			{
				//std::cout << "Enumerating bones:" << j << " " << boneName << " " << boneChildren[boneName][j] << std::endl;

				bones[i].children.push_back(getIndexByValue(boneChildren[boneName][j], boneNames));
			}

			/*if (boneName == "Bone.020")
			{
				std::cout << i << std::endl;
			}*/
		}

		startBones = bones;
		currentBones = bones;

		///std::cout << "Hello!" << std::endl;
		
		std::getline(f, tempLine); //vertice count
		int numberVertices;

		if (std::regex_search(tempLine, match, pattern_count)) {
			std::string number_str = match.str();
			numberVertices = std::stoi(number_str);
		}
		else {
			throw std::runtime_error("No number found in the input string.");
		}

		std::vector<Vector3f> vertices;

		vertices.resize(numberVertices);
		for (int i = 0; i < numberVertices; i++)
		{
			std::getline(f, tempLine);

			std::vector<float> floatValues;

			auto b = tempLine.cbegin();
			auto e = tempLine.cend();
			while (std::regex_search(b, e, match, pattern_float)) {
				floatValues.push_back(std::stof(match.str()));
				b = match.suffix().first;
			}

			vertices[i] = Vector3f{floatValues[0], floatValues[1], floatValues[2]};
		}


		//==== process uv and normals begin

		//std::cout << "Hello x1" << std::endl;

		std::getline(f, tempLine); //===UV Coordinates:

		std::getline(f, tempLine); //triangle count
		int numberTriangles;

		if (std::regex_search(tempLine, match, pattern_count)) {
			std::string number_str = match.str();
			numberTriangles = std::stoi(number_str);
		}
		else {
			throw std::runtime_error("No number found in the input string.");
		}


		// Now process UVs
		std::vector<std::array<Vector2f, 3>> uvCoords;

		uvCoords.resize(numberTriangles);

		for (int i = 0; i < numberTriangles; i++)
		{
			std::getline(f, tempLine); //Face 0

			int uvCount;
			std::getline(f, tempLine);
			if (std::regex_search(tempLine, match, pattern_count)) {
				std::string number_str = match.str();
				uvCount = std::stoi(number_str);
			}
			else {
				throw std::runtime_error("No number found in the input string.");
			}

			if (uvCount != 3)
			{
				throw std::runtime_error("more than 3 uvs");
			}

			std::vector<float> floatValues;

			for (int j = 0; j < 3; j++)
			{
				std::getline(f, tempLine); //UV <Vector (-0.3661, -1.1665)>

				auto b = tempLine.cbegin();
				auto e = tempLine.cend();
				floatValues.clear();
				while (std::regex_search(b, e, match, pattern_float)) {
					floatValues.push_back(std::stof(match.str()));
					b = match.suffix().first;
				}

				if (floatValues.size() != 2)
				{
					throw std::runtime_error("more than 2 uvs---");
				}

				uvCoords[i][j] = Vector2f{ floatValues[0],floatValues[1] };
			}






		}

		//std::cout << "Hello eee" << std::endl;

		std::getline(f, tempLine); //===Normals:


		std::vector<Vector3f> normals;

		normals.resize(numberVertices);
		for (int i = 0; i < numberVertices; i++)
		{
			std::getline(f, tempLine);

			std::vector<float> floatValues;

			auto b = tempLine.cbegin();
			auto e = tempLine.cend();
			while (std::regex_search(b, e, match, pattern_float)) {
				floatValues.push_back(std::stof(match.str()));
				b = match.suffix().first;
			}

			normals[i] = Vector3f{ floatValues[0], floatValues[1], floatValues[2] };
		}

		//==== process uv and normals end

		std::getline(f, tempLine); //triangle count. 
		//numberTriangles; //Need to check if new value is the same as was read before

		if (std::regex_search(tempLine, match, pattern_count)) {
			std::string number_str = match.str();
			numberTriangles = std::stoi(number_str);
		}
		else {
			throw std::runtime_error("No number found in the input string.");
		}

		std::vector<std::array<int, 3>> triangles;

		triangles.resize(numberTriangles);
		for (int i = 0; i < numberTriangles; i++)
		{
			std::getline(f, tempLine);

			std::vector<int> intValues;

			auto b = tempLine.cbegin();
			auto e = tempLine.cend();
			while (std::regex_search(b, e, match, pattern_int)) {
				intValues.push_back(std::stoi(match.str()));
				b = match.suffix().first;
			}

			triangles[i] = { intValues[0], intValues[1], intValues[2] };
		}

		std::getline(f, tempLine);//=== Vertex Weights ===
		std::vector<std::array<BoneWeight, MAX_BONE_COUNT>> localVerticesBoneWeight;
		localVerticesBoneWeight.resize(numberVertices);

		for (int i = 0; i < numberVertices; i++)
		{
			std::getline(f, tempLine); //skip Vertex 0:
			std::getline(f, tempLine); //vertex group count
			int boneCount;

			if (std::regex_search(tempLine, match, pattern_count)) {
				std::string number_str = match.str();
				boneCount = std::stoi(number_str);
			}
			else {
				throw std::runtime_error("No number found in the input string.");
			}

			if (boneCount > MAX_BONE_COUNT)
			{
				throw std::runtime_error("more than 5 bones");
			}

			float sumWeights = 0;

			for (int j = 0; j < boneCount; j++)
			{
				std::getline(f, tempLine); //Group: 'Bone', Weight: 0.9929084181785583
				if (std::regex_search(tempLine, match, pattern_bone_weight)) {
					std::string word = match.str(1); 
					double weight = std::stod(match.str(2)); 
					//std::cout << "Bone name: " << word << ", weight: " << weight << std::endl;
					int boneNumber = getIndexByValue(word, boneNames);
					localVerticesBoneWeight[i][j].boneIndex = boneNumber;
					localVerticesBoneWeight[i][j].weight = weight;
					sumWeights += weight;
				}
				else {
					throw std::runtime_error("No match found in the input string.");
				}
			}

			//Normalize weights:
			for (int j = 0; j < boneCount; j++)
			{
				localVerticesBoneWeight[i][j].weight = localVerticesBoneWeight[i][j].weight / sumWeights;
			}

		}

		std::getline(f, tempLine);//=== Animation Keyframes ===
		std::getline(f, tempLine);//=== Bone Transforms per Keyframe ===



		std::getline(f, tempLine);
		int numberKeyFrames;

		if (std::regex_search(tempLine, match, pattern_count)) {
			std::string number_str = match.str();
			numberKeyFrames = std::stoi(number_str);
		}
		else {
			throw std::runtime_error("No number found in the input string.");
		}

		animations.resize(1);

		animations[0].keyFrames.resize(numberKeyFrames);

		for (int i = 0; i < numberKeyFrames; i++)
		{
			std::getline(f, tempLine);
			int numberFrame;

			if (std::regex_search(tempLine, match, pattern_count)) {
				std::string number_str = match.str();
				numberFrame = std::stoi(number_str);
			}
			else {
				throw std::runtime_error("No number found in the input string.");
			}

			/*if (i == 0)
			{
				startingFrame = numberFrame;
			}*/

			animations[0].keyFrames[i].frame = numberFrame;

			animations[0].keyFrames[i].bones.resize(numberBones);

			for (int j = 0; j < numberBones; j++)
			{
				std::getline(f, tempLine);
				std::string boneName = tempLine.substr(8);
				//std::cout << "Processing keyframe " << i << ", bone: " << j  << " " << boneName << std::endl;
				int boneNumber = getIndexByValue(boneName, boneNames);
				animations[0].keyFrames[i].bones[boneNumber] = startBones[boneNumber];

				std::getline(f, tempLine); //  Location: <Vector (0.0000, 0.0000, -0.0091)>

				std::vector<float> floatValues;

				auto b = tempLine.cbegin();
				auto e = tempLine.cend();
				while (std::regex_search(b, e, match, pattern_float)) {
					floatValues.push_back(std::stof(match.str()));
					b = match.suffix().first;
				}

				animations[0].keyFrames[i].bones[boneNumber].boneStartWorld = Vector3f{ floatValues[0], floatValues[1], floatValues[2] };
			
				std::getline(f, tempLine); // Rotation
				std::getline(f, tempLine); // Matrix

				//=============== Matrix begin ==================
				std::getline(f, tempLine);


				b = tempLine.cbegin();
				e = tempLine.cend();
				floatValues.clear();
				while (std::regex_search(b, e, match, pattern_float)) {
					floatValues.push_back(std::stof(match.str()));
					b = match.suffix().first;
				}

				animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[0] = floatValues[0];
				animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[0 + 1 * 4] = floatValues[1];
				animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[0 + 2 * 4] = floatValues[2];
				animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[0 + 3 * 4] = floatValues[3];


				std::getline(f, tempLine);
				b = tempLine.cbegin();
				e = tempLine.cend();
				floatValues.clear();
				while (std::regex_search(b, e, match, pattern_float)) {
					floatValues.push_back(std::stof(match.str()));
					b = match.suffix().first;
				}

				animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[1] = floatValues[0];
				animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[1 + 1 * 4] = floatValues[1];
				animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[1 + 2 * 4] = floatValues[2];
				animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[1 + 3 * 4] = floatValues[3];

				std::getline(f, tempLine);
				b = tempLine.cbegin();
				e = tempLine.cend();
				floatValues.clear();
				while (std::regex_search(b, e, match, pattern_float)) {
					floatValues.push_back(std::stof(match.str()));
					b = match.suffix().first;
				}

				animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[2] = floatValues[0];
				animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[2 + 1 * 4] = floatValues[1];
				animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[2 + 2 * 4] = floatValues[2];
				animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[2 + 3 * 4] = floatValues[3];


				std::getline(f, tempLine);
				b = tempLine.cbegin();
				e = tempLine.cend();
				floatValues.clear();
				while (std::regex_search(b, e, match, pattern_float)) {
					floatValues.push_back(std::stof(match.str()));
					b = match.suffix().first;
				}

				animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[3] = floatValues[0];
				animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[3 + 1 * 4] = floatValues[1];
				animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[3 + 2 * 4] = floatValues[2];
				animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[3 + 3 * 4] = floatValues[3];

				//std::getline(f, tempLine);// ignore last matrix line

				//=============== Matrix end ==================
			
			}

		}

		// Now let's process bone weights and vertices

		for (int i = 0; i < numberTriangles; i++)
		{
			
			mesh.PositionData.push_back(vertices[triangles[i][0]]);
			mesh.PositionData.push_back(vertices[triangles[i][1]]);
			mesh.PositionData.push_back(vertices[triangles[i][2]]);
			
			verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][0]]);
			verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][1]]);
			verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][2]]);

			mesh.TexCoordData.push_back(uvCoords[i][0]);
			mesh.TexCoordData.push_back(uvCoords[i][1]);
			mesh.TexCoordData.push_back(uvCoords[i][2]);
		}

		startMesh = mesh;
	}

	void BoneSystem::LoadFromBinaryFile(const std::string& fileName, const std::string& ZIPFileName)
	{
		std::vector<char> fileData;

		if (!ZIPFileName.empty())
		{
			fileData = readFileFromZIP(fileName, ZIPFileName);
		}
		else
		{
			std::ifstream f(fileName, std::ios::binary | std::ios::ate);
			if (!f.is_open()) throw std::runtime_error("Failed to open binary file: " + fileName);
			std::streamsize fileSize = f.tellg();
			f.seekg(0);
			fileData.resize(static_cast<size_t>(fileSize));
			f.read(fileData.data(), fileSize);
		}

		const char* ptr = fileData.data();

		auto readRaw = [&](void* dst, size_t n) {
			std::memcpy(dst, ptr, n);
			ptr += n;
		};
		auto readUint32 = [&]() -> uint32_t { uint32_t v; readRaw(&v, 4); return v; };
		auto readInt32  = [&]() -> int32_t  { int32_t  v; readRaw(&v, 4); return v; };
		auto readFloat  = [&]() -> float    { float    v; readRaw(&v, 4); return v; };
		auto readVec3   = [&]() -> Vector3f { return Vector3f{readFloat(), readFloat(), readFloat()}; };
		auto readVec2   = [&]() -> Vector2f { return Vector2f{readFloat(), readFloat()}; };

		// Header
		char magic[4];
		readRaw(magic, 4);
		if (std::memcmp(magic, "BSAF", 4) != 0)
			throw std::runtime_error("Invalid binary animation file (bad magic)");
		uint32_t version = readUint32();
		if (version != 1)
			throw std::runtime_error("Unsupported binary animation file version");

		// ---- Bones ----
		uint32_t numBones = readUint32();
		std::vector<Bone> bones(numBones);

		for (uint32_t i = 0; i < numBones; i++)
		{
			bones[i].boneStartWorld = readVec3();
			bones[i].boneLength = readFloat();

			// 3x3 matrix (row-major in file).
			// Stored with stride-3 into Matrix4f to match the text loader.
			float m[9];
			for (int j = 0; j < 9; j++) m[j] = readFloat();

			bones[i].boneMatrixWorld = Matrix4f::Zero();
			for (int r = 0; r < 3; r++)
				for (int c = 0; c < 3; c++)
					bones[i].boneMatrixWorld.data()[r + c * 3] = m[r * 3 + c];

			bones[i].parent = readInt32();

			uint32_t numChildren = readUint32();
			bones[i].children.resize(numChildren);
			for (uint32_t j = 0; j < numChildren; j++)
				bones[i].children[j] = readInt32();
		}

		startBones = bones;
		currentBones = bones;

		// ---- Vertices ----
		uint32_t numVertices = readUint32();
		std::vector<Vector3f> vertices(numVertices);
		for (uint32_t i = 0; i < numVertices; i++)
			vertices[i] = readVec3();

		// ---- UV Coordinates ----
		uint32_t numFaces = readUint32();
		std::vector<std::array<Vector2f, 3>> uvCoords(numFaces);
		for (uint32_t i = 0; i < numFaces; i++)
			for (int j = 0; j < 3; j++)
				uvCoords[i][j] = readVec2();

		// ---- Normals (read but not currently used by mesh) ----
		std::vector<Vector3f> normals(numVertices);
		for (uint32_t i = 0; i < numVertices; i++)
			normals[i] = readVec3();

		// ---- Triangles ----
		uint32_t numTriangles = readUint32();
		std::vector<std::array<int, 3>> triangles(numTriangles);
		for (uint32_t i = 0; i < numTriangles; i++)
			triangles[i] = { readInt32(), readInt32(), readInt32() };

		// ---- Vertex Weights ----
		std::vector<std::array<BoneWeight, MAX_BONE_COUNT>> localVerticesBoneWeight(numVertices);
		for (uint32_t i = 0; i < numVertices; i++)
		{
			uint32_t numGroups = readUint32();
			float sumWeights = 0;
			for (uint32_t j = 0; j < numGroups; j++)
			{
				int boneIdx = readInt32();
				float weight = readFloat();
				if (j < MAX_BONE_COUNT)
				{
					localVerticesBoneWeight[i][j].boneIndex = boneIdx;
					localVerticesBoneWeight[i][j].weight = weight;
					sumWeights += weight;
				}
			}
			// Normalize weights
			uint32_t cap = (numGroups < MAX_BONE_COUNT) ? numGroups : MAX_BONE_COUNT;
			for (uint32_t j = 0; j < cap; j++)
				localVerticesBoneWeight[i][j].weight /= sumWeights;
		}

		// ---- Animation Keyframes ----
		uint32_t numKeyframes = readUint32();
		animations.resize(1);
		animations[0].keyFrames.resize(numKeyframes);

		for (uint32_t i = 0; i < numKeyframes; i++)
		{
			animations[0].keyFrames[i].frame = readInt32();
			animations[0].keyFrames[i].bones.resize(numBones);

			for (uint32_t j = 0; j < numBones; j++)
			{
				animations[0].keyFrames[i].bones[j] = startBones[j];
				animations[0].keyFrames[i].bones[j].boneStartWorld = readVec3();

				// 4x4 matrix (row-major in file, stored with stride-4 into Matrix4f)
				float m[16];
				for (int k = 0; k < 16; k++) m[k] = readFloat();

				for (int r = 0; r < 4; r++)
					for (int c = 0; c < 4; c++)
						animations[0].keyFrames[i].bones[j].boneMatrixWorld.data()[r + c * 4] = m[r * 4 + c];
			}
		}

		// ---- Build per-triangle mesh (same expansion as text loader) ----
		for (uint32_t i = 0; i < numTriangles; i++)
		{
			mesh.PositionData.push_back(vertices[triangles[i][0]]);
			mesh.PositionData.push_back(vertices[triangles[i][1]]);
			mesh.PositionData.push_back(vertices[triangles[i][2]]);

			verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][0]]);
			verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][1]]);
			verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][2]]);

			mesh.TexCoordData.push_back(uvCoords[i][0]);
			mesh.TexCoordData.push_back(uvCoords[i][1]);
			mesh.TexCoordData.push_back(uvCoords[i][2]);
		}

		startMesh = mesh;
	}

	void BoneSystem::PrepareGpuSkinningData()
	{
		size_t vertexCount = verticesBoneWeight.size();
		gpuBoneData.boneIndices0.resize(vertexCount);
		gpuBoneData.boneIndices1.resize(vertexCount);
		gpuBoneData.boneWeights0.resize(vertexCount);
		gpuBoneData.boneWeights1.resize(vertexCount);

		for (size_t i = 0; i < vertexCount; i++)
		{
			gpuBoneData.boneIndices0[i] = Vector4f(
				static_cast<float>(max(0, verticesBoneWeight[i][0].boneIndex)),
				static_cast<float>(max(0, verticesBoneWeight[i][1].boneIndex)),
				static_cast<float>(max(0, verticesBoneWeight[i][2].boneIndex)),
				static_cast<float>(max(0, verticesBoneWeight[i][3].boneIndex))
			);
			gpuBoneData.boneIndices1[i] = Vector2f(
				static_cast<float>(max(0, verticesBoneWeight[i][4].boneIndex)),
				static_cast<float>(max(0, verticesBoneWeight[i][5].boneIndex))
			);
			gpuBoneData.boneWeights0[i] = Vector4f(
				verticesBoneWeight[i][0].weight,
				verticesBoneWeight[i][1].weight,
				verticesBoneWeight[i][2].weight,
				verticesBoneWeight[i][3].weight
			);
			gpuBoneData.boneWeights1[i] = Vector2f(
				verticesBoneWeight[i][4].weight,
				verticesBoneWeight[i][5].weight
			);
		}

		gpuBoneData.prepared = true;

		if (startBones.size() > MAX_GPU_BONES)
		{
			std::cout << "Warning: model has " << startBones.size()
				<< " bones, exceeding GPU skinning limit of " << MAX_GPU_BONES << std::endl;
		}
	}

	void BoneSystem::ComputeSkinningMatrices(int frame, std::vector<Matrix4f>& outMatrices) const
	{
		int startingKeyFrame = -1;
		for (size_t i = 0; i < animations[0].keyFrames.size() - 1; i++)
		{
			int oldFrame = animations[0].keyFrames[i].frame;
			int nextFrame = animations[0].keyFrames[i + 1].frame;
			if (frame >= oldFrame && frame < nextFrame)
			{
				startingKeyFrame = static_cast<int>(i);
				break;
			}
		}

		if (startingKeyFrame == -1)
		{
			outMatrices.resize(startBones.size());
			for (auto& m : outMatrices) m = Matrix4f::Identity();
			return;
		}

		int modifiedFrameNumber = frame - animations[0].keyFrames[startingKeyFrame].frame;
		int diffFrames = animations[0].keyFrames[startingKeyFrame + 1].frame - animations[0].keyFrames[startingKeyFrame].frame;
		float t = (modifiedFrameNumber + 0.f) / diffFrames;

		const std::vector<Bone>& oneFrameBones = animations[0].keyFrames[startingKeyFrame].bones;
		const std::vector<Bone>& nextFrameBones = animations[0].keyFrames[startingKeyFrame + 1].bones;

		outMatrices.resize(startBones.size());

		for (size_t i = 0; i < startBones.size(); i++)
		{
			Vector3f interpPos;
			interpPos(0) = oneFrameBones[i].boneStartWorld(0) + t * (nextFrameBones[i].boneStartWorld(0) - oneFrameBones[i].boneStartWorld(0));
			interpPos(1) = oneFrameBones[i].boneStartWorld(1) + t * (nextFrameBones[i].boneStartWorld(1) - oneFrameBones[i].boneStartWorld(1));
			interpPos(2) = oneFrameBones[i].boneStartWorld(2) + t * (nextFrameBones[i].boneStartWorld(2) - oneFrameBones[i].boneStartWorld(2));

			Matrix3f oneFrameBonesMatrix = oneFrameBones[i].boneMatrixWorld.block<3, 3>(0, 0);
			Matrix3f nextFrameBonesMatrix = nextFrameBones[i].boneMatrixWorld.block<3, 3>(0, 0);

			Eigen::Quaternionf q1 = Eigen::Quaternionf(oneFrameBonesMatrix).normalized();
			Eigen::Quaternionf q2 = Eigen::Quaternionf(nextFrameBonesMatrix).normalized();
			Eigen::Quaternionf result = q1.slerp(t, q2);

			Matrix3f boneMatrixWorld3 = result.toRotationMatrix();

			Matrix4f currentBoneMatrixWorld4 = Eigen::Matrix4f::Identity();
			currentBoneMatrixWorld4.block<3, 3>(0, 0) = boneMatrixWorld3;
			currentBoneMatrixWorld4.block<3, 1>(0, 3) = interpPos;

			Matrix4f startBoneMatrixWorld4 = animations[0].keyFrames[0].bones[i].boneMatrixWorld;
			Matrix4f invertedStartBoneMatrixWorld4 = startBoneMatrixWorld4.inverse();

			outMatrices[i] = currentBoneMatrixWorld4 * invertedStartBoneMatrixWorld4;
		}
	}

	void BoneSystem::Interpolate(int frame)
	{
		int startingFrame = -1;
		for (int i = 0; i < animations[0].keyFrames.size() - 1; i++)
		{
			int oldFrame = animations[0].keyFrames[i].frame;
			int nextFrame = animations[0].keyFrames[i + 1].frame;
			if (frame >= oldFrame && frame < nextFrame)
			{
				startingFrame = i;
				break;
			}
		}

		if (startingFrame == -1)
		{
			std::cout << "Exception here: frame number is out of range of keyframes. Frame: " << frame << std::endl;
			throw std::runtime_error("Exception here");
		}

		int modifiedFrameNumber = frame - animations[0].keyFrames[startingFrame].frame;

		int diffFrames = animations[0].keyFrames[startingFrame + 1].frame - animations[0].keyFrames[startingFrame].frame;

		float t = (modifiedFrameNumber + 0.f) / diffFrames;

		std::vector<Bone>& oneFrameBones = animations[0].keyFrames[startingFrame].bones;
		std::vector<Bone>& nextFrameBones = animations[0].keyFrames[startingFrame+1].bones;

		std::vector<Matrix4f> skinningMatrixForEachBone;
		skinningMatrixForEachBone.resize(currentBones.size());

		
		for (int i = 0; i < currentBones.size(); i++)
		{
			currentBones[i].boneStartWorld(0) = oneFrameBones[i].boneStartWorld(0) + t * (nextFrameBones[i].boneStartWorld(0) - oneFrameBones[i].boneStartWorld(0));
			currentBones[i].boneStartWorld(1) = oneFrameBones[i].boneStartWorld(1) + t * (nextFrameBones[i].boneStartWorld(1) - oneFrameBones[i].boneStartWorld(1));
			currentBones[i].boneStartWorld(2) = oneFrameBones[i].boneStartWorld(2) + t * (nextFrameBones[i].boneStartWorld(2) - oneFrameBones[i].boneStartWorld(2));
			
			Matrix3f oneFrameBonesMatrix;

			oneFrameBonesMatrix = oneFrameBones[i].boneMatrixWorld.block<3, 3>(0, 0);

			Matrix3f nextFrameBonesMatrix;

			nextFrameBonesMatrix = nextFrameBones[i].boneMatrixWorld.block<3, 3>(0, 0);
			
			Eigen::Quaternionf q1 = Eigen::Quaternionf(oneFrameBonesMatrix).normalized();
			Eigen::Quaternionf q2 = Eigen::Quaternionf(nextFrameBonesMatrix).normalized();
			Eigen::Quaternionf q1_norm = q1.normalized();
			Eigen::Quaternionf q2_norm = q2.normalized();

			Eigen::Quaternionf result = q1_norm.slerp(t, q2_norm);

			Matrix3f boneMatrixWorld3 = result.toRotationMatrix();

			currentBones[i].boneMatrixWorld = Eigen::Matrix4f::Identity();

			// Копируем 3x3 матрицу в верхний левый угол
			currentBones[i].boneMatrixWorld.block<3, 3>(0, 0) = boneMatrixWorld3;

			// Копируем позицию в последний столбец (первые 3 элемента)
			currentBones[i].boneMatrixWorld.block<3, 1>(0, 3) = currentBones[i].boneStartWorld;

	
			Matrix4f currentBoneMatrixWorld4 = currentBones[i].boneMatrixWorld;
			Matrix4f startBoneMatrixWorld4 = animations[0].keyFrames[0].bones[i].boneMatrixWorld;

			Matrix4f inverstedStartBoneMatrixWorld4 = startBoneMatrixWorld4.inverse();

			skinningMatrixForEachBone[i] = currentBoneMatrixWorld4 * inverstedStartBoneMatrixWorld4;
			
		}

		/*std::cout << "m=" << skinningMatrixForEachBone[5] << std::endl;

		std::cout << "Start Mesh data " << std::endl;
		std::cout << startMesh.PositionData[0] << std::endl;
		std::cout << startMesh.PositionData[10] << std::endl;
		std::cout << startMesh.PositionData[100] << std::endl;
		*/
		for (int i = 0; i < mesh.PositionData.size(); i++)
		{
			Vector4f originalPos = { 
				startMesh.PositionData[i](0), 
				startMesh.PositionData[i](1), 
				startMesh.PositionData[i](2), 1.0};

			Vector4f finalPos = Vector4f{0.f, 0.f, 0.f, 0.f};

			bool vMoved = false;

			for (int j = 0; j < MAX_BONE_COUNT; j++)
			{
				if (verticesBoneWeight[i][j].weight != 0)
				{
					if (verticesBoneWeight[i][j].boneIndex == -1)
					{
						std::cout << "Exception here: bone index is -1 but weight is > 0" << std::endl;
						throw std::runtime_error("Bones loaded incorrectly - bone index is -1 but weight is > 0");
					}
					vMoved = true;
					finalPos = finalPos + (skinningMatrixForEachBone[verticesBoneWeight[i][j].boneIndex] * originalPos) * verticesBoneWeight[i][j].weight;
				
					/*if (i == 100)
					{
						std::cout << "bone index=" << verticesBoneWeight[i][j].boneIndex << std::endl;
						std::cout << "weight=" << verticesBoneWeight[i][j].weight << std::endl;
						std::cout << "skinning matrix=" << skinningMatrixForEachBone[verticesBoneWeight[i][j].boneIndex] << std::endl;
						std::cout << "original pos=" << originalPos.transpose() << std::endl;
						std::cout << "final pos=" << finalPos.transpose() << std::endl;
						std::cout << "-----------------" << std::endl;
					}*/
				}
			}

			/*if (i == 100)
			{
				std::cout << originalPos << std::endl;
				std::cout << finalPos << std::endl;
			}*/

			/*if (abs(finalPos(0) - originalPos(0)) > 1 || abs(finalPos(1) - originalPos(1)) > 1 || abs(finalPos(2) - originalPos(2)) > 1)
			{

			}*/

			if (!vMoved)
			{
				finalPos = originalPos;
			}

			mesh.PositionData[i](0) = finalPos(0);
			mesh.PositionData[i](1) = finalPos(1);
			mesh.PositionData[i](2) = finalPos(2);
		}

	}


}