#include <maya/MIOStream.h>
#include <maya/MPxGeometryFilter.h>
#include <maya/MItGeometry.h>
#include <maya/MFnPlugin.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>
#include <maya/MPoint.h>
#include <maya/MTimer.h>
#include <maya/MFnMesh.h>
#include <maya/MPointArray.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MFnMeshData.h>
#include <maya/MMeshIntersector.h>
#include <maya/MThreadUtils.h>
#include <tbb/tbb.h>
#define MCheckStatus(status,message)    \
    if( MStatus::kSuccess != status ) { \
        cerr << message << "\n";        \
        return status;                  \
    }
{
public:
                        splatDeformer();
    virtual             ~splatDeformer();
    static  void*       creator();
    
    
public:
    
private:
    
    
    
};
MTypeId splatDeformer::id( 0x8104D );
 
MObject splatDeformer::deformingMesh;
 
MObject splatDeformer::parallelEnabled;
 
splatDeformer::splatDeformer() {}
splatDeformer::~splatDeformer() {}
void* splatDeformer::creator()
{
    return new splatDeformer();
}
MStatus splatDeformer::initialize()
 
{
    
    
    status = addAttribute( deformingMesh );
    MCheckStatus(status, "ERROR in addAttribute(deformingMesh)\n");
    status = addAttribute( parallelEnabled );
    MCheckStatus(status, "ERROR in addAttribute(parallelEnabled)\n");
    status = attributeAffects( deformingMesh, outputGeom );
    MCheckStatus(status, "ERROR in attributeAffects(deformingMesh)\n");
    status = attributeAffects( parallelEnabled, outputGeom );
    MCheckStatus(status, "ERROR in attributeAffects(parallelEnabled)\n");
}
{
    MObject thisNode = this->thisMObject();
 
    {
        printf("Ignoring requested plug\n");
        return status;
    }
    
    
    
    
    {
        
        
        
        
        MPlug inPlug(thisNode,input);
 
        MCheckStatus(status, "ERROR getting input mesh\n");
    
        {
            do
            {
                MCheckStatus(status, "ERROR getting input mesh element\n");
                MCheckStatus(status, "ERROR getting input mesh element index\n");
                computeOneOutput( inputIndex, data, hInputElement );
        }
    }
    else
    {
        
        
        
        
        MPlug inPlug(thisNode,input);
 
        inPlug.selectAncestorLogicalIndex(plug.
logicalIndex(),input);
        MCheckStatus(status, "ERROR getting input mesh\n");
    
    }
    return status;
}
template<typename Value>
class cancelable_range {
    tbb::blocked_range<Value> my_range;
    volatile bool& my_stop;
public:
    
    cancelable_range( int begin, int end, int grainsize, volatile bool& stop ) :
        my_range(begin,end,grainsize),
        my_stop(stop)
    {}
    cancelable_range( cancelable_range& r, tbb::split ) :
        my_range(r.my_range,tbb::split()),
        my_stop(r.my_stop)
    {}
    cancelable_range & operator=( const cancelable_range & );
    void cancel() const {my_stop=true;}
    bool empty() const {return my_stop || my_range.empty();}
    bool is_divisible() const {return !my_stop && my_range.is_divisible();}
    Value begin() const {return my_range.begin();}
    Value end() const {return my_stop ? my_range.begin() : my_range.end();}
};
{
    MObject thisNode = this->thisMObject();
 
    
    MPlug outPlug(thisNode, outputGeom);
 
    outPlug.selectAncestorLogicalIndex(index, outputGeom);
    
    {
        printf("Incorrect input geometry type\n");
    }
    
    unsigned int groupId = hGroup.
asLong();
 
    
    MCheckStatus(status, "ERROR getting deforming mesh\n");
    {
        printf(
"Incorrect deformer geometry type %d\n", deformData.
type());
    }
    
    {
        printf("Incorrect output mesh type\n");
    }
    
    {
        printf("Input surface is NULL\n");
    }
    MCheckStatus(status, "ERROR setting points\n");
    
    
    
    iter.allPositions(verts);
    unsigned int nPoints = verts.
length();
 
    
    volatile bool failed = false;
    bool parallelEnabled = (bool) parallelEnabledData.
asBool(); 
 
    if( parallelEnabled )
    {
        bool stop = false;
        tbb::parallel_for( cancelable_range<unsigned int>(0,nPoints,nPoints/1000,stop),
                           [&](const cancelable_range<unsigned int>& r)
        {
            
            
            for(unsigned int i = r.begin(); i < r.end(); ++i)
            {
                
                
                
                
                
                {
                    failed = true;
                    r.cancel();
                }
                else
                {
                    
                    
                }
            }
        });
    }
    else
    {
        for(int i=0; i<nPoints; ++i )
        {
            
            {
                failed = true;
                break;
            }
        }
    }
    timer.
endTimer(); printf(
"Runtime for %s loop %f\n", parallelEnabled ? 
"parallel" : 
"serial", timer.
elapsedTime() );
    
    iter.setAllPositions(verts);
    
    
    
    
    
    
    
    
    if(failed)
    {
        printf("Closest point failed\n");
    }
    return status;
}
{
    MFnPlugin plugin( obj, PLUGIN_COMPANY, 
"1.0", 
"Any");
 
    result = plugin.registerNode( "splatDeformer", splatDeformer::id, splatDeformer::creator,
    return result;
}
{
    result = plugin.deregisterNode( splatDeformer::id );
    return result;
}