#include <string.h>
#include <maya/MIOStream.h>
#include <math.h>
#include <maya/MPxNode.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MFnPlugin.h>
#include <maya/MFnNurbsCurve.h>
#include <maya/MFnNurbsCurveData.h>
#include <maya/MString.h>
#include <maya/MTypeId.h>
#include <maya/MPlug.h>
#include <maya/MPointArray.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>
#include <maya/MArrayDataHandle.h>
#include <maya/MArrayDataBuilder.h>
class multiCurve : public MPxNode
{
public:
                                                multiCurve() {};
        virtual                         ~multiCurve();
        virtual MStatus         compute( const MPlug& plug, MDataBlock& data );
        static  void*           creator();
        static  MStatus         initialize();
public:
        static  MObject     inputCurve;
        static  MObject         outputCurves;
        static  MObject         numCurves;
        static  MObject         curveOffset;
        static  MTypeId         id;
};
MTypeId     multiCurve::id( 0x8000a );
MObject     multiCurve::inputCurve;
MObject     multiCurve::outputCurves;
MObject     multiCurve::numCurves;
MObject     multiCurve::curveOffset;
#define PERRORfail(stat,msg) \
                                                                if (!(stat)) { \
                                                                        stat.perror((msg)); \
                                                                        return (stat); \
                                                                }
multiCurve::~multiCurve () {}
void* multiCurve::creator()
{
        return new multiCurve();
}
MStatus multiCurve::initialize()
{
        MStatus                         stat;
        MFnNumericAttribute     nAttr;
        MFnTypedAttribute       typedAttr;
        
        numCurves = nAttr.create ("numCurves", "nc",
                                                          MFnNumericData::kLong, 5, &stat);
        PERRORfail(stat, "initialize create numCurves attribute");
        CHECK_MSTATUS ( nAttr.setKeyable( true ) );
        stat = addAttribute( numCurves );
        PERRORfail(stat, "addAttribute(numCurves)");
        
        curveOffset = nAttr.create ("curveOffset", "co",
                                                          MFnNumericData::kDouble, 1.0, &stat);
        PERRORfail(stat, "initialize create curveOffset attribute");
        CHECK_MSTATUS ( nAttr.setKeyable( true ) );
        stat = addAttribute( curveOffset );
        PERRORfail(stat, "addAttribute(curveOffset)");
        inputCurve = typedAttr.create( "inputCurve", "ic",
                                                                   MFnNurbsCurveData::kNurbsCurve, &stat );
        PERRORfail(stat, "initialize create inputCurve attribute");
        CHECK_MSTATUS ( typedAttr.setReadable( false ) );
        CHECK_MSTATUS ( typedAttr.setWritable( true ) );
        stat = addAttribute( inputCurve );
        PERRORfail(stat, "addAttribute(inputCurve)");
        
        outputCurves = typedAttr.create( "outputCurves", "oc",
                                                                         MFnNurbsCurveData::kNurbsCurve, &stat );
        PERRORfail(stat, "initialize create outputCurves attribute");
        CHECK_MSTATUS ( typedAttr.setArray( true ) );
        CHECK_MSTATUS ( typedAttr.setReadable( true ) );
        CHECK_MSTATUS ( typedAttr.setWritable( false ) );
        CHECK_MSTATUS ( typedAttr.setUsesArrayDataBuilder( true ) );
        stat = addAttribute( outputCurves );
        PERRORfail(stat, "addAttribute(outputCurves)");
        stat = attributeAffects( numCurves, outputCurves );
        PERRORfail(stat, "attributeAffects(inputCurve, outputCurves)");
        stat = attributeAffects( curveOffset, outputCurves );
        PERRORfail(stat, "attributeAffects(inputCurve, outputCurves)");
        stat = attributeAffects( inputCurve, outputCurves );
        PERRORfail(stat, "attributeAffects(inputCurve, outputCurves)");
        return stat;
}
MStatus multiCurve::compute( const MPlug& plug, MDataBlock& data )
{
        MStatus stat;
        if ( plug == outputCurves )
        {
                MDataHandle numCurvesHandle =  data.inputValue(numCurves, &stat);
                PERRORfail(stat, "multiCurve::compute getting numCurves");
                int num = numCurvesHandle.asLong();
                MDataHandle curveOffsetHandle =  data.inputValue(curveOffset, &stat);
                PERRORfail(stat, "multiCurve::compute getting curveOffset");
                double baseOffset = curveOffsetHandle.asDouble();
                MDataHandle inputCurveHandle = data.inputValue(inputCurve, &stat);
                PERRORfail(stat, "multiCurve::compute getting inputCurve");
                MObject inputCurveObject ( inputCurveHandle.asNurbsCurveTransformed() );
                MFnNurbsCurve inCurveFS ( inputCurveObject );
                MArrayDataHandle outputArray = data.outputArrayValue(outputCurves,
                                                                                                                         &stat);
                PERRORfail(stat, "multiCurve::compute getting output data handle");
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                MArrayDataBuilder builder(outputCurves, num, &stat);
                PERRORfail(stat, "multiCurve::compute creating builder");
                
                for (int curveNum = 0; curveNum < num; curveNum++) {
                        MDataHandle outHandle = builder.addElement(curveNum);
                        MFnNurbsCurveData dataCreator;
                        MObject outCurveData = dataCreator.create();
                        MObject outputCurve  = inCurveFS.copy(inputCurveObject,
                                                                                                  outCurveData, &stat);
                        PERRORfail(stat, "multiCurve::compute copying curve");
                        MFnNurbsCurve outCurveFS ( outputCurve );
                        MPointArray cvs;
                        double offset = baseOffset * (curveNum+1);
                        outCurveFS.getCVs ( cvs, MSpace::kWorld );
                        int numCVs = cvs.length();
                        for (int i = 0; i < numCVs; i++) {
                                cvs[i].x += offset;
                        }
                        outCurveFS.setCVs ( cvs );
                        outHandle.set(outCurveData);
                }
                
                
                
                
                stat = outputArray.set(builder);
                PERRORfail(stat, "multiCurve::compute setting the builder");
                
                
                
                
                stat = outputArray.setAllClean();
                PERRORfail(stat, "multiCurve::compute cleaning outputCurves");
        } else {
                return MS::kUnknownParameter;
        }
        
        return stat;
}
MStatus initializePlugin( MObject obj )
{
        MFnPlugin plugin( obj, PLUGIN_COMPANY, "3.0", "Any");
        CHECK_MSTATUS (plugin.registerNode( "multiCurve", multiCurve::id,
                                                                multiCurve::creator, multiCurve::initialize ) );
        return MS::kSuccess;;
}
MStatus uninitializePlugin( MObject obj )
{
        MFnPlugin plugin( obj );
        CHECK_MSTATUS (plugin.deregisterNode( multiCurve::id ) );
        
        return MS::kSuccess;;
}