/****************************************************************
 *
 * 作   者：王建军
 * 时   间：2024/03/27
 * 开发语言：C++
 * 开发工具：QtCreator(Qt)
 * 文   件：pesdkutility.cpp
 * 版   权：武汉创景可视技术有限公司
 *
 ***************************************************************/
#include "pesdkutiltiy.h"

#include <functional>
#include <filesystem>

#include "fbxsdkutility.h"
#include "strconv.h"
#include "simplelog.h"
#include "my_utility.h"
#include "MVOUtilities.h"

#include <sstream>
#include <filesystem>
#include <map>
#include <utility>

#define STB_IMAGE_WRITE_IMPLEMENTATION
#define STB_IMAGE_WRITE_STATIC
#include "stb_image_write.h"

extern FbxManager* gSdkManager;
extern FbxScene* gScene;

namespace Vis
{
VsDatabase *fieldPathToVisNode(std::string path)
{
    const char* rootFieldPath = "/";
    VS_KEY key = VsFieldExists(rootFieldPath);
    if (key == VS_INVALID_KEY)
    {
        return nullptr;
    }
    VsDatabase* rootNode = nullptr;
    VsShowDatabaseByKey(key, rootNode);

    char szFieldPath[4095]; // 节点路径
    VsShowFieldPathName(key, szFieldPath);

    std::string path1 = szFieldPath;
    if(path1.compare(path) == 0)
        return rootNode;

    VsDatabase* node = nullptr;

    std::function<void (VsDatabase*)> f1 =

        [&](VsDatabase* visNode) {
            VS_KEY key = visNode->key;
            if (key == VS_INVALID_KEY)
            {
                return;
            }

            char szFieldPath[4095];
            VsShowFieldPathName(key, szFieldPath);

            std::string path1 = szFieldPath;
            if(path1.compare(path) == 0)
            {
                node = visNode;
                return;
            }

            VsDatabase* childNode = visNode->childList;
            while (childNode)
            {
                f1(childNode);
                childNode = childNode->next;
            }

        };

    f1(rootNode);

    return node;
}

std::string debugVisNode(VsDatabase *visNode)
{
    if(visNode)
    {
        VS_KEY key = visNode->key;
        char szFieldPath[4095]; // 节点路径
        VsShowFieldPathName(key, szFieldPath);
        char name[255]; // 节点名称
        VsShowFieldName(key, name);

        printf("key: %lld, name: %s, path: %s\n", key, name, szFieldPath);
        std::stringstream ss;
        ss << "key: " << key << ", name: " << name << ", path:" << szFieldPath << std::endl;
        return ss.str();
    }
    else
    {
        printf("vis node is null\n");
        return std::string("vis node is null");
    }
}

void traverseVisNodeAndSaveAsFbxFile(VsDatabase *visNode, std::string savePath)
{
    Fbx::InitializeSdkManagerAndScene();

    FbxNode* lRootNode = gScene->GetRootNode();
    FbxNode* lFirstNode = nullptr;
    {
        char name[255];
        VsShowFieldName(visNode->key, name);
        lFirstNode = FbxNode::Create(gScene, StrConv::AnsiToUTF8(name).c_str());
        FbxNull* lNull = FbxNull::Create(gScene, "Null");
        lFirstNode->SetNodeAttribute(lNull);
        lRootNode->AddChild(lFirstNode);
    }

    int texCount = -1;
    VsShowTexturesCount(texCount);
    VS_KEY* texKeys = new VS_KEY[texCount];
    VsShowTextures(texCount, texKeys);

    std::map<VS_KEY, std::pair<std::string, FbxFileTexture*>> keyTexMap;

    std::filesystem::path path(savePath);
    std::string baseName = path.stem().string();
    std::string dir = path.parent_path().string() + "/" + baseName;
    std::filesystem::create_directory(std::filesystem::path(dir));

    for(int i = 0; i <texCount; i++ )
    {
        std::string texImageFileName = dir  + "/" + MyUtility::GenerateUUID() + ".png";
        keyTexMap[texKeys[i]] = {texImageFileName, FbxFileTexture::Create(gScene, "Diffuse Texture")};
    }
    delete []texKeys;

// 遍历循环PE所有节点-----------------------------------------------------------------------------------------
#if 1

    std::function<void (VsDatabase*, FbxNode*)> f =

        [&](VsDatabase* visnode, FbxNode* fbxnode) {
            VS_KEY currentKey = visnode->key;
            if (currentKey == VS_INVALID_KEY)
            {
                return;
            }

            VsOpenFieldByKey(currentKey);
            int meshCount = 0;      // 网格数量
            VS_KEY* keys = NULL;    // 网格key数组
            VsShowFieldContents(".", "trimeshes", meshCount, keys);
            char name[255];         // 节点名称
            char fieldName[255];
            VsShowFieldName(currentKey, name);
            VsShowFieldPathName(currentKey, fieldName);

            VS_KEY texKey = -1;
            VsShowFieldTextureKey(".", texKey);
            std::string texImageFileName = "";
            std::filesystem::path path(savePath);
            if(texKey != -1)
            {
                int imageWidth = 0;
                int imageHeight = 0;
                int imageComponent = 0;
                VsShowTextureSize(texKey, imageWidth, imageHeight, imageComponent);

                unsigned char* pBits = VsNewArray(imageWidth * imageHeight * imageComponent);
                BOOL bEnv = false;
                float texMat[16] = {0};
                int coordIndex = 0;
                int texType = VS_MAP_COLOR;
                int texIndex = 0;
                VsShowFieldTextureParam(".", bEnv, texMat, coordIndex, texType, texIndex);
                float repeatX = 0.0, repeatY = 0.0,  rotation = 0.0, offsetX = 0.0, offsetY = 0.0;
                MVOExplaineTextureMatrix(texMat, repeatX, repeatY,  rotation, offsetX, offsetY);
                VsShowFieldTextureBits(".", pBits, imageWidth, imageHeight, imageComponent, texType, texIndex);

                texImageFileName = keyTexMap[texKey].first;
                std::filesystem::path txtPath(texImageFileName);
                if(!std::filesystem::exists(txtPath))
                {
                    if(stbi_write_png(texImageFileName.c_str(), imageWidth, imageHeight, imageComponent, pBits, 0))
                    {
                        // 找不到fbx sdk加载char*纹理接口，只能把char*数据保存成本地文件，然后再加载本地文件做纹理
                        FbxString basePath = texImageFileName.c_str();
                        FbxFileTexture* lTexture = keyTexMap[texKey].second;
                        lTexture->SetFileName(basePath.Buffer());
                        lTexture->SetTextureUse(FbxTexture::eStandard);
                        lTexture->SetMappingType(FbxTexture::eUV);
                        lTexture->SetMaterialUse(FbxFileTexture::eModelMaterial);
                        lTexture->SetSwapUV(true);
                        lTexture->SetTranslation(0.0, 0.0);
                        lTexture->SetScale(1.0, 1.0);
                        lTexture->SetRotation(0.0, 0.0);
                    }
                }
            }

            VsCloseField();

            for(int meshIdx = 0; meshIdx < meshCount; meshIdx++)
            {
                FbxNode* lNode = nullptr;

                // 创建一个fbx mesh对象
                FbxMesh* lMesh = FbxMesh::Create(gScene, StrConv::AnsiToUTF8(name).c_str());

                FbxGeometryElementNormal* lGeometryElementNormal = lMesh->CreateElementNormal();
                lGeometryElementNormal->SetMappingMode(FbxGeometryElement::eByControlPoint);
                lGeometryElementNormal->SetReferenceMode(FbxGeometryElement::eDirect);
                FbxGeometryElementUV* lUVDiffuseElement = lMesh->CreateElementUV("DiffuseUV");
                FbxGeometryElementUV* lUVAmbientElement = lMesh->CreateElementUV("AmbientUV");
                FbxGeometryElementUV* lUVEmissiveElement = lMesh->CreateElementUV("EmissiveUV");
                lUVDiffuseElement->SetMappingMode(FbxGeometryElement::eByControlPoint);
                lUVDiffuseElement->SetReferenceMode(FbxGeometryElement::eDirect);
                lUVAmbientElement->SetMappingMode(FbxGeometryElement::eByControlPoint);
                lUVAmbientElement->SetReferenceMode(FbxGeometryElement::eDirect);
                lUVEmissiveElement->SetMappingMode(FbxGeometryElement::eByControlPoint);
                lUVEmissiveElement->SetReferenceMode(FbxGeometryElement::eDirect);

                float diffuseR, diffuseG, diffuseB, diffuseA;   // 慢反射
                float ambientR, ambientG, ambientB;             // 环境光
                float emissionR, emissionG, emissionB;          // 自发光
                float shininess;                                // 高光
                float transparency;                             // 透明度
                float specularR, specularG, specularB;          // 镜面反射


                VsShowMaterialDiffuseByKey(currentKey, diffuseR, diffuseG, diffuseB, diffuseA);
                VsShowMaterialAmbientByKey(currentKey, ambientR, ambientG, ambientB);
                VsShowMaterialEmissionByKey(currentKey, emissionR, emissionG, emissionB);
                VsShowMaterialShininessByKey(currentKey, shininess);
                VsShowTransparencyByKey(currentKey, transparency);
                VsShowMaterialSpecularByKey(currentKey, specularR, specularG, specularB);

                FbxString lMaterialName = "material";
                FbxString lShadingName = "Phong"; // "Lambert"/"Phong"
                FbxDouble4 lDdiffuseColor(diffuseR, diffuseG, diffuseB, diffuseA);
                FbxDouble3 lAmbientColor(ambientR, ambientG, ambientB);
                FbxDouble3 lEmissionColor(emissionR, emissionG, emissionB);
                FbxDouble3 lSpecularColor(specularR, specularG, specularB);

                // 创建mehs节点
                if(meshCount == 1)
                {
                    lNode = fbxnode;
                }
                else
                {
                    lNode = FbxNode::Create(gScene, StrConv::AnsiToUTF8(name).c_str());
                    fbxnode->AddChild(lNode);
                }
                // 设置mesh节点属性
                lNode->SetNodeAttribute(lMesh);
                lNode->SetShadingMode(FbxNode::eTextureShading);

                // 一个网格属性
                int pointCount = 0;         // 点数量
                int lodLevel = 0;           // LOD层
                int pathCount = 0;          // 通道个数?不知道有什么卵用
                int faceCount = 0;          // 面数量
                unsigned int* faceIndexes;  // 面索引数组
                float cubeSize;             // 包围盒大小
                float* points;              // 点数组
                float* normals;             // 法线数组
                float* texCoord;            // 纹理数组
                float* colors;              // 点颜色数组
                VsShowTriMeshDataRef(keys[meshIdx], lodLevel,
                                     cubeSize, pointCount, pathCount,
                                     points, normals, texCoord, colors,
                                     faceCount, faceIndexes);

                // 初始化一个fbx mesh的点数量
                lMesh->InitControlPoints(pointCount);
                FbxVector4* lControlPoints = lMesh->GetControlPoints();


                //--------------------------------------------------------------------------------------------------------------------------------------
                // 获取penode中三维坐标，填充到fbxnode中
                // 这么做，也是可以的，只是把所有坐标都转为世界坐标后，有点不精确
                // for (int pointIdx = 0; pointIdx < pointCount; pointIdx++)
                // {
                //     float pointCoordinate[3] = {0};

                //     // 将三维点坐标转为世界坐标
                //     VS_KEY currentKey = key;
                //     VS_KEY rootKey = VsFieldExists("/scene/model");
                //     VsComputePosition(currentKey, points + 3 * pointIdx, rootKey, pointCoordinate);

                //     // 一个fbx mesh中的三维点
                //     FbxVector4 vertex(pointCoordinate[0], pointCoordinate[1], pointCoordinate[2]);
                //     lControlPoints[pointIdx] = vertex;
                // }

                // 网格坐标, 法线坐标
                for (int pointIdx = 0; pointIdx < pointCount; pointIdx++)
                {
                    float pointCoordinate[3] = {0};
                    pointCoordinate[0] = *(points + 3 * pointIdx);
                    pointCoordinate[1] = *(points + 3 * pointIdx + 1);
                    pointCoordinate[2] = *(points + 3 * pointIdx + 2);
                    // 一个fbx mesh中的三维点
                    FbxVector4 vertexVec(pointCoordinate[0], pointCoordinate[1], pointCoordinate[2]);
                    lControlPoints[pointIdx] = vertexVec;

                    if(normals)
                    {
                        float pointNormal[3] = {0};
                        pointNormal[0] = *(normals + 3 * pointIdx);
                        pointNormal[1] = *(normals + 3 * pointIdx + 1);
                        pointNormal[2] = *(normals + 3 * pointIdx + 2);
                        FbxVector4 normalVec(pointNormal[0], pointNormal[1], pointNormal[2]);
                        lGeometryElementNormal->GetDirectArray().Add(normalVec);
                    }

                    if(texCoord)
                    {
                        float pointUV[2] = {0};
                        pointUV[0] = *(texCoord + 2 * pointIdx);
                        pointUV[1] = *(texCoord + 2 * pointIdx + 1);
                        FbxVector2 uvVec(pointUV[0], pointUV[1]);
                        lUVDiffuseElement->GetDirectArray().Add(uvVec);
                        lUVAmbientElement->GetDirectArray().Add(uvVec);
                        lUVEmissiveElement->GetDirectArray().Add(uvVec);
                    }
                }

                FbxSurfacePhong* lMaterial = nullptr;
                if(texKey != -1)
                {
                    //FbxSurfacePhong* lMaterial = nullptr;
                    lMaterial = lNode->GetSrcObject<FbxSurfacePhong>(0);
                    if (lMaterial == NULL)
                    {
                        FbxString lMaterialName = "toto";
                        FbxString lShadingName = "Phong";
                        FbxDouble3 lBlack(0.0, 0.0, 0.0);
                        FbxDouble3 lRed(1.0, 0.0, 0.0);
                        FbxDouble3 lDiffuseColor(0.75, 0.75, 0.0);
                        lMaterial = FbxSurfacePhong::Create(gScene, lMaterialName.Buffer());

                        // FbxSurfacePhong* lMaterial = FbxSurfacePhong::Create(gScene, lMaterialName.Buffer());
                        // FbxSurfaceLambert* lMaterial = FbxSurfaceLambert::Create(gScene, lMaterialName.Buffer());
                        lMaterial->Diffuse.Set(lDdiffuseColor);
                        lMaterial->Ambient.Set(lAmbientColor);
                        lMaterial->Emissive.Set(lEmissionColor);
                        lMaterial->ShadingModel.Set(lShadingName);
                        lMaterial->Shininess.Set(shininess);
                        lMaterial->TransparencyFactor.Set(transparency);
                        lMaterial->Specular.Set(lSpecularColor);
                        //lMaterial->AmbientFactor.Set(1.);
                        //lMaterial->DiffuseFactor.Set(1.);
                        //lMaterial->SpecularFactor.Set(1.);
                    }

                    if (lMaterial)
                    {
                        lMaterial->Diffuse.ConnectSrcObject(keyTexMap[texKey].second);
                    }
                }
                else
                {
                    lMaterial = FbxSurfacePhong::Create(gScene, lMaterialName.Buffer());
                    lMaterial->Diffuse.Set(lDdiffuseColor);
                    lMaterial->Ambient.Set(lAmbientColor);
                    lMaterial->Emissive.Set(lEmissionColor);
                    lMaterial->ShadingModel.Set(lShadingName);
                    lMaterial->Shininess.Set(shininess);
                    lMaterial->TransparencyFactor.Set(transparency);
                    lMaterial->Specular.Set(lSpecularColor);
                }
                lNode->AddMaterial(lMaterial);

                // 设置面，通过设置顶点索引，构建面----------------------------------------------------------------------------------------
                // 指定哪些三维点构成一个面
                // faceCount是visnode中的面数量
                // 在visnode中，面只有三角形，且一个点被多个面共用时，要重复记录该点
                // 所以，visnode中的面数量，一定是点数量的1/3
                for (int i = 0; i < faceCount; i++)
                {
                    lMesh->BeginPolygon(i);
                    for (int j = 0; j < 3; j++)
                    {
                        //lMesh->AddPolygon(i * 3 + j);
                        int pointId = faceIndexes[i * 3 + j];
                        lMesh->AddPolygon(pointId);
                    }
                    lMesh->EndPolygon();
                    //lMeshNode->AddMaterial(lMaterial);
                }

//                // 删除保存本地的纹理文件
//                namespace fs = std::filesystem;
//                fs::path filePath = texImageFileName;
//                // 检查文件是否存在
//                if (fs::exists(filePath))
//                {
//                    fs::remove(filePath);
//                }

            }


            VsDatabase* visChildNode = visnode->childList;
            while (visChildNode)
            {
                VS_KEY visChildKey = visChildNode->key;
                VsOpenFieldByKey(visChildKey);
                char visChildName[255];
                VsShowFieldName(visChildKey, visChildName);
                VsCloseField();

                FbxNode* fbxChildNode = FbxNode::Create(gScene, StrConv::AnsiToUTF8(visChildName).c_str());
                fbxnode->AddChild(fbxChildNode);
                FbxNull* lNull = FbxNull::Create(gScene, "Null");
                fbxChildNode->SetNodeAttribute(lNull);

                // 设置fbx模型节点的变换矩阵--------------------------------------------------------------------------------------
                float transf[16];
                // 获取从子节点到父节点之间的变换矩阵
                VsShowTransformByKey(visChildKey, visnode->key, transf);

                // 将变换矩阵存储在fbx模型中的父节点中
                FbxAMatrix localTransform;
                for (int rowIdx = 0; rowIdx < 4; ++rowIdx)
                {
                    for (int colIdx = 0; colIdx < 4; ++colIdx)
                    {
                        localTransform[rowIdx][colIdx] = transf[4 * rowIdx + colIdx];
                    }
                }
                fbxChildNode->LclTranslation.Set(localTransform.GetT());
                fbxChildNode->LclRotation.Set(localTransform.GetR());
                fbxChildNode->LclScaling.Set(localTransform.GetS());

                f(visChildNode, fbxChildNode);

                visChildNode = visChildNode->next;
            }

        };

    f(visNode, lFirstNode);

#endif

// 处理PE平台的一个节点--------------------------------------------------------------------------------------------------------
#if 0

    // 处理本节点
    VS_KEY key = penode->key;
    VsOpenFieldByKey(key);
    int meshCount = 0;       // 网格数量
    VS_KEY* keys = NULL;    // 网格key数组
    VsShowFieldContents(".", "trimeshes", meshCount, keys);
    VsCloseField();

    char szFieldPath[4095]; // 节点路径
    VsShowFieldPathName(key, szFieldPath);
    char name[255]; // 节点名称
    VsShowFieldName(key, name);

    // 创建fbx网格节点
    FbxNode* lMeshNode = FbxNode::Create(gScene, name);
    lRootNode->AddChild(lMeshNode);

    for(int i = 0; i < meshCount; i++)
    {
        // 创建一个fbx mesh对象
        FbxMesh* lMesh = FbxMesh::Create(gScene, "mesh");

        // Create a node for our mesh in the scene.
        FbxNode* lMeshNode = FbxNode::Create(gScene, name);
        lRootNode->AddChild(lMeshNode);

        // Set the node attribute of the mesh node.
        lMeshNode->SetNodeAttribute(lMesh);


        // 一个网格属性
        int pointCount = 0;         // 点数量
        int lodLevel = 0;           // LOD层
        int pathCount = 0;          // 通道个数?不知道有什么卵用
        int faceCount = 0;          // 面数量
        unsigned int* faceIndexes;  // 面索引数组
        float cubeSize;             // 包围盒大小
        float* points;              // 点数组
        float* normals;             // 法线数组
        float* texCoord;            // 纹理数组
        float* colors;              // 点颜色数组
        VsShowTriMeshDataRef(keys[i], lodLevel,
                             cubeSize, pointCount, pathCount,
                             points, normals, texCoord,
                             colors, faceCount, faceIndexes);

        //qDebug() << QString("pointCount:%1, pathCount:%2, faceCount:%3").arg(pointCount).arg(pathCount).arg(faceCount);

        // 初始化一个fbx mesh的点数量
        lMesh->InitControlPoints(pointCount);
        FbxVector4* lControlPoints = lMesh->GetControlPoints();

        for (size_t v = 0; v < pointCount; v++)
        {
            float point_world[3] = {0};
            //VsComputePointMultiMatrix3d(points + 3 * v, matrix, point_world);
            VsComputePosition(key, points + 3 * v, VsFieldExists("/scene/model"), point_world);
            // qDebug() << QString("x:%1, y:%2, z:%3").arg(point_world[0]).arg(point_world[1]).arg(point_world[2]);

            // 一个fbx mesh中的三维点
            FbxVector4 vertex(point_world[0], point_world[1], point_world[2]);
            lControlPoints[v] = vertex;
        }

        // 设置面，通过设置顶点索引，构建面
        // 如果不设置面，那么顶点将无法显示
        // for (size_t f = 0; f < faceCount * 3; f++)
        // {
        //    qDebug() << faceIndexes[f];
        // }
        for (int i = 0; i < faceCount; i++)
        {
            lMesh->BeginPolygon(i);
            //std::cout << "index:";
            for (int j = 0; j < 3; j++)
            {
                //int index = i * 3 + j;
                //std::cout << index << "\t";
                lMesh->AddPolygon(i * 3 + j);
            }
            lMesh->EndPolygon();
            //std::cout << std::endl;
        }
    }
#endif

    Fbx::SaveFbx(savePath.c_str());
    Fbx::DestroySdkObjects(gSdkManager, true);
}

VsDatabase* getParentByVisNode(VsDatabase *visNode)
{
    const char* rootFieldPath = "/";
    VS_KEY key = VsFieldExists(rootFieldPath);
    if (key == VS_INVALID_KEY)
    {
        return nullptr;
    }
    VsDatabase* pVsDb = nullptr;
    VsShowDatabaseByKey(key, pVsDb);

    if(pVsDb == visNode)
    {
        return nullptr;
    }
    if(pVsDb->childList == visNode)
    {
        return pVsDb;
    }

    VsDatabase* _db = nullptr;

    std::function<void (VsDatabase*)> f1 =

        [&](VsDatabase* db) {
            VS_KEY key = db->key;
            if (key == VS_INVALID_KEY)
            {
                return;
            }

            char szFieldPath[4095];
            VsShowFieldPathName(key, szFieldPath);

            if(db->childList == visNode)
            {
                _db = db;
                return;
            }

            VsDatabase* pChild = db->childList;
            while (pChild)
            {
                f1(pChild);
                pChild = pChild->next;
            }

        };

    f1(pVsDb);

    return _db;
}

VsDatabase *getParentByVisKey(VS_KEY visKey)
{
    const char* rootFieldPath = "/";
    VS_KEY key = VsFieldExists(rootFieldPath);
    if (key == VS_INVALID_KEY)
    {
        return nullptr;
    }
    VsDatabase* pVsDb = nullptr;
    VsShowDatabaseByKey(key, pVsDb);

    if(pVsDb->key == visKey)
    {
        return nullptr;
    }
    if(pVsDb->childList->key == visKey)
    {
        return pVsDb;
    }

    VsDatabase* _db = nullptr;

    std::function<void (VsDatabase*)> f1 =

        [&](VsDatabase* db) {
            VS_KEY key = db->key;
            if (key == VS_INVALID_KEY)
            {
                return;
            }

            if(!db->childList)
            {
                return;
            }

            if(db->childList->key == visKey)
            {
                _db = db;
                return;
            }

            VsDatabase* pChild = db->childList;
            while (pChild)
            {
                f1(pChild);
                pChild = pChild->next;
            }

        };

    f1(pVsDb);

    return _db;
}

VsDatabase *getVisNodeByVsKey(intptr_t visKey)
{
    const char* rootFieldPath = "/";
    VS_KEY rootKey = VsFieldExists(rootFieldPath);
    if (rootKey == VS_INVALID_KEY)
    {
        return nullptr;
    }
    VsDatabase* rootNode = nullptr;
    VsShowDatabaseByKey(rootKey, rootNode);

    if(rootKey == visKey)
        return rootNode;

    VsDatabase* node = nullptr;

    std::function<void (VsDatabase*)> f1 =

        [&](VsDatabase* visNode) {
            VS_KEY currentKey = visNode->key;
            if (currentKey == VS_INVALID_KEY)
            {
                return;
            }

            if(currentKey == visKey)
            {
                node = visNode;
                return;
            }

            VsDatabase* childNode = visNode->childList;
            while (childNode)
            {
                f1(childNode);
                childNode = childNode->next;
            }

        };

    f1(rootNode);

    return node;
}

}

