// ReSharper disable CppDeclaratorNeverUsed
// ReSharper disable CppDFAUnreachableFunctionCall

#include "../include/zassimp/importer.h"
#include <assimp/scene.h>
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include <Windows.h>
#include <iostream>
#include <span>
#include <utility>

namespace {


std::string GetAiMatrixPrint(const aiMatrix4x4 &am) {
    std::stringstream ss;
    ss << am.a1 << " " << am.a2 << " " << am.a3 << " " << am.a4 << '\n' <<
            am.b1 << " " << am.b2 << " " << am.b3 << " " << am.b4 << '\n' <<
            am.c1 << " " << am.c2 << " " << am.c3 << " " << am.c4 << '\n' <<
            am.d1 << " " << am.d2 << " " << am.d3 << " " << am.d4 << std::endl;
    return ss.str();
}
void PrintAiBoneInfo(const aiBone *bone) {
    std::cout << "    Print Bone Info" << std::endl;
    std::cout << "Name: " << bone->mName.C_Str() <<

            // mArmature 是 “bone armature node”，用于 skeleton conversion，
            //  并且必须启用 aiProcess_PopulateArmatureData 才会填充；
            //  默认构造时它就是 nullptr
            // "\nArmatureName: " << bone->mArmature->mName.C_Str() <<

            // 这个东西是 DCC 场景图/transform hierarchy 的一部分。
            // "\nNode->Name: " << bone->mNode->mName.C_Str() <<

            "\nNumWeights: " << bone->mNumWeights <<
            "\nWeights[0].VertexID: " << bone->mWeights->mVertexId <<
            "\nWeights[0].Weight: " << bone->mWeights->mWeight <<
            std::endl;
}
void PrintAiMeshInfo(const aiMesh *am) {
    std::cout << "======== Print AiMesh Info ========" << std::endl;
    std::cout << "Name: " << am->mName.C_Str() <<
            "\nHasBones: " << std::boolalpha << am->HasBones() <<
            "\nNumBones: " << am->mNumBones <<
            "\nHasFaces: " << std::boolalpha << am->HasFaces() <<
            "\nNumFaces: " << am->mNumFaces <<
            "\nMinAABB:" << am->mAABB.mMin.x << am->mAABB.mMin.y << am->mAABB.mMin.z <<
            "\nMaxAABB: " << am->mAABB.mMax.x << am->mAABB.mMax.y << am->mAABB.mMax.z <<
            "\nHasNormals: " << std::boolalpha << am->HasNormals() <<
            "\nHasPositions: " << std::boolalpha << am->HasPositions() <<
            "\nHasVertexColors[0]: " << std::boolalpha << am->HasVertexColors(0) <<
            "\nHasVertexColors[1]: " << std::boolalpha << am->HasVertexColors(1) <<
            "\nGetNumColorChannels: " << am->GetNumColorChannels() <<
            "\nMaterialIndex: " << am->mMaterialIndex <<
            "\nNumAnimMeshes: " << am->mNumAnimMeshes <<
            std::endl;
    for (size_t i = 0; i < am->mNumBones; ++i) {
        PrintAiBoneInfo(am->mBones[i]);
    }
}
int curNodeLevel = 0;
void RecursePrintAiNodeInfo(const aiNode *node) {
    curNodeLevel++;
    std::cout << "++++++++ recurse print node LEVEL=" << curNodeLevel << " ++++++++" <<
            "\nname: " << node->mName.C_Str() <<
            "\nTsfMatrix:\n" << GetAiMatrixPrint(node->mTransformation) <<
            "NumMeshes: " << node->mNumMeshes <<
            "\nMeshIds: ";
    for (unsigned int meshid: std::span(node->mMeshes, node->mNumMeshes)) {
        std::cout << meshid << ' ';
    }
    std::cout << "\nSubNodeCount: " << node->mNumChildren << std::endl;
    for (auto *subnode: std::span(node->mChildren, node->mNumChildren)) {
        RecursePrintAiNodeInfo(subnode);
    }


    curNodeLevel--;
}


constexpr unsigned int GetFlag_Simple() {
    return aiProcess_Triangulate | aiProcess_FlipUVs;
}

constexpr unsigned int GetFlag_Smooth() {
    return aiProcess_Triangulate |
           aiProcess_JoinIdenticalVertices |
           aiProcess_GenSmoothNormals |
           aiProcess_CalcTangentSpace |
           aiProcess_ImproveCacheLocality |
           aiProcess_OptimizeMeshes |
           aiProcess_SortByPType |
           aiProcess_FindInvalidData |
           aiProcess_FindDegenerates |
           aiProcess_MakeLeftHanded |
           aiProcess_FlipUVs |
           aiProcess_FlipWindingOrder |
           aiProcess_ConvertToLeftHanded;

}

constexpr unsigned int GetFlag_NoSmoothNormal() {
    return aiProcess_Triangulate |
           aiProcess_JoinIdenticalVertices |
           aiProcess_CalcTangentSpace |
           aiProcess_ImproveCacheLocality |
           aiProcess_OptimizeMeshes |
           aiProcess_SortByPType |
           aiProcess_FindInvalidData |
           aiProcess_FindDegenerates |
           aiProcess_MakeLeftHanded |
           aiProcess_FlipUVs |
           aiProcess_FlipWindingOrder |
           aiProcess_ConvertToLeftHanded;
}

// DirectX::XMFLOAT4X4 AiToFloat4x4(const aiMatrix4x4 &ai) {
//     return {
//         ai.a1, ai.a2, ai.a3, ai.a4,
//         ai.b1, ai.b2, ai.b3, ai.b4,
//         ai.c1, ai.c2, ai.c3, ai.c4,
//         ai.d1, ai.d2, ai.d3, ai.d4
//     };
// }
DirectX::XMFLOAT4X4 AiToRowMajorFloat4x4(const aiMatrix4x4 &m) {
    return {
        m.a1, m.b1, m.c1, m.d1,
        m.a2, m.b2, m.c2, m.d2,
        m.a3, m.b3, m.c3, m.d3,
        m.a4, m.b4, m.c4, m.d4
    };
}

DirectX::XMFLOAT4X4 InverseFloat4x4(const DirectX::XMFLOAT4X4 &origin) {
    using namespace DirectX;

    XMMATRIX m = XMLoadFloat4x4(&origin);
    m = XMMatrixInverse(nullptr, m);

    XMFLOAT4X4 result{};
    XMStoreFloat4x4(&result, m);
    return result;
}

class Processor {
public:
    explicit Processor(std::filesystem::path path,
                       const aiScene *scene) : modelPath(std::move(path)),
                                               m_scene(scene) {}


    Z::Model *ProcessAiScene() {
        m_model = new Z::Model{};
        // m_model->name = m_scene->mName.C_Str();
        // m_model->name = modelPath.string();
        std::u8string u8 = modelPath.u8string();
        m_model->name = {reinterpret_cast<const char *>(u8.c_str()), u8.size()};

        m_model->path = modelPath;

        // 创建MeshData，顺序映射 aiScene->mMeshes 数组
        m_model->ar_meshDatas.reserve(m_scene->mNumMeshes);
        int _i = 0;
        for (const aiMesh *am: std::span(m_scene->mMeshes, m_scene->mNumMeshes)) {
            m_model->ar_meshDatas.push_back(CreateMeshData(am, _i));
            _i++;
        }

        // 处理NodeTree
        m_model->skeletonData.rootNode = BuildBoneNodeTree(m_scene->mRootNode);
        m_model->skeletonData.gInvTsf =
                InverseFloat4x4(AiToRowMajorFloat4x4(m_scene->mRootNode->mTransformation));

        // 填充 AnimationClip
        if (m_scene->HasAnimations()) {
            m_model->ar_animationClips.reserve(m_scene->mNumAnimations);
            for (const auto *aiAnim: std::span(m_scene->mAnimations, m_scene->mNumAnimations)) {
                m_model->ar_animationClips.emplace_back(FillAnimClip(aiAnim));
            }
        }

        return m_model;
    }

private:
    //region 创建MeshData,建立Bone的索引表,递归创建skeleten data
    Z::Mesh CreateMeshData(const aiMesh *am, int index) const {
        Z::Mesh mesh{};
        mesh.index = index;
        mesh.name = am->mName.C_Str();
        //装填material data
        // mesh.material = LoadMaterialData(am);
        // mesh.material=nullptr;
        //装填vertex data
        const uint32_t c_vert = am->mNumVertices;
        mesh.ar_vertices.reserve(c_vert);
        for (uint32_t i = 0; i < c_vert; ++i) {
            Z::VertexData vert{};
            vert.ar_boneIndex.fill(-1);
            vert.pos = {am->mVertices[i].x, am->mVertices[i].y, am->mVertices[i].z};
            if (am->HasNormals()) {
                vert.normal = {am->mNormals[i].x, am->mNormals[i].y, am->mNormals[i].z};
            }
            if (am->HasTangentsAndBitangents()) {
                vert.tangent = {am->mTangents[i].x, am->mTangents[i].y, am->mTangents[i].z};
            }
            if (am->HasTextureCoords(0)) {
                vert.uv0 = {am->mTextureCoords[0][i].x, am->mTextureCoords[0][i].y};
            }
            if (am->HasTextureCoords(1)) {
                vert.uv1 = {am->mTextureCoords[1][i].x, am->mTextureCoords[1][i].y};
            }
            if (am->HasVertexColors(0)) {
                vert.color = {
                    am->mColors[0][i].r, am->mColors[0][i].g,
                    am->mColors[0][i].b, am->mColors[0][i].a
                };
            }
            mesh.ar_vertices.push_back(vert);
        }
        // 构建bone name <-> IDX 表，并填写每个Vertex的骨骼ID,对应权重
        for (auto *bone: std::span(am->mBones, am->mNumBones)) {
            if (bone->mNumWeights > 0) {
                mesh.hasSkin = true;
                UINT boneID = GetOrCreateBoneID(bone);
                std::cout << "= = = = 创建Mesh阶段读取aiMesh->mBones[],aiBone->name=" <<
                        bone->mName.C_Str() << " BoneNumWeights=" << bone->mNumWeights <<
                        " 当前BoneID=" << boneID << std::endl;
                for (aiVertexWeight &vWeight: std::span(bone->mWeights, bone->mNumWeights)) {
                    auto &vert = mesh.ar_vertices[vWeight.mVertexId];
                    for (int i = 0; i < Z::MAX_BONES_PER_VERT; ++i) {
                        if (vert.ar_boneIndex[i] == -1) {
                            vert.ar_boneIndex[i] = static_cast<int>(boneID);
                            vert.ar_boneWeights[i] = vWeight.mWeight;
                            break;
                        }
                    }
                }
            }
        }
        //装填 INDICES DATA

        mesh.ar_indices.reserve(am->mNumFaces * 3);
        for (const auto &face: std::span(am->mFaces, am->mNumFaces)) {
            if (face.mNumIndices != 3) continue;
            mesh.ar_indices.push_back(face.mIndices[0]);
            mesh.ar_indices.push_back(face.mIndices[1]);
            mesh.ar_indices.push_back(face.mIndices[2]);
        }

        return mesh;
    }

    // Z::MaterialData LoadMaterialData(const aiMesh *am) const {
    //     Z::MaterialData out{};
    //     if (!am || am->mMaterialIndex >= m_scene->mNumMaterials) return out;
    //
    //     aiMaterial *mat = m_scene->mMaterials[am->mMaterialIndex];
    //
    //     out.name = mat->GetName().C_Str();
    //
    //     // // baseColor: PBR 优先
    //     // aiColor4D base{};
    //     // if (mat->Get(AI_MATKEY_BASE_COLOR, base) == AI_SUCCESS) {
    //     //     out.baseColor = {base.r, base.g, base.b, base.a};
    //     // } else {
    //     //     // fallback: 老式 diffuse
    //     //     aiColor3D diff{};
    //     //     if (mat->Get(AI_MATKEY_COLOR_DIFFUSE, diff) == AI_SUCCESS) {
    //     //         out.baseColor = {diff.r, diff.g, diff.b, 1.0f};
    //     //     }
    //     // }
    //
    //     auto LoadTextures = [&](aiTextureType type) {
    //         const unsigned int n = mat->GetTextureCount(type);
    //
    //         for (unsigned int i = 0; i < n; ++i) {
    //             aiString path;
    //             if (mat->GetTexture(type, i, &path) != AI_SUCCESS) continue;
    //
    //             Z::TextureData tex{};
    //             tex.path = path.C_Str();
    //             tex.type = static_cast<Z::TextureType>(type);
    //
    //             out.ar_textures.push_back(std::move(tex));
    //         }
    //     };
    //
    //     // 常用纹理
    //     LoadTextures(aiTextureType_BASE_COLOR); // PBR base color / albedo
    //     LoadTextures(aiTextureType_DIFFUSE); // 传统 diffuse
    //     LoadTextures(aiTextureType_NORMALS); // normal
    //     LoadTextures(aiTextureType_HEIGHT); // 有些格式把 normal 放这里
    //     LoadTextures(aiTextureType_SPECULAR); // specular
    //     LoadTextures(aiTextureType_METALNESS); // metallic
    //     LoadTextures(aiTextureType_DIFFUSE_ROUGHNESS); // roughness
    //     LoadTextures(aiTextureType_AMBIENT_OCCLUSION); // ao
    //     LoadTextures(aiTextureType_EMISSIVE); // emissive
    //
    //     return out;
    // }


    UINT GetOrCreateBoneID(const aiBone *bone) const {
        auto *name = bone->mName.C_Str();
        auto idx = 0;
        auto &arr = m_model->skeletonData.ar_boneInfo;
        for (auto it = arr.begin(); it != arr.end(); ++it) {
            if (it->name == name) {
                return idx;
            }
            idx++;
        }
        arr.push_back({name, AiToRowMajorFloat4x4(bone->mOffsetMatrix)});
        idx = static_cast<int>(arr.size() - 1);
        m_model->skeletonData.map_boneName_idx.emplace(name, idx);
        return idx;
    }

    // 处理skeleton[0] 的 NodeTree
    Z::Node BuildBoneNodeTree(const aiNode *an) {
        Z::Node node{};
        node.name = an->mName.C_Str();
        std::cout << "+ + + + 正在递归处理NodeTree，当前node.name=" << node.name << std::endl;
        node.localTsf = AiToRowMajorFloat4x4(an->mTransformation);
        // 处理mesh
        for (UINT meshIdx: std::span(an->mMeshes, an->mNumMeshes)) {
            Z::Mesh *meshData = &m_model->ar_meshDatas[meshIdx];
            std::cout << "+ + Node中处理Mesh，meshIdx=" << meshIdx <<
                    ", MeshData.Name=" << meshData->name << std::endl;
            meshData->localTsf = AiToRowMajorFloat4x4(an->mTransformation); //非骨骼模型的统一偏移矩阵
        }

        int cc = static_cast<int>(an->mNumChildren);
        node.ar_child.reserve(cc);
        for (int i = 0; i < cc; ++i) {
            node.ar_child.push_back(BuildBoneNodeTree(an->mChildren[i]));
        }
        return node;
    }
    //endregion

    //region 填充 AnimationClip

    static DirectX::XMFLOAT3 AiVector3ToXMFloat3(const aiVector3D &vec3) {
        return {vec3.x, vec3.y, vec3.z};
    }
    static DirectX::XMFLOAT4 AiVector4ToXMFloat4(const aiQuaternion &vec4) {
        return {vec4.x, vec4.y, vec4.z, vec4.w};
    }

    static Z::AnimationClip FillAnimClip(const aiAnimation *ai) {
        Z::AnimationClip anim{};
        anim.name = ai->mName.C_Str();
        anim.durationTicks = ai->mDuration;
        anim.tickPerSec = ai->mTicksPerSecond;

        anim.map_channels.reserve(ai->mNumChannels);
        for (int i = 0; i < ai->mNumChannels; ++i) {
            aiNodeAnim *c = ai->mChannels[i];
            Z::NodeAnimation nodeAnim{};
            nodeAnim.name = ai->mName.C_Str();
            nodeAnim.ar_posKeys.reserve(c->mNumPositionKeys);
            for (int j = 0; j < c->mNumPositionKeys; ++j) {
                nodeAnim.ar_posKeys.emplace_back(
                    c->mPositionKeys[j].mTime, AiVector3ToXMFloat3(c->mPositionKeys[j].mValue));
            }
            nodeAnim.ar_rotationKeys.reserve(c->mNumRotationKeys);
            for (int j = 0; j < c->mNumRotationKeys; ++j) {
                nodeAnim.ar_rotationKeys.emplace_back(
                    c->mRotationKeys[j].mTime, AiVector4ToXMFloat4(c->mRotationKeys[j].mValue));
            }
            nodeAnim.ar_scaleKeys.reserve(c->mNumScalingKeys);
            for (int j = 0; j < c->mNumScalingKeys; ++j) {
                nodeAnim.ar_scaleKeys.emplace_back(
                    c->mScalingKeys[j].mTime, AiVector3ToXMFloat3(c->mScalingKeys[j].mValue));
            }
            anim.map_channels.emplace(std::string{c->mNodeName.C_Str()}, std::move(nodeAnim));
        }

        return anim;
    }

    //endregion
private :
    const std::filesystem::path modelPath;
    const aiScene *const m_scene;
    Z::Model *m_model{};
};


}


bool Importer::ImportFromFile(const std::filesystem::path &path, Z::Model **out) const {
    // 导入器
    Assimp::Importer importer;

    // 导入前配置选项
    // 打开FBX单位从 cm->m 的转换开关
    importer.SetPropertyBool(AI_CONFIG_FBX_CONVERT_TO_M, true);
    // 设置全局缩放因子
    // importer.SetPropertyFloat(AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY, 0.01f);

    // 导入文件,初步处理
    const aiScene *scene = importer.ReadFile(
        reinterpret_cast<const char *>(path.u8string().c_str()),
        GetFlag_NoSmoothNormal() | aiProcess_GlobalScale);

    // 导入后验证
    if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
        MessageBox(nullptr, L"Error", L"Assimp ReadFile Error",MB_OK);
        return false;
    }

    // for (auto *am: std::span(scene->mMeshes, scene->mNumMeshes)) {
    //     for (int i = 0; i < am->mNumBones; ++i) {
    //         aiBone *bone = am->mBones[i];
    //         std::cout << "BoneName = " << bone->mName.C_Str() <<
    //                 "\nNumWeights = " << bone->mNumWeights << std::endl;
    //     }
    // }

    // RecursePrintAiNodeInfo(scene->mRootNode);

    Processor pro(path, scene);
    *out = pro.ProcessAiScene();

    return true;
}
void Importer::ReleaseLoadedModel(const Z::Model *model) const {
    delete model;
}




















//
