#pragma once
#include "render/Renderer.h"
#include <unordered_map>


namespace ZL
{
	constexpr int MAX_BONE_COUNT = 6;
	constexpr int MAX_GPU_BONES = 64;
	struct Bone
	{
		Eigen::Vector3f boneStartWorld;
		float boneLength;
		Eigen::Matrix4f boneMatrixWorld;

		int parent;
		std::vector<int> children;
	};

	struct BoneWeight
	{
		int boneIndex = -1;
		float weight = 0;
	};

	struct AnimationKeyFrame
	{
		int frame;
		std::vector<Bone> bones;
	};

	struct Animation
	{
		std::vector<AnimationKeyFrame> keyFrames;
	};

	struct GpuBoneData {
		std::vector<Vector4f> boneIndices0;  // bone indices 0-3 per vertex (as float)
		std::vector<Vector2f> boneIndices1;  // bone indices 4-5 per vertex
		std::vector<Vector4f> boneWeights0;  // bone weights 0-3 per vertex
		std::vector<Vector2f> boneWeights1;  // bone weights 4-5 per vertex
		bool prepared = false;
		void PrepareGpuSkinningData(const std::vector<std::array<BoneWeight, MAX_BONE_COUNT>>& verticesBoneWeight);
	};

	struct MeshBoneData
	{
		VertexDataStruct mesh;
		VertexDataStruct startMesh;
		std::vector<std::array<BoneWeight, MAX_BONE_COUNT>> verticesBoneWeight;
	};

	struct BoneSystemNew
	{
		std::unordered_map<std::string, MeshBoneData> meshes;
		std::vector<std::string> meshNamesOrdered;

		Matrix4f armatureMatrix;

		std::vector<Bone> startBones;
		std::vector<Bone> currentBones;
		std::vector<std::string> boneNames;

		std::vector<Animation> animations;
		int startingFrame = 0;

		void LoadFromFile(const std::string& fileName, const std::string& ZIPFileName = "");
		void LoadFromBinaryFile(const std::string& fileName, const std::string& ZIPFileName = "");

		void Interpolate(int frame);

		int findBoneIndex(const std::string& name) const;
	};

	struct MeshGpuSkinningData
	{
		GpuBoneData gpuBoneData;

		VertexRenderStruct bindPoseMutable;
		std::shared_ptr<VBOHolder> boneIndices0VBO;
		std::shared_ptr<VBOHolder> boneIndices1VBO;
		std::shared_ptr<VBOHolder> boneWeights0VBO;
		std::shared_ptr<VBOHolder> boneWeights1VBO;
		bool gpuSkinningPrepared = false;

		void prepareGpuSkinningVBOs(MeshBoneData& meshData);
		void RenderVBO(Renderer& renderer);
	};

	struct GpuSkinningShaderDataNew
	{
		std::unordered_map<std::string, MeshGpuSkinningData> perMesh;
		std::vector<Eigen::Matrix4f> skinningMatrices;

		void prepareGpuSkinningVBOs(BoneSystemNew& model);
		void RenderVBO(Renderer& renderer, const std::vector<std::string>& meshNamesOrdered);
		void ComputeSkinningMatrices(const std::vector<Bone>& startBones, const std::vector<AnimationKeyFrame>& keyFrames, int frame);
	};

	struct BoneAnimationDataNew
	{
		BoneSystemNew model;
		float currentFrame = 0.f;
		int lastFrame = -1;
		int totalFrames = 1;

		GpuSkinningShaderDataNew gpuSkinningShaderData;
	};
};
