#pragma once
#include "Mesh.h"

using namespace std;

MeshClass::MeshClass()
{

}

//std::map<int, MeshClass::WeightInfo> MeshClass::GetWeightData()
//{
//    MS status;
//    map<int, WeightInfo> weightMap;
//
//	MItDependencyNodes it(MFn::kSkinClusterFilter);
//
//	while (!it.isDone()) {
//
//        MObject object = it.thisNode(&status);
//        if (status != MS::kSuccess) {
//            MGlobal::displayError(MString() + " it.thisNode() ERROR: " + status.errorString());
//            break;
//        }
//        MFnSkinCluster skinCluster(object, &status);
//        if (status != MS::kSuccess) {
//            MGlobal::displayError(MString() + "skinCluster() ERROR: " + status.errorString());
//            break;
//        }
//        MDagPathArray influences;
//
//        unsigned int nrOfInfluences = skinCluster.influenceObjects(influences,&status);
//        if (status != MS::kSuccess) {
//            MGlobal::displayError(MString() + "skinCluster.influenceObjects() ERROR: " + status.errorString());
//            break;
//        }
//
//        unsigned int index;
//        index = skinCluster.indexForOutputConnection(0,&status);
//        if (status != MS::kSuccess) {
//            MGlobal::displayError(MString() + "skinCluster.indexForOutputConnection() ERROR: " + status.errorString());
//            break;
//        }
//        MDagPath skinPath;
//        status = skinCluster.getPathAtIndex(index, skinPath);
//        if (status != MS::kSuccess) {
//            MGlobal::displayError(MString() + "skinCluster.getPathAtIndex() ERROR: " + status.errorString());
//            break;
//        }
//
//        MItGeometry geomIter(skinPath);
//        //for (unsigned int i = 0; i < nrOfInfluences; i++) {
//        //    MGlobal::displayInfo(MString() + " Influence object name: " + influences[i].partialPathName().asChar());
//        //}
//        WeightInfo weightInfo;
//
//        while (!geomIter.isDone()) {
//            MObject comp = geomIter.component(&status);
//            if (status != MS::kSuccess) {
//                MGlobal::displayError(MString() + "geomIter.component() ERROR: " + status.errorString());
//                break;
//            }
//            MFloatArray weights;
//            unsigned int influenceCount;
//            status = skinCluster.getWeights(skinPath, comp, weights, influenceCount);
//            if (status != MS::kSuccess) {
//                MGlobal::displayError(MString() + "skinCluster.getWeights() ERROR: " + status.errorString());
//                break;
//            }
//            MFnDependencyNode test(comp);
//            unsigned int nrOfWeights = 0;
//
//            for (unsigned int j = 0; j < weights.length() && nrOfWeights != 4; j++) {
//                if (weights[j] > 0.00001) {
//                    weightInfo.BoneWeights[nrOfWeights] = weights[j];
//                    weightInfo.BoneIndices[nrOfWeights] = j;
//                    nrOfWeights++;
//                }
//            }
//
//            float totalWeight = 0.0f;
//            for (unsigned int i = 0; i < 4; i++) {
//                totalWeight += weightInfo.BoneWeights[i];
//            }
//            for (unsigned int i = 0; i < 4; i++) {
//                weightInfo.BoneWeights[i] /= totalWeight;
//            }
//            weightMap[geomIter.index()] = weightInfo;
//
//            geomIter.next();
//        }
//        it.next();
//	}
//    return weightMap;
//}

Mesh MeshClass::GetMeshData(MObjectArray object, bool collision)
{
    MS status;
    Mesh newMesh;
	newMesh.isCollison = collision;
    vector<VertexLayout>& vertexList = newMesh.Vertices;
    map<string, vector<int>>& indexLists = newMesh.Indices;
    for (int ObjectID = 0; ObjectID < object.length(); ObjectID++) {
        if (!object[ObjectID].hasFn(MFn::kMesh))
            continue;

		
        MObject node = object[ObjectID];
        MFnDependencyNode thisNode(node);
        MPlugArray connections;
        thisNode.findPlug("inMesh").connectedTo(connections, true, true);
        MPlug weightList, weights;
        MObject weightListObject;

		if (!collision) {
			for (unsigned int i = 0; i < connections.length(); i++) {
				if (connections[i].node().apiType() == MFn::kSkinClusterFilter) {
					MFnSkinCluster skinCluster(connections[i].node());
					weightList = skinCluster.findPlug("weightList", &status);
					weightListObject = weightList.attribute();
					weights = skinCluster.findPlug("weights");
					newMesh.hasSkin = true;
					break;
				}
			}
		}

        // In here, we retrieve triangulated polygons from the mesh
        MFnMesh mesh(object[ObjectID]);
        MDagPathArray dagPaths;
        status = MDagPath::getAllPathsTo(object[ObjectID], dagPaths);
        if (status != MS::kSuccess) {
            MGlobal::displayError(MString() + "MDagPath::getAllPathsTo() ERROR: " + status.errorString());
            break;
        }

        for (int pathID = 0; pathID < dagPaths.length(); pathID++) {
            MDagPath thisMeshPath(dagPaths[pathID]);

            MMatrix transformMatrix = thisMeshPath.inclusiveMatrix(&status);
            if (status != MS::kSuccess) {
                MGlobal::displayError(MString() + "thisMeshPath.inclusiveMatrix() ERROR: " + status.errorString() + " for " + thisMeshPath.fullPathName());
                break;
            }

            map<unsigned int, vector<unsigned int>> vertexToIndex;;

            MIntArray intdexOffsetVertexCount, vertices, triangleList;
            MPointArray dummy;
            unsigned int vertexIndex;
            MVector normal;
            MPoint pos;
            float2 UV;
            double biTangent[3];
            double biNormal[3];
            MFloatVectorArray Tangents;
            MFloatVectorArray biNormals;

            MObjectArray shaderList;
            MIntArray shaderIndexList;
            status = mesh.getConnectedShaders(0, shaderList, shaderIndexList);
            if (status != MS::kSuccess) {
                MGlobal::displayError(MString() + "mesh.getConnectedShaders() ERROR: " + status.errorString()  + " for " + thisMeshPath.fullPathName());
                break;
            }

            if (shaderList.length() == 0) {
                MGlobal::displayError(MString() + "Object: \"" + thisMeshPath.fullPathName() + "\" have no material and will not be exported");
                break;
            }
            map<string, vector<int>> materialFaceIDs;

            MPlugArray plugArray;
            for (int i = 0; i < shaderIndexList.length(); i++) {
                MFnDependencyNode shader(shaderList[shaderIndexList[i]]);
                MPlug p_Plug = shader.findPlug("surfaceShader", status);
                if (status != MS::kSuccess) {
                    MGlobal::displayError(MString() + "shader.findPlug(\"surfaceShader\") ERROR: " + status.errorString()  + " for " + thisMeshPath.fullPathName());
                    continue;
                }
                if (p_Plug.connectedTo(plugArray, true, false, &status)) {
                    if (status != MS::kSuccess) {
                        MGlobal::displayError(MString() + "p_Plug.connectedTo() ERROR in if: " + status.errorString()  + " for " + thisMeshPath.fullPathName());
                        continue;
                    }
                    MFnDependencyNode node = plugArray[0].node();
                    materialFaceIDs[node.name().asChar()].push_back(i);
                }
                if (status != MS::kSuccess) {
                    MGlobal::displayError(MString() + "p_Plug.connectedTo() ERROR: " + status.errorString()  + " for " + thisMeshPath.fullPathName());
                    continue;
                }
            }

           // map<int, WeightInfo> vertexWeights = GetWeightData();
            status = mesh.getTangents(Tangents, MSpace::kObject);
            if (status != MS::kSuccess) {
                MGlobal::displayError(MString() + "mesh.getTangents ERROR: " + status.errorString()  + " for " + thisMeshPath.fullPathName());
                continue;
            }
            status = mesh.getBinormals(biNormals, MSpace::kObject);
            if (status != MS::kSuccess) {
                MGlobal::displayError(MString() + "mesh.getBinormals ERROR: " + status.errorString()  + " for " + thisMeshPath.fullPathName());
                continue;
            }
            if(Tangents.length() == 0 || biNormals.length() == 0){
                MGlobal::displayError(MString() + "Unknown ERROR with " + thisMeshPath.fullPathName());
                continue;
            }
            MItMeshFaceVertex faceVert(object[ObjectID]);
            int intDummy = 0;

            MItMeshPolygon meshPolyIter(object[ObjectID]);
            MFloatPointArray positions;

            mesh.getPoints(positions);

            for (auto aMaterial : materialFaceIDs) {
                for (auto faceID : aMaterial.second) {
                    vector<array<unsigned int, 2>> localVertexToGlobalIndex;

                    status = meshPolyIter.setIndex(faceID, intDummy);
                    if (status != MS::kSuccess) {
                        MGlobal::displayError(MString() + " meshPolyIter.setIndex() ERROR: " + status.errorString() +  " for faceID " + faceID + " in mesh " + thisMeshPath.fullPathName());
                        break;
                    }

                    status = meshPolyIter.getVertices(vertices);
                    if (status != MS::kSuccess) {
                        MGlobal::displayError(MString() + "  meshPolyIter.getVertices() ERROR: " + status.errorString() +  " for faceID " + faceID + " in mesh " + thisMeshPath.fullPathName());
                        break;
                    }

                    status = meshPolyIter.getTriangles(dummy, triangleList);
                    if (status != MS::kSuccess) {
                        MGlobal::displayError(MString() + "  meshPolyIter.getTriangles() ERROR: " + status.errorString() +  " for faceID " + faceID + " in mesh " + thisMeshPath.fullPathName());
                        break;
                    }
                    //MGlobal::displayInfo("Befor Second Loop");
                    for (unsigned int i = 0; i < vertices.length(); i++) {
                        VertexLayout thisVertex;

                        vertexIndex = meshPolyIter.vertexIndex(i, &status);
                        if (status != MS::kSuccess) {
                            MGlobal::displayError(MString() + "  meshPolyIter.vertexIndex() ERROR: " + status.errorString() + "for local vertex " + i + " in " + faceID + " in mesh " + thisMeshPath.fullPathName());
                            break;
                        }

                        status = faceVert.setIndex(meshPolyIter.index(), i, intDummy, intDummy);
                        if (status != MS::kSuccess) {
                            MGlobal::displayError(MString() + "faceVert.setIndex() ERROR: " + status.errorString() + "for local vertex " + i + " in " + faceID + " in mesh " + thisMeshPath.fullPathName());
                            break;
                        }
                        //MGlobal::displayInfo("In Second Loop");
                        //pos = faceVert.position(MSpace::kTransform);
                        //mesh.getPoint(vertexIndex, pos, MSpace::kPostTransform);
                        pos = positions[vertexIndex];
                        pos = pos * transformMatrix;

                        thisVertex.Pos[0] = pos.x;
                        thisVertex.Pos[1] = pos.y;
                        thisVertex.Pos[2] = pos.z;
						thisVertex.isCollision = collision;

						if (!collision) {
							status = faceVert.getNormal(normal, MSpace::kObject);
							if (status != MS::kSuccess) {
								MGlobal::displayError(MString() + "faceVert.getNormal() ERROR: " + status.errorString() + "for local vertex " + i + " in " + faceID + " in mesh " + thisMeshPath.fullPathName());
								break;
							}

							thisVertex.Normal[0] = normal[0];
							thisVertex.Normal[1] = normal[1];
							thisVertex.Normal[2] = normal[2];

							MFloatVector Tangent = Tangents[faceVert.tangentId()];
							//MVector tmp = faceVert.getTangent(MSpace::kObject, NULL);
							//tmp.get(biTangent);
							thisVertex.Tangent[0] = Tangent[0];
							thisVertex.Tangent[1] = Tangent[1];
							thisVertex.Tangent[2] = Tangent[2];

							MFloatVector biNormal = biNormals[faceVert.tangentId()];
							//faceVert.getBinormal().get(biNormal);
							thisVertex.BiNormal[0] = biNormal[0];
							thisVertex.BiNormal[1] = biNormal[1];
							thisVertex.BiNormal[2] = biNormal[2];

							status = faceVert.getUV(UV);
							if (status != MS::kSuccess) {
								MGlobal::displayError(MString() + " faceVert.getUV() ERROR: " + status.errorString() + "for local vertex " + i + " in " + faceID + " in mesh " + thisMeshPath.fullPathName());
								break;
							}
							thisVertex.Uv[0] = UV[0];
							thisVertex.Uv[1] = UV[1];

                        
							if (newMesh.hasSkin) {
								thisVertex.useWeights = true;
								float totalWeight = 0.0f;
								unsigned int totalBones = 0;
								MIntArray jointIDs /* ??? */;
								weights.selectAncestorLogicalIndex(vertexIndex, weightListObject);
								weights.getExistingArrayAttributeIndices(jointIDs);
								for (unsigned int i = 0; i < jointIDs.length() && i < 4; i++) {
									if (weights[i].asFloat() > 0.001f) {
										thisVertex.BoneIndices[totalBones] = jointIDs[i];
										thisVertex.BoneWeights[totalBones] = weights[i].asFloat();
										totalWeight = totalWeight + weights[i].asFloat();
										totalBones++;
									}
								}
                           
								for (unsigned int i = 0; i < 4; i++) {
									//thisVertex.BoneWeights[i] = thisVertex.BoneWeights[i] / totalWeight;
								}
							}
						}
                        //float totalWeight = thisVertex.BoneWeights[0] + thisVertex.BoneWeights[1] + thisVertex.BoneWeights[2] + thisVertex.BoneWeights[3];
                        //if (totalWeight > 0.0001f) {
                        //    thisVertex.BoneWeights[0] /= totalWeight;
                        //    thisVertex.BoneWeights[1] /= totalWeight;
                        //    thisVertex.BoneWeights[2] /= totalWeight;
                        //    thisVertex.BoneWeights[3] /= totalWeight;
                        //}

                        std::vector<VertexLayout>::iterator it = std::find(vertexList.begin(), vertexList.end(), thisVertex);
                        array<unsigned int, 2> tmp;
                        if (it != vertexList.end()) {
                            tmp[0] = vertexIndex;
                            tmp[1] = it - vertexList.begin();
                            localVertexToGlobalIndex.push_back(tmp);
                        } else {
                            tmp[0] = vertexIndex;
                            tmp[1] = vertexList.size();
                            localVertexToGlobalIndex.push_back(tmp);
                            vertexList.push_back(thisVertex);
                        }
                        //MGlobal::displayInfo(MString() + "localVertexToGlobalIndex[localVertexToGlobalIndex.size()-1]: " + localVertexToGlobalIndex[localVertexToGlobalIndex.size()-1][0] + " " + localVertexToGlobalIndex[localVertexToGlobalIndex.size()-1][1]);
                        //cout << "Pos: " << thisVertex.Pos[0] << "/" << thisVertex.Pos[1] << "/" << thisVertex.Pos[2] << endl;
                        //cout << "Normals: " << thisVertex.Normal[0] << "/" << thisVertex.Normal[1] << "/" << thisVertex.Normal[2] << endl;
                        //cout << "Bi-Normals: " << thisVertex.BiNormal[0] << "/" << thisVertex.BiNormal[1] << "/" << thisVertex.BiNormal[2] << endl;
                        //cout << "Bi-Tangents: " << thisVertex.BiTangent[0] << "/" << thisVertex.BiTangent[1] << "/" << thisVertex.BiTangent[2] << endl;
                        //cout << "UV: " << thisVertex.Uv[0] << "/" << thisVertex.Uv[1] << endl;
                    }
                    for (unsigned int i = 0; i < triangleList.length(); i++) {
                        unsigned int  k = 0;
                        if (localVertexToGlobalIndex.size() > 0) {
                            //MGlobal::displayInfo(MString() + "triangleList[i] : " + triangleList[i]);
                            while (localVertexToGlobalIndex[k][0] != triangleList[i] && k < localVertexToGlobalIndex.size()) {
                                k++;
                            }
                            //MGlobal::displayInfo(MString() + "localVertexToGlobalIndex[k] : " + localVertexToGlobalIndex[k][0] + " " + localVertexToGlobalIndex[k][1]);
                            indexLists[aMaterial.first.c_str()].push_back(localVertexToGlobalIndex[k][1]);
                        }
                    }
                }
                //  MGlobal::displayInfo( MString() + "localVertexToGlobalIndex.size(): " + localVertexToGlobalIndex.size());
                //    if (localVertexToGlobalIndex.size() > 0) {
                //        MGlobal::displayInfo(MString() + "triangleList.length(): " + triangleList.length());
                //        for (unsigned int i = triangleList.length() - 1; i >= 0; i--) {
                //            MGlobal::displayInfo(MString() + "i: " + i);
                //            unsigned int  k = localVertexToGlobalIndex.size() - 1;
                //            MGlobal::displayInfo(MString() + "triangleList[i] : " + triangleList[i]);
                //            while (localVertexToGlobalIndex[k] != triangleList[i] && k >= 0) {
                //                MGlobal::displayInfo(MString() + "k: " + k);
                //                k--;
                //            }
                //            MGlobal::displayInfo(MString() + "localVertexToGlobalIndex[k] : " + localVertexToGlobalIndex[k]);
                //            indexList.push_back(indexOffset + k);
                //        }
                //    }
            }
        }
    }

    int totalIndices = 0;
    for (auto aList : newMesh.Indices) {
        totalIndices += aList.second.size();
    }
    newMesh.NumIndices = totalIndices;
    newMesh.NumVertices = newMesh.Vertices.size();

    return newMesh;
}

MeshClass::~MeshClass()
{

}