#include "Xgen.h"

#include "utils.h"

#include <ai_msg.h>

XGenHelper::XGenHelper(MObject &desc)
    : m_description(desc)
{
    // GetSplineBase(m_splineBase);
}

XGenHelper::XGenHelper(string descName)
{
    GetMayaObject(descName, m_description);
    GetMayaDagPath(descName, m_descPath);
    // GetSplineBase(m_splineBase);
}

XGenHelper::XGenHelper(MDagPath &path)
    : m_description(path.node()), m_descPath(path)
{
    // GetSplineBase(m_splineBase);
}

XGenHelper::~XGenHelper()
{
}

void XGenHelper::GetSplineData(SplineDataMap &splineDataMap, unsigned int step)
{
    MFnDagNode fnDesc(m_description);
    static const MString sApplyRenderOverrideCmd = "xgmSplineApplyRenderOverride ";
    MGlobal::executeCommand(sApplyRenderOverrideCmd + fnDesc.partialPathName());

    MObject splineBase = GetSplineBase();
    MFnDependencyNode fnSplineBase(splineBase);

    AiMsgDebug("-------------------------------------");
    AiMsgDebug("[%s] : xgmSplineDescription : %s", kTranslatorName, fnDesc.partialPathName().asChar());
    AiMsgDebug("[%s] :        xgmSplineBase : %s", kTranslatorName, fnSplineBase.name().asChar());
    AiMsgDebug("-------------------------------------");

    AiMsgInfo("[%s] : Init splineDataMap...", kTranslatorName);
    MPlug boundMeshPlug = fnSplineBase.findPlug("boundMesh", true);

    for (unsigned int b = 0; b < boundMeshPlug.numElements(); b++)
    {
        MPlug p = boundMeshPlug.elementByLogicalIndex(b);
        MPlug meshPlug = p.source();
        if (meshPlug.isNull())
            continue;
        MFnMesh fnMesh(meshPlug.node());
        string meshName = fnMesh.name().asChar();
        SplineData splineData;
        splineDataMap[meshName] = splineData;

        AiMsgInfo("[%s] : splineDataMap : %s", kTranslatorName, meshName.c_str());
    }
    AiMsgInfo("-------------------------------------");

    std::string data;
    MPlug outPlug = fnDesc.findPlug("outRenderData");
    MObject outObj = outPlug.asMObject();
    MPxData *outData = MFnPluginData(outObj).data();

    if (outData)
    {
        std::ostringstream opaqueStrm;
        outData->writeBinary(opaqueStrm);
        data = opaqueStrm.str();
    }

    // Compute the padding bytes and number of array elements
    const unsigned int tail = data.size() % sizeof(unsigned int);
    const unsigned int padding = (tail > 0) ? sizeof(unsigned int) - tail : 0;
    const unsigned int nelements = (unsigned int)data.size() / sizeof(unsigned int) + (tail > 0 ? 1 : 0);

    const size_t sampleSize = size_t(nelements) * sizeof(unsigned int) - padding;

    AiMsgDebug("    tail : %s", tail);
    AiMsgDebug("    padding : %s", padding);
    AiMsgDebug("    nelements : %s", nelements);
    AiMsgDebug("    sampleSize : %s", sampleSize);

    XgFnSpline splines;

    double frame;
    MString cmd("currentTime -query");
    MGlobal::executeCommand(cmd, frame);

    // std::stringstream opaqueStrm;
    // opaqueStrm.write(data.c_str(), sampleSize);
    if (!splines.load(std::stringstream(data), sampleSize, (float)frame))
    {
        AiMsgWarning("[%s] Failed to load sample.", kTranslatorName);
    }

    // Process multiplier and density mask
    splines.executeScript();

    for (XgItSpline splineIt = splines.iterator(); !splineIt.isDone(); splineIt.next())
    {
        const unsigned int stride = splineIt.primitiveInfoStride();
        const unsigned int primitiveCount = splineIt.primitiveCount();
        const unsigned int *primitiveInfos = splineIt.primitiveInfos();
        const SgVec3f *positions = splineIt.positions(0);
#if defined(XGEN_SPLINE_API_VERSION) && XGEN_SPLINE_API_VERSION >= XGEN_SPLINE_VERSION_2017_UPDATE3
        const SgVec3f *directions = splineIt.widthDirection(0);
#endif
        AiMsgDebug("    stride : %d, primitiveCount : %d, meshId : %s", stride, primitiveCount, splineIt.boundMeshId());

        string meshId = splineIt.boundMeshId();
        MPlug boundMeshPlug = fnSplineBase.findPlug("boundMesh", true);

        if (boundMeshPlug.numElements() == 0)
            AiMsgWarning("[%s] : xgmSplineBase : No mesh connect to the %s.boundMesh.", kTranslatorName, fnSplineBase.name().asChar());

        for (unsigned int b = 0; b < boundMeshPlug.numElements(); b++)
        {
            MPlug p = boundMeshPlug.elementByLogicalIndex(b);
            int at = p.name().indexW(meshId.c_str());

            if (at < 0)
                continue;

            boundMeshPlug = p;
        }

        if (boundMeshPlug.isNull())
        {
            AiMsgWarning("[%s] : %s.boundMesh plug is Null.", kTranslatorName, fnSplineBase.name().asChar());
            continue;
        }

        MPlug meshPlug = boundMeshPlug.source();
        MFnMesh fnMesh(meshPlug.node());
        string meshName = fnMesh.name().asChar();

        AiMsgDebug("    boundMeshPlug : %s", boundMeshPlug.name().asChar());

        for (unsigned int p = 0; p < primitiveCount; p++)
        {
            const unsigned int offset = primitiveInfos[p * stride];
            const unsigned int length = primitiveInfos[p * stride + 1];

            AiMsgDebug("    offset : %d", offset);
            AiMsgDebug("    length : %d", length);

            MPointArray curve_pts;

            for (unsigned int i = 0; i < length; i++)
            {
                SgVec3f p = positions[i + offset];
                MPoint cpt(p[0], p[1], p[2]);
                curve_pts.append(cpt);
            }
            splineDataMap[meshName].push_back(curve_pts);
        }
    }

    AiMsgInfo("-------------------------------------");

    for (auto it = splineDataMap.begin(); it != splineDataMap.end(); it++)
        AiMsgInfo("    Mesh : %s, Spline Count : %d", it->first.c_str(), it->second.size());

    AiMsgInfo("-------------------------------------");
}

void XGenHelper::GetSplineRootPoints(PointArrayMap &pointArrayMap, unsigned int step)
{
    AiMsgInfo("[%s] : GetSplineRootPoints() : %s", kTranslatorName, GetDescName().asChar());

    SplineDataMap splineDataMap;
    GetSplineData(splineDataMap, step);
    for (auto it = splineDataMap.begin(); it != splineDataMap.end(); it++)
    {
        MPointArray points;
        string mesh = it->first;
        SplineData splineData = it->second;

        for (unsigned int i = 0; i < splineData.size(); i++)
        {
            MPointArray curve_pts = splineData[i];
            points.append(curve_pts[0]);
        }
        pointArrayMap[mesh] = points;
    }
}

void XGenHelper::GetSplineRootNormals(VectorArrayMap &normalArrayMap, unsigned int step)
{
    MStatus status;
    PointArrayMap rootPointArrayMap;
    GetSplineRootPoints(rootPointArrayMap, step);

    for (auto it = rootPointArrayMap.begin(); it != rootPointArrayMap.end(); it++)
    {
        MVectorArray normals;

        string mesh = it->first;
        MPointArray rootPoints = it->second;

        MDagPath meshDagPath;
        GetMayaDagPath(mesh, meshDagPath);

        if (!meshDagPath.isValid())
        {
            MGlobal::displayError(GetDescName() + " is invalid");
            continue;
        }
        if (!meshDagPath.hasFn(MFn::kMesh))
        {
            MGlobal::displayError(MString(mesh.c_str()) + " is invalid");
            continue;
        }

        MMeshIntersector intersector;
        MMatrix matrix = meshDagPath.inclusiveMatrix();
        status = intersector.create(meshDagPath.node(), matrix);

        if (status == MS::kFailure)
        {
            AiMsgError("Fail to create mesh intersector.");
            return;
        }

        if (status == MS::kInvalidParameter)
        {
            AiMsgError("Object is not a kMesh or kMeshData");
            return;
        }

        for (unsigned i = 0; i < rootPoints.length(); i++)
        {
            MPoint pt = rootPoints[i];
            MPointOnMesh pointInfo;
            intersector.getClosestPoint(pt, pointInfo);
            MFloatVector closestNormal = pointInfo.getNormal();
            closestNormal = closestNormal * -1;
            normals.append(closestNormal);
        }
        normalArrayMap[mesh] = normals;
    }
}

void XGenHelper::GetSplineRootPointsAndNormals(MPointArray &pointArray, MVectorArray &vectorArray, unsigned int step)
{
    AiMsgInfo("[%s] : getSplineRootPointsAndNormals() : %s", kTranslatorName, GetDescName().asChar());

    MStatus status;

    PointArrayMap rootPointArrayMap;
    GetSplineRootPoints(rootPointArrayMap, step);

    for (auto it = rootPointArrayMap.begin(); it != rootPointArrayMap.end(); it++)
    {
        string mesh = it->first;
        MPointArray rootPoints = it->second;

        MDagPath meshDagPath;
        GetMayaDagPath(mesh, meshDagPath);

        if (!meshDagPath.isValid())
        {
            MGlobal::displayError(GetDescName() + " is invalid");
            continue;
        }
        if (!meshDagPath.hasFn(MFn::kMesh))
        {
            MGlobal::displayError(MString(mesh.c_str()) + " is invalid");
            continue;
        }

        MMeshIntersector intersector;
        MMatrix matrix = meshDagPath.inclusiveMatrix();
        status = intersector.create(meshDagPath.node(), matrix);

        if (status == MS::kFailure)
        {
            AiMsgError("Fail to create mesh intersector.");
            return;
        }

        if (status == MS::kInvalidParameter)
        {
            AiMsgError("Object is not a kMesh or kMeshData");
            return;
        }
        for (unsigned i = 0; i < rootPoints.length(); i++)
        {
            MPoint pt = rootPoints[i];
            MPointOnMesh pointInfo;
            intersector.getClosestPoint(pt, pointInfo);
            MFloatVector closestNormal = pointInfo.getNormal();
            closestNormal = closestNormal * -1;
            pointArray.append(pt);
            vectorArray.append(closestNormal);
        }
    }
}

MObjectArray XGenHelper::GetSplineBaseMesh()
{
    MObject splineBase = GetSplineBase();
    MObjectArray meshList;
    MFnDependencyNode fnSplineBaseNode(splineBase);
    MString name = fnSplineBaseNode.name();
    MPlug boundMeshPlug = fnSplineBaseNode.findPlug("boundMesh");
    for (unsigned int i = 0; i < boundMeshPlug.numElements(); i++)
    {
        MPlug p = boundMeshPlug.elementByLogicalIndex(i);
        MPlug meshPlug = p.source();
        if (meshPlug.isNull())
            continue;
        meshList.append(meshPlug.node());
    }
    return meshList;
}

MObject XGenHelper::GetSplineBase()
{
    MObject splineBase;
    MFnDependencyNode fnDesc(m_description);
    MPlug rootPlug = fnDesc.findPlug("inSplineData", true);
    MObjectArray splineBaseNodes = GetTypeNodes(rootPlug, "xgmSplineBase", MItDependencyGraph::kUpstream);
    if (splineBaseNodes.length())
        splineBase = splineBaseNodes[0];
    return splineBase;
}

bool XGenHelper::HasBoundMesh()
{
    MObject splineBase = GetSplineBase();
    MFnDependencyNode fnSplineBase(splineBase);
    MPlug boundMeshPlug = fnSplineBase.findPlug("boundMesh", true);
    return (boundMeshPlug.numElements() > 0) ? true : false;
}

MString XGenHelper::GetDescName(bool fullPathName)
{
    MFnDagNode fnDesc(m_description);
    if (fullPathName)
        return fnDesc.fullPathName();
    return fnDesc.name();
}

MString XGenHelper::GetSplineBaseName()
{
    MObject splineBase = GetSplineBase();
    MFnDependencyNode fnSplineBase(splineBase);
    return fnSplineBase.name();
}

int XGenHelper::GetBaseSegment()
{
    MObject splineBase = GetSplineBase();
    MFnDependencyNode fnSplineBase(splineBase);
    MPlug cvCountPlug = fnSplineBase.findPlug("cvCount");
    int cvCount = cvCountPlug.asInt();
    return cvCount;
}

void GetSplineData(string description, SplineData &splineData, unsigned int step, bool debug)
{
    MSelectionList selList;
    selList.clear();
    selList.add(MString(description.c_str()));
    MDagPath descDagPath;
    selList.getDagPath(0, descDagPath);
    MObject descNode;
    selList.getDependNode(0, descNode);

    if (!descDagPath.isValid())
    {
        AiMsgWarning("[%s] : %s is invalid.", kTranslatorName, description.c_str());
    }

    MFnDagNode fnDagNode(descDagPath);
    static const MString sApplyRenderOverrideCmd = "xgmSplineApplyRenderOverride ";
    MGlobal::executeCommand(sApplyRenderOverrideCmd + fnDagNode.partialPathName());

    MObject splineBase;
    GetSplineBaseNode(descNode, splineBase);

    MFnDependencyNode fnSplineBaseNode(splineBase);

    AiMsgDebug("-------------------------------------");
    AiMsgDebug("[%s] : xgmSplineDescription : %s", kTranslatorName, fnDagNode.partialPathName().asChar());
    AiMsgDebug("[%s] : xgmSplineBase : %s", kTranslatorName, fnSplineBaseNode.name().asChar());
    AiMsgDebug("-------------------------------------");

    // Init mesh spline data map
    MPlug boundMeshPlug = fnSplineBaseNode.findPlug("boundMesh", true);

    if (boundMeshPlug.numElements() == 0)
        AiMsgWarning("[%s] : xgmSplineBase : No mesh connect to the %s.boundMesh", kTranslatorName, fnSplineBaseNode.name().asChar());

    std::string data;
    MPlug outPlug = fnDagNode.findPlug("outRenderData");
    MObject outObj = outPlug.asMObject();
    MPxData *outData = MFnPluginData(outObj).data();

    if (outData)
    {
        std::ostringstream opaqueStrm;
        outData->writeBinary(opaqueStrm);
        data = opaqueStrm.str();
    }

    // Compute the padding bytes and number of array elements
    const unsigned int tail = data.size() % sizeof(unsigned int);
    const unsigned int padding = (tail > 0) ? sizeof(unsigned int) - tail : 0;
    const unsigned int nelements = (unsigned int)data.size() / sizeof(unsigned int) + (tail > 0 ? 1 : 0);

    const size_t sampleSize = size_t(nelements) * sizeof(unsigned int) - padding;

    AiMsgDebug("    tail : %s", tail);
    AiMsgDebug("    padding : %s", padding);
    AiMsgDebug("    nelements : %s", nelements);
    AiMsgDebug("    sampleSize : %s", sampleSize);

    double frame;
    MString cmd("currentTime -query");
    MGlobal::executeCommand(cmd, frame);

    XgFnSpline splines;
    // std::stringstream opaqueStrm;
    // opaqueStrm.write(data.c_str(), sampleSize);
    if (!splines.load(std::stringstream(data), sampleSize, (float)frame))
    {
        AiMsgWarning("[%s] : Failed to load sample.", kTranslatorName);
    }

    splines.executeScript();

    for (XgItSpline splineIt = splines.iterator(); !splineIt.isDone(); splineIt.next())
    {
        const unsigned int stride = splineIt.primitiveInfoStride();
        const unsigned int primitiveCount = splineIt.primitiveCount();
        const unsigned int *primitiveInfos = splineIt.primitiveInfos();
        const SgVec3f *positions = splineIt.positions(0);
#if defined(XGEN_SPLINE_API_VERSION) && XGEN_SPLINE_API_VERSION >= XGEN_SPLINE_VERSION_2017_UPDATE3
        const SgVec3f *directions = splineIt.widthDirection(0);
#endif
        AiMsgDebug("    stride : %d", stride);

        AiMsgInfo("    primitiveCount : %d, meshId : %s", primitiveCount, splineIt.boundMeshId());

        string meshId = splineIt.boundMeshId();
        MPlug boundMeshPlug = fnSplineBaseNode.findPlug("boundMesh", true);

        if (boundMeshPlug.numElements() == 0)
            AiMsgWarning("[%s] : xgmSplineBase : No mesh connect to the %s.boundMesh.", kTranslatorName, fnSplineBaseNode.name().asChar());

        for (unsigned int b = 0; b < boundMeshPlug.numElements(); b++)
        {
            MPlug p = boundMeshPlug.elementByLogicalIndex(b);
            int at = p.name().indexW(meshId.c_str());

            if (at < 0)
                continue;

            boundMeshPlug = p;
        }

        if (boundMeshPlug.isNull())
        {
            AiMsgWarning("[%s] : %s.boundMesh plug is Null.", kTranslatorName, fnSplineBaseNode.name().asChar());
            continue;
        }

        MPlug meshPlug = boundMeshPlug.source();
        MFnMesh fnMesh(meshPlug.node());
        string meshName = fnMesh.name().asChar();

        AiMsgInfo("    meshId : %s", boundMeshPlug.name().asChar());

        /*auto it = splineDataMap.find(meshName);

        if (it != splineDataMap.end()) {
        cout << "[XgSplineDataTranslator] : Initial splineDataMap : " << meshName.c_str() << endl;
        SplineData splineData;
        splineDataMap[meshName] = splineData;
        }*/

        for (unsigned int p = 0; p < primitiveCount; p++)
        {
            const unsigned int offset = primitiveInfos[p * stride];
            const unsigned int length = primitiveInfos[p * stride + 1];

            AiMsgDebug("    offset : %d", offset);
            AiMsgDebug("    length : %d", length);

            MPointArray curve_pts;

            for (unsigned int i = 0; i < length; i++)
            {
                SgVec3f p = positions[i + offset];
                MPoint cpt(p[0], p[1], p[2]);
                curve_pts.append(cpt);
            }
            splineData.push_back(curve_pts);
        }
    }

    AiMsgInfo("-------------------------------------");
    AiMsgInfo("  Spline Count : %d", splineData.size());
    AiMsgInfo("-------------------------------------");
}

void GetSplineData(string description, SplineDataMap &splineDataMap, unsigned int step, bool debug)
{
    // SplineDataMap splineDataMap;

    AiMsgInfo("[%s] : GetSplineData() : %s", kTranslatorName, description.c_str());

    MSelectionList selList;
    selList.clear();
    selList.add(MString(description.c_str()));
    MDagPath descDagPath;
    selList.getDagPath(0, descDagPath);
    MObject descNode;
    selList.getDependNode(0, descNode);

    if (!descDagPath.isValid())
    {
        AiMsgWarning("[%s] : %s is invalid.", kTranslatorName, description.c_str());
    }

    MFnDagNode fnDagNode(descDagPath);
    static const MString sApplyRenderOverrideCmd = "xgmSplineApplyRenderOverride ";
    MGlobal::executeCommand(sApplyRenderOverrideCmd + fnDagNode.partialPathName());

    MObject splineBase;
    GetSplineBaseNode(descNode, splineBase);

    MFnDependencyNode fnSplineBaseNode(splineBase);

    AiMsgDebug("-------------------------------------");
    AiMsgDebug("[%s] : xgmSplineDescription : %s", kTranslatorName, fnDagNode.partialPathName().asChar());
    AiMsgDebug("[%s] : xgmSplineBase : %s", kTranslatorName, fnSplineBaseNode.name().asChar());
    AiMsgDebug("-------------------------------------");

    // Init mesh spline data map
    MPlug boundMeshPlug = fnSplineBaseNode.findPlug("boundMesh", true);

    if (boundMeshPlug.numElements() == 0)
        AiMsgWarning("[%s] : xgmSplineBase : No mesh connect to the %s.boundMesh", kTranslatorName, fnSplineBaseNode.name().asChar());

    AiMsgInfo("[%s] : init splineDataMap", kTranslatorName);

    for (unsigned int b = 0; b < boundMeshPlug.numElements(); b++)
    {
        MPlug p = boundMeshPlug.elementByLogicalIndex(b);
        MPlug meshPlug = p.source();
        if (meshPlug.isNull())
            continue;
        MFnMesh fnMesh(meshPlug.node());
        string meshName = fnMesh.name().asChar();
        SplineData splineData;
        splineDataMap[meshName] = splineData;

        AiMsgInfo("[%s] : splineDataMap : %s", kTranslatorName, meshName.c_str());
    }
    AiMsgInfo("-------------------------------------");

    std::string data;
    MPlug outPlug = fnDagNode.findPlug("outRenderData");
    MObject outObj = outPlug.asMObject();
    MPxData *outData = MFnPluginData(outObj).data();

    if (outData)
    {
        std::ostringstream opaqueStrm;
        outData->writeBinary(opaqueStrm);
        data = opaqueStrm.str();
        // cout << data << endl;
    }

    // Compute the padding bytes and number of array elements
    const unsigned int tail = data.size() % sizeof(unsigned int);
    const unsigned int padding = (tail > 0) ? sizeof(unsigned int) - tail : 0;
    const unsigned int nelements = (unsigned int)data.size() / sizeof(unsigned int) + (tail > 0 ? 1 : 0);

    const size_t sampleSize = size_t(nelements) * sizeof(unsigned int) - padding;

    AiMsgDebug("    tail : %s", tail);
    AiMsgDebug("    padding : %s", padding);
    AiMsgDebug("    nelements : %s", nelements);
    AiMsgDebug("    sampleSize : %s", sampleSize);

    XgFnSpline splines;

    double frame;
    MString cmd("currentTime -query");
    MGlobal::executeCommand(cmd, frame);

    // std::stringstream opaqueStrm;
    // opaqueStrm.write(data.c_str(), sampleSize);
    if (!splines.load(std::stringstream(data), sampleSize, (float)frame))
    {
        AiMsgWarning("[%s] Failed to load sample.", kTranslatorName);
    }

    // Process multiplier and density mask
    splines.executeScript();

    for (XgItSpline splineIt = splines.iterator(); !splineIt.isDone(); splineIt.next())
    {
        const unsigned int stride = splineIt.primitiveInfoStride();
        const unsigned int primitiveCount = splineIt.primitiveCount();
        const unsigned int *primitiveInfos = splineIt.primitiveInfos();
        const SgVec3f *positions = splineIt.positions(0);
#if defined(XGEN_SPLINE_API_VERSION) && XGEN_SPLINE_API_VERSION >= XGEN_SPLINE_VERSION_2017_UPDATE3
        const SgVec3f *directions = splineIt.widthDirection(0);
#endif
        AiMsgDebug("    stride : %d, primitiveCount : %d, meshId : %s", stride, primitiveCount, splineIt.boundMeshId());

        string meshId = splineIt.boundMeshId();
        MPlug boundMeshPlug = fnSplineBaseNode.findPlug("boundMesh", true);

        if (boundMeshPlug.numElements() == 0)
            AiMsgWarning("[%s] : xgmSplineBase : No mesh connect to the %s.boundMesh.", kTranslatorName, fnSplineBaseNode.name().asChar());

        for (unsigned int b = 0; b < boundMeshPlug.numElements(); b++)
        {
            MPlug p = boundMeshPlug.elementByLogicalIndex(b);
            int at = p.name().indexW(meshId.c_str());

            if (at < 0)
                continue;

            boundMeshPlug = p;
        }

        if (boundMeshPlug.isNull())
        {
            AiMsgWarning("[%s] : %s.boundMesh plug is Null.", kTranslatorName, fnSplineBaseNode.name().asChar());
            continue;
        }

        MPlug meshPlug = boundMeshPlug.source();
        MFnMesh fnMesh(meshPlug.node());
        string meshName = fnMesh.name().asChar();

        AiMsgDebug("    boundMeshPlug : %s", boundMeshPlug.name().asChar());

        for (unsigned int p = 0; p < primitiveCount; p++)
        {
            const unsigned int offset = primitiveInfos[p * stride];
            const unsigned int length = primitiveInfos[p * stride + 1];

            AiMsgDebug("    offset : %d", offset);
            AiMsgDebug("    length : %d", length);

            MPointArray curve_pts;

            for (unsigned int i = 0; i < length; i++)
            {
                SgVec3f p = positions[i + offset];
                MPoint cpt(p[0], p[1], p[2]);
                curve_pts.append(cpt);
            }
            splineDataMap[meshName].push_back(curve_pts);
        }
    }

    AiMsgInfo("-------------------------------------");

    for (auto it = splineDataMap.begin(); it != splineDataMap.end(); it++)
        AiMsgInfo("    Mesh : %s, Spline Count : %d", it->first.c_str(), it->second.size());

    AiMsgInfo("-------------------------------------");
}

void GetSplineRootPoints(string description, MPointArray &pointArray, unsigned int step, bool debug)
{
    SplineData splineData;
    GetSplineData(description, splineData, step, debug);
    for (unsigned int i = 0; i < splineData.size(); i++)
    {
        MPointArray curve_pts = splineData[i];
        pointArray.append(curve_pts[0]);
    }
}

void GetSplineRootPoints(string description, PointArrayMap &rootPointArrayMap, unsigned int step, bool debug)
{
    AiMsgInfo("[%s] : GetSplineRootPoints() : %s", kTranslatorName, description.c_str());

    SplineDataMap splineDataMap;
    GetSplineData(description, splineDataMap, step, debug);
    for (auto it = splineDataMap.begin(); it != splineDataMap.end(); it++)
    {
        MPointArray points;
        string mesh = it->first;
        SplineData splineData = it->second;

        for (unsigned int i = 0; i < splineData.size(); i++)
        {
            MPointArray curve_pts = splineData[i];
            points.append(curve_pts[0]);
        }
        rootPointArrayMap[mesh] = points;
    }
}
void GetSplineRootNormals(string description, MVectorArray &vectorArray, unsigned int step, bool debug)
{
    MStatus status;

    PointArrayMap rootPointArrayMap;
    GetSplineRootPoints(description, rootPointArrayMap, step, debug);

    for (auto it = rootPointArrayMap.begin(); it != rootPointArrayMap.end(); it++)
    {

        string mesh = it->first;

        MPointArray rootPoints = it->second;

        MSelectionList selList;
        selList.clear();
        selList.add(MString(mesh.c_str()));
        MDagPath dagPath;
        selList.getDagPath(0, dagPath);

        if (!dagPath.isValid())
        {
            MGlobal::displayError(MString(description.c_str()) + " is invalid");
            continue;
        }
        if (!dagPath.hasFn(MFn::kMesh))
        {
            MGlobal::displayError(MString(mesh.c_str()) + " is invalid");
            continue;
        }

        MMeshIntersector intersector;
        MMatrix matrix = dagPath.inclusiveMatrix();
        status = intersector.create(dagPath.node(), matrix);
        if (status)
        {
            for (unsigned i = 0; i < rootPoints.length(); i++)
            {
                MPoint pt = rootPoints[i];
                MPointOnMesh pointInfo;
                intersector.getClosestPoint(pt, pointInfo);
                MFloatVector closestNormal = pointInfo.getNormal();
                closestNormal = closestNormal * -1;
                vectorArray.append(closestNormal);
            }
        }
    }
}

void GetSplineRootNormals(string description, VectorArrayMap &rootNormalArrayMap, unsigned int step, bool debug)
{
    MStatus status;

    PointArrayMap rootPointArrayMap;
    GetSplineRootPoints(description, rootPointArrayMap, step, debug);

    for (auto it = rootPointArrayMap.begin(); it != rootPointArrayMap.end(); it++)
    {
        MVectorArray normals;
        string mesh = it->first;

        MPointArray rootPoints = it->second;

        MSelectionList selList;
        selList.clear();
        selList.add(MString(mesh.c_str()));
        MDagPath dagPath;
        selList.getDagPath(0, dagPath);

        if (!dagPath.isValid())
        {
            MGlobal::displayError(MString(description.c_str()) + " is invalid");
            continue;
        }
        if (!dagPath.hasFn(MFn::kMesh))
        {
            MGlobal::displayError(MString(mesh.c_str()) + " is invalid");
            continue;
        }

        MMeshIntersector intersector;
        MMatrix matrix = dagPath.inclusiveMatrix();
        status = intersector.create(dagPath.node(), matrix);
        if (status)
        {
            for (unsigned i = 0; i < rootPoints.length(); i++)
            {
                MPoint pt = rootPoints[i];
                MPointOnMesh pointInfo;
                intersector.getClosestPoint(pt, pointInfo);
                MFloatVector closestNormal = pointInfo.getNormal();
                closestNormal = closestNormal * -1;
                normals.append(closestNormal);
            }
            rootNormalArrayMap[mesh] = normals;
        }
    }
}

void GetSplineRootPointsAndNormals(string description, MPointArray &pointArray, MVectorArray &vectorArray, unsigned int step, bool debug)
{
    AiMsgInfo("[%s] : getSplineRootPointsAndNormals() : %s", kTranslatorName, description.c_str());

    MStatus status;

    PointArrayMap rootPointArrayMap;
    GetSplineRootPoints(description, rootPointArrayMap, step, debug);

    for (auto it = rootPointArrayMap.begin(); it != rootPointArrayMap.end(); it++)
    {

        string mesh = it->first;

        MPointArray rootPoints = it->second;

        MSelectionList selList;
        selList.clear();
        selList.add(MString(mesh.c_str()));
        MDagPath dagPath;
        selList.getDagPath(0, dagPath);

        if (!dagPath.isValid())
        {
            MGlobal::displayError(MString(description.c_str()) + " is invalid");
            continue;
        }
        if (!dagPath.hasFn(MFn::kMesh))
        {
            MGlobal::displayError(MString(mesh.c_str()) + " is invalid");
            continue;
        }

        MMeshIntersector intersector;
        MMatrix matrix = dagPath.inclusiveMatrix();
        status = intersector.create(dagPath.node(), matrix);
        if (status)
        {
            for (unsigned i = 0; i < rootPoints.length(); i++)
            {
                MPoint pt = rootPoints[i];
                MPointOnMesh pointInfo;
                intersector.getClosestPoint(pt, pointInfo);
                MFloatVector closestNormal = pointInfo.getNormal();
                closestNormal = closestNormal * -1;
                pointArray.append(pt);
                vectorArray.append(closestNormal);
            }
        }
    }
}

void GetSplineBaseNode(MObject &desc, MObject &splineBase)
{
    MFnDependencyNode depNode(desc);
    MPlug rootPlug = depNode.findPlug("inSplineData", true);
    MItDependencyGraph dgIt(rootPlug,
                            MFn::kInvalid,
                            MItDependencyGraph::kUpstream,
                            MItDependencyGraph::kDepthFirst,
                            MItDependencyGraph::kPlugLevel);
    dgIt.next();
    while (!dgIt.isDone())
    {
        MPlug plug = dgIt.previousPlug();
        MObject node = plug.node();
        if (node.isNull())
            continue;
        MFnDependencyNode depNode(node);
        if (depNode.typeName() == "xgmSplineBase")
        {
            splineBase = node;
            break;
        }
        dgIt.next();
    }
}

MSelectionList GetSplineBaseMesh(MObject splineBase)
{
    MSelectionList meshList;
    MFnDependencyNode fnSplineBaseNode(splineBase);
    MString name = fnSplineBaseNode.name();
    MPlug boundMeshPlug = fnSplineBaseNode.findPlug("boundMesh");
    for (unsigned int i = 0; i < boundMeshPlug.numElements(); i++)
    {
        MPlug p = boundMeshPlug.elementByLogicalIndex(i);
        MString n = p.name().asChar();
        MPlug meshPlug = p.source();
        if (meshPlug.isNull())
            continue;
        meshList.add(meshPlug.node());
    }
    return meshList;
}

int GetSplineBaseSegement(MObject splineBase)
{
    MFnDependencyNode fnSplineBaseNode(splineBase);
    MPlug cvCountPlug = fnSplineBaseNode.findPlug("cvCount");
    int cvCount = cvCountPlug.asInt();
    return cvCount;
}