#include "testNobjectNode.h"
#include <maya/MIOStream.h>
#include <maya/MPlug.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>
#include <maya/MGlobal.h>
#include <maya/MTime.h>
#include <maya/MFnMesh.h>
#include <maya/MFnMeshData.h>
#include <maya/MFnUnitAttribute.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MFnIntArrayData.h>
#include <maya/MFnComponentListData.h>
#include <maya/MFnSingleIndexedComponent.h>
#include <maya/MDagPath.h>
#include <maya/MPoint.h>
#include <maya/MFloatPointArray.h>
#include <maya/MItMeshVertex.h>
#include <maya/MFnPlugin.h>
#include <maya/MFnNObjectData.h>
#include "stdio.h"
const MTypeId testNobjectNode::id( 0x85003 );
MObject testNobjectNode::startState;
MObject testNobjectNode::currentState;
MObject testNobjectNode::currentTime;
MObject testNobjectNode::nextState;
MObject testNobjectNode::inputGeom;
MObject testNobjectNode::outputGeom;
inline void statCheck( MStatus stat, MString msg )
{
        if ( !stat )
        {
                cout<<msg<<"\n";
        }
}
testNobjectNode::testNobjectNode()
{
    
    fNObject.createNCloth();
}
MStatus testNobjectNode::compute(const MPlug &plug, MDataBlock &data)
{
        MStatus stat;
        if ( plug == outputGeom )
        {
                MObject inMeshObj = data.inputValue(inputGeom).asMesh();
                
        cerr<<"pull on outputGeom\n";
                MFnMeshData meshDataFn;
        MFnMesh inputMesh(inMeshObj);
                MObject newMeshObj = meshDataFn.create();
                MFnMesh newMeshFn;
                newMeshFn.copy( inMeshObj, newMeshObj );        
        
        
        data.inputValue(currentTime).asTime();
                
        
        
        
                MObject nextNObj = data.inputValue(nextState).data();
        MFloatPointArray pts;        
        
        
        fNObject.getPositions(pts);
        if(pts.length() == (unsigned int) inputMesh.numVertices()) {
            newMeshFn.setPoints(pts);
        }
                newMeshFn.setObject( newMeshObj );
                data.outputValue(outputGeom).set(newMeshObj);
                data.setClean(plug);
        }
        if ( plug == currentState )
        {               
        MFnNObjectData outputData;
        MObject mayaNObjectData = outputData.create();
        outputData.setObject(mayaNObjectData);
        
        outputData.setObjectPtr(&fNObject);        
        outputData.setCached(false);
        MDataHandle currStateOutputHandle = data.outputValue(currentState);
        currStateOutputHandle.set(outputData.object());
          
        cerr<<"pull on currentState\n";
        }
        if ( plug == startState )
        {
            int ii,jj;
        
        MObject inMeshObj = data.inputValue(inputGeom).asMesh();
                                
        MFnMesh inputMesh(inMeshObj);           
        int numPolygons = inputMesh.numPolygons();
        int * faceVertCounts = new int[numPolygons];
                
        
        int facesArrayLength = 0;
        for(ii=0;ii<numPolygons;ii++) {
            MIntArray verts;
            inputMesh.getPolygonVertices(ii,verts);
            faceVertCounts[ii] = verts.length();
            facesArrayLength += verts.length();
        }
        int * faces = new int[facesArrayLength];
        int currIndex = 0;
        for(ii=0;ii<numPolygons;ii++) {
            MIntArray verts;
            inputMesh.getPolygonVertices(ii,verts);
            for(jj=0;jj<(int)verts.length();jj++) {
                faces[currIndex++] = verts[jj];
            }
        }
        int numEdges = inputMesh.numEdges();
        int * edges = new int[2*numEdges];
        currIndex = 0;
        for(ii=0;ii<numEdges;ii++) {
            int2 edge;
            inputMesh.getEdgeVertices(ii,edge);
            edges[currIndex++] = edge[0];
            edges[currIndex++] = edge[1];
        }
        
        
        fNObject.setTopology(numPolygons, faceVertCounts, faces,numEdges, edges );
        delete[] faceVertCounts;
        delete[] faces;
        delete[] edges;        
        unsigned int numVerts = 0;
        numVerts = inputMesh.numVertices();        
        MFloatPointArray vertexArray;
        inputMesh.getPoints(vertexArray);
        fNObject.setPositions(vertexArray,true);
        MFloatPointArray velocitiesArray;
        velocitiesArray.setLength(numVerts);
        for(ii=0;ii<(int)numVerts;ii++) {
            velocitiesArray[ii].x = 0.0f;
            velocitiesArray[ii].y = 0.0f;
            velocitiesArray[ii].z = 0.0f;
            velocitiesArray[ii].w = 0.0f;
        }
        fNObject.setVelocities(velocitiesArray);
        fNObject.setThickness(0.05f);
        fNObject.setInverseMass(1.0f);
        fNObject.setBounce(0.0f);
        fNObject.setFriction(0.1f);
        fNObject.setDamping(0.0f);
        fNObject.setBendResistance(0.0f);
        fNObject.setMaxIterations(100);        
        fNObject.setMaxSelfCollisionIterations(100);
        fNObject.setStretchAndCompressionResistance(20.0f,10.0f);
        fNObject.setSelfCollisionFlags(false);
        fNObject.setCollisionFlags(true);                   
        
        MFnNObjectData outputData;
        MObject mayaNObjectData = outputData.create();
        outputData.setObject(mayaNObjectData);
        
        outputData.setObjectPtr(&fNObject);        
        outputData.setCached(false);
        MDataHandle startStateOutputHandle = data.outputValue(startState);
        startStateOutputHandle.set(outputData.object());
        cerr<<"pull on startState\n";
        }
        else {
                stat = MS::kUnknownParameter;
        }
        return stat;
}
MStatus testNobjectNode::initialize()
{
        MStatus stat;
        MFnTypedAttribute tAttr;
        nextState = tAttr.create("nextState", "nxs", MFnData::kNObject, MObject::kNullObj, &stat );
        statCheck(stat, "failed to create nextState");
        tAttr.setWritable(true);
        tAttr.setStorable(true);
        tAttr.setHidden(true);
        inputGeom = tAttr.create("inputGeom", "ing", MFnData::kMesh, MObject::kNullObj, &stat );
        statCheck(stat, "failed to create inputGeom");
        tAttr.setWritable(true);
        tAttr.setStorable(true);
        tAttr.setHidden(true);
        currentState = tAttr.create("currentState", "cus", MFnData::kNObject, MObject::kNullObj, &stat );
        statCheck(stat, "failed to create currentState");
        tAttr.setWritable(true);
        tAttr.setStorable(false);
        tAttr.setHidden(true);
        startState = tAttr.create( "startState", "sts", MFnData::kNObject, MObject::kNullObj, &stat );
        statCheck(stat, "failed to create startState");
        tAttr.setWritable(true);
        tAttr.setStorable(false);
        tAttr.setHidden(true);
        outputGeom = tAttr.create("outputGeom", "outg", MFnData::kMesh, MObject::kNullObj, &stat );
        statCheck(stat, "failed to create outputGeom");
        tAttr.setWritable(true);
        tAttr.setStorable(false);
        tAttr.setHidden(true);
        MFnUnitAttribute uniAttr;
        currentTime = uniAttr.create( "currentTime", "ctm" , MFnUnitAttribute::kTime,  0.0, &stat  );    
        addAttribute(inputGeom);
        addAttribute(outputGeom);
        addAttribute(currentTime);
        addAttribute(startState);
        addAttribute(currentState);
        addAttribute(nextState);
        
        attributeAffects(inputGeom, outputGeom);
        attributeAffects(nextState, outputGeom);
        attributeAffects(inputGeom, startState);
        attributeAffects(currentTime, outputGeom);
        attributeAffects(currentTime, currentState);    
    attributeAffects(currentTime, startState);    
        return MStatus::kSuccess;
}
MStatus initializePlugin ( MObject obj )
{
        MStatus   status;
        MFnPlugin plugin(obj, "Autodesk - nCloth Prototype 5", "8.5", "Any");
        status = plugin.registerNode ( "testNobjectNode", testNobjectNode::id,testNobjectNode ::creator, testNobjectNode::initialize );
        if ( !status )
        {
                status.perror("registerNode");
                return status;
        }
        return status;
}
MStatus uninitializePlugin ( MObject obj )
{
        MStatus   status;
        MFnPlugin plugin(obj);
        status = plugin.deregisterNode(testNobjectNode::id);
        if ( !status )
        {
                status.perror("deregisterNode");
                return status;
        }
        return status;
}