#include "zlog.h"
#include "assimp_loader.h"
#include "asset/file_manager.h"
#include "asset/resource_manager.h"
#include "assimp/Importer.hpp"
#include "assimp/scene.h"
#include "assimp/postprocess.h"
namespace engineapi {
	void AssimpLoader::Init()
	{
		ResourceManager::GetSingleton().RegisterLoader<AssimpLoader>(".obj");
        ResourceManager::GetSingleton().RegisterLoader<AssimpLoader>(".ply");
	}
	ResourceBundle AssimpLoader::LoadFile(PackagePath handle, const MetaBundle& meta)
	{
        auto m = meta.FetchMeta<Model>();
        auto asset = m ? ResourceManager::GetSingleton().LoaderEmplaceResource<Model>(m->guid)
            : ResourceManager::GetSingleton().LoaderEmplaceResource<Model>();
        uint32_t flag = FileManager::FindPathFlag(handle);
        if (flag & FileFlag::File_Http) {
            return {};
        }
		LoadModel(handle, *asset, meta);
        ResourceBundle bundle{ asset };
        for (auto& mesh : asset->GetMeshs()) {
            bundle.Add(mesh);
        }
        return bundle;
	}
	void AssimpLoader::LoadModel(PackagePath handle, Model& model, const MetaBundle& meta)
	{
		// 用ASSIMP加载模型文件
		Assimp::Importer importer;
		const aiScene* scene = importer.ReadFile(handle.RealPath(), aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace
			| aiProcess_FixInfacingNormals | aiProcess_FlipWindingOrder | aiProcess_LimitBoneWeights);

		// 检查异常
		if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
		{
			zlog::error("ASSIMP: {}", importer.GetErrorString());
            return;
		}
		ProcessNode(model, meta, scene->mRootNode, scene);
	}
	void AssimpLoader::ProcessNode(Model& model, const MetaBundle& meta, const aiNode* pNode, const aiScene* pScene)
	{
		// 处理Mesh数据
		auto& meshs = model.GetMeshs();
		for (unsigned int i = 0; i < pNode->mNumMeshes; i++)
		{
			// aiNode仅包含索引来获取aiScene中的实际对象
			// aiScene包含所有数据，aiNode只是为了让数据组织起来(比如记录节点之间的关系)
			aiMesh* mesh = pScene->mMeshes[pNode->mMeshes[i]];
            auto m = meta.FetchMeta<Mesh>(mesh->mName.C_Str());
			meshs.push_back(ProcessMesh(m, mesh));
		}

		// 加载骨骼数据
		//pBoneNode->name = pNode->mName.C_Str();
		//pBoneNode->transform = aiMatrix4x4ToMatrix4(pNode->mTransformation);

		// 递归处理子节点
		for (unsigned int i = 0; i < pNode->mNumChildren; i++)
		{
			//pBoneNode->children.push_back(new BoneNode());
			ProcessNode(model, meta, pNode->mChildren[i], pScene);
		}
	}
    RscHandle<Mesh> AssimpLoader::ProcessMesh(const SerializedMeta* m, const aiMesh* mesh)
	{
        // data to fill
        vector<BoneVertex> vertices;
        vector<uint32_t> indices;
        // Walk through each of the mesh's vertices
        for (unsigned int i = 0; i < mesh->mNumVertices; i++)
        {
            BoneVertex vertex;
            Vector3 vector; // we declare a placeholder vector since assimp uses its own vector class that doesn't directly convert to Vector3 class so we transfer the data to this placeholder Vector3 first.

            // positions
            vector.x = mesh->mVertices[i].x;
            vector.y = mesh->mVertices[i].y;
            vector.z = mesh->mVertices[i].z;
            vertex.Position = vector;

            // normals
            vector.x = mesh->mNormals[i].x;
            vector.y = mesh->mNormals[i].y;
            vector.z = mesh->mNormals[i].z;
            vertex.Normal = vector;

            // texture coordinates
            if (mesh->mTextureCoords[0]) // does the mesh contain texture coordinates?
            {
                Vector2 vec;
                // a vertex can contain up to 8 different texture coordinates. We thus make the assumption that we won't 
                // use models where a vertex can have multiple texture coordinates so we always take the first set (0).
                vec.x = mesh->mTextureCoords[0][i].x;
                vec.y = mesh->mTextureCoords[0][i].y;
                vertex.TexCoords = vec;
            }
            else
            {
                vertex.TexCoords = Vector2(0.0f, 0.0f);
            }

            // tangent and bitangent
            if (mesh->HasTangentsAndBitangents())
            {
                vector.x = mesh->mTangents[i].x;
                vector.y = mesh->mTangents[i].y;
                vector.z = mesh->mTangents[i].z;
                vertex.Tangent = vector;
            }
            else
            {
                vertex.Tangent = Vector3(0.0f, 0.0f, 0.0f);
            }
            vertices.push_back(vertex);
        }
        // now wak through each of the mesh's faces (a face is a mesh its triangle) and retrieve the corresponding vertex indices.
        for (unsigned int i = 0; i < mesh->mNumFaces; i++)
        {
            const aiFace& face = mesh->mFaces[i];
            // retrieve all indices of the face and store them in the indices vector
            for (unsigned int j = 0; j < face.mNumIndices; j++)
            {
                indices.push_back(face.mIndices[j]);
            }
        }
        auto asset = m ? ResourceManager::GetSingleton().LoaderEmplaceResource<Mesh>(m->guid, vertices, indices)
            : ResourceManager::GetSingleton().LoaderEmplaceResource<Mesh>(vertices, indices);
        if (m) {
            YAML::Text_Unserialize(m->t_meta, asset->Meta());
        }
        asset->Name(mesh->mName.C_Str());
        return asset;
	}
}
