#include <stdio.h>
#include <maya/MIOStream.h>
#include <math.h>
#include <maya/MString.h>
#include <maya/MArgList.h>
#include <maya/MEvent.h>
#include <maya/MGlobal.h>
#include <maya/M3dView.h>
#include <maya/MPoint.h>
#include <maya/MPointArray.h>
#include <maya/MDoubleArray.h>
#include <maya/MDagPath.h>
#include <maya/MPxContext.h>
#include <maya/MPxContextCommand.h>
#include <maya/MPxToolCommand.h> 
#include <maya/MToolsInfo.h>
#include <maya/MFnPlugin.h>
#include <maya/MFnNurbsCurve.h> 
#include <maya/MSyntax.h>
#include <maya/MArgParser.h>
#include <maya/MArgDatabase.h>
#include <maya/MCursor.h>
#include <maya/MGL.h>
#define kPitchFlag          "-p"
#define kPitchFlagLong      "-pitch"
#define kRadiusFlag         "-r"
#define kRadiusFlagLong     "-radius"
#define kNumberCVsFlag      "-ncv"
#define kNumberCVsFlagLong  "-numCVs"
#define kUpsideDownFlag     "-ud"
#define kUpsideDownFlagLong "-upsideDown"
#define     NUMBER_OF_CVS       20
{
public:
                    helixTool(); 
    virtual         ~helixTool(); 
    static void*    creator();
    
    void            setRadius(double newRadius);
    void            setPitch(double newPitch);
    void            setNumCVs(unsigned newNumCVs);
    void            setUpsideDown(bool newUpsideDown);
private:
    double          radius;         
    double          pitch;          
    unsigned        numCV;          
    bool            upDown;         
                                    
};
void* helixTool::creator()
{
    return new helixTool;
}
helixTool::~helixTool() {}
helixTool::helixTool()
{
    numCV = 20;
    upDown = false;
    setCommandString("helixToolCmd");
}
    
{
    
    return syntax;
}
{
    status = parseArgs(args);
        return status;
    return redoIt();
}
{
    if (argData.isFlagSet(kPitchFlag)) {
        double tmp;
        status = argData.getFlagArgument(kPitchFlag, 0, tmp);
        if (!status) {
            status.
perror(
"pitch flag parsing failed");
            return status;
        }
        pitch = tmp;
    }
    
    if (argData.isFlagSet(kRadiusFlag)) {
        double tmp;
        status = argData.getFlagArgument(kRadiusFlag, 0, tmp);
        if (!status) {
            status.
perror(
"radius flag parsing failed");
            return status;
        }
        radius = tmp;
    }
    
    if (argData.isFlagSet(kNumberCVsFlag)) {
        unsigned tmp;
        status = argData.getFlagArgument(kNumberCVsFlag, 0, tmp);
        if (!status) {
            status.
perror(
"numCVs flag parsing failed");
            return status;
        }
        numCV = tmp;
    }
    
    if (argData.isFlagSet(kUpsideDownFlag)) {
        bool tmp;
        status = argData.getFlagArgument(kUpsideDownFlag, 0, tmp);
        if (!status) {
            status.
perror(
"upside down flag parsing failed");
            return status;
        }
        upDown = tmp;
    }
}   
{
    const unsigned  deg     = 3;            
    const unsigned  ncvs    = numCV;        
    const unsigned  spans   = ncvs - deg;   
    const unsigned  nknots  = spans+2*deg-1;
    unsigned        i;
    int upFactor;
    if (upDown) upFactor = -1;
    else upFactor = 1;
    
    
    for (i = 0; i < ncvs; i++)
                                      upFactor * pitch * (double) i, 
                                      radius * sin((double) i)));
    for (i = 0; i < nknots; i++)
        knotSequences.
append((
double) i);
    
    
    curveFn.
create(controlVertices, knotSequences, deg, 
    if (!stat) {
        stat.
perror(
"Error creating curve");
        return stat;
    }
    return stat;
}
{
    MObject transform = path.transform();
 
    return stat;
}
bool helixTool::isUndoable() const
{
    return true;    
}
{
    command.
addArg(commandString());
}
void helixTool::setRadius(double newRadius)
{
    radius = newRadius;
}
void helixTool::setPitch(double newPitch)
{
    pitch = newPitch;
}
void helixTool::setNumCVs(unsigned newNumCVs)
{
    numCV = newNumCVs;
}
void helixTool::setUpsideDown(bool newUpsideDown)
{
    upDown = newUpsideDown;
}
const char helpString[] = "Click and drag to draw helix";
{
public:
                    helixContext();
    void            setNumCVs(unsigned newNumCVs);
    void            setUpsideDown(bool newUpsideDown);
    unsigned        numCVs();
    bool            upsideDown();
private:
    void            drawGuide();
    bool            firstDraw;
    short           startPos_x, startPos_y;
    short           endPos_x, endPos_y;
    unsigned        numCV;
    bool            upDown;
    GLdouble        height,radius;
    
};
helixContext::helixContext() 
{
    numCV = 20;
    upDown = false;
    setTitleString("Helix Tool");
    
    
}
void helixContext::toolOnSetup(
MEvent &)
 
{
    setHelpString(helpString);
}
{
    event.getPosition(startPos_x, startPos_y);
    firstDraw = true;
}
void helixContext::drawGuide()
{
    int upFactor;
    if (upDown) upFactor = 1;
    else upFactor = -1;
    
    
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
        glRotatef(upFactor*90.0f, 1.0f, 0.0f, 0.0f);
        GLUquadricObj *qobj = gluNewQuadric();
        gluQuadricDrawStyle(qobj, GLU_LINE);
        GLdouble factor = (GLdouble)numCV;
        radius = double(abs(endPos_x - startPos_x))/factor + 0.1;
        height = double(abs(endPos_y - startPos_y))/factor + 0.1;
        gluCylinder( qobj, radius, radius, height, 8, 1 );
    glPopMatrix();
}
{
    view.beginXorDrawing(false);
    if (!firstDraw) {
        
        drawGuide();
    } else {
        firstDraw = false;
    }
    event.getPosition(endPos_x, endPos_y);
    
    drawGuide();
    view.endXorDrawing();
}
{
    
    if (!firstDraw) {
        view.beginXorDrawing(false);
        drawGuide();
        view.endXorDrawing();
    }
    helixTool * cmd = (helixTool*)newToolCommand();
    cmd->setPitch( height/numCV );
    cmd->setRadius( radius );
    cmd->setNumCVs( numCV );
    cmd->setUpsideDown( upDown );
    cmd->redoIt();
    cmd->finalize();
}
{
    return setHelpString( helpString );
}
void helixContext::getClassName( 
MString & name )
 const 
{
}
void helixContext::setNumCVs( unsigned newNumCVs )
{
    numCV = newNumCVs;
}
void helixContext::setUpsideDown( bool newUpsideDown )
{
    upDown = newUpsideDown;
}
unsigned helixContext::numCVs()
{
    return numCV;
}
bool helixContext::upsideDown()
{
    return upDown;
}
{
public: 
                        helixContextCmd();
    static void*        creator();
protected:
    helixContext*       fHelixContext;
};
helixContextCmd::helixContextCmd() {}
{
    fHelixContext = new helixContext();
    return fHelixContext;
}
void* helixContextCmd::creator()
{
    return new helixContextCmd;
}
MStatus helixContextCmd::doEditFlags()
 
{
    
    
        unsigned numCVs;
        if (!status) {
            status.
perror(
"numCVs flag parsing failed.");
            return status;
        }
        fHelixContext->setNumCVs(numCVs);
    }
        bool upsideDown;
        if (!status) {
            status.
perror(
"upsideDown flag parsing failed.");
            return status;
        }
        fHelixContext->setUpsideDown(upsideDown);
    }
    
}
MStatus helixContextCmd::doQueryFlags()
 
{
    
        setResult((int) fHelixContext->numCVs());
    }
        setResult(fHelixContext->upsideDown());
    }
    
}
MStatus helixContextCmd::appendSyntax()
 
{
    
    }
        mySyntax.
addFlag(kUpsideDownFlag, kUpsideDownFlagLong,
    }
}
{
    MFnPlugin plugin(obj, PLUGIN_COMPANY, 
"3.0", 
"Any");
 
    
    
    
    status = plugin.registerContextCommand("helixToolContext",
                                           helixContextCmd::creator,
                                           "helixToolCmd",
                                           helixTool::creator,
                                           helixTool::newSyntax);
    if (!status) {
        status.
perror(
"registerContextCommand");
        return status;
    }
    return status;
}
{
    
    
    status = plugin.deregisterContextCommand( "helixToolContext",
                                              "helixToolCmd" );
    if (!status) {
        status.
perror(
"deregisterContextCommand");
        return status;
    }
    return status;
}