#include <maya/MIOStream.h>
#include <math.h>
#include <stdlib.h>
#include <maya/MFnPlugin.h>
#include <maya/MString.h>
#include <maya/MGlobal.h>
#include <maya/M3dView.h>
#include <maya/MDagPath.h>
 
#include <maya/MItSelectionList.h>
#include <maya/MPxContextCommand.h>
#include <maya/MPxContext.h>
#include <maya/MCursor.h>
#include <maya/MEvent.h>
#include <maya/MGL.h>
#include <maya/MPoint.h>
#include <maya/MPointArray.h>
#include <maya/MItCurveCV.h>
#include <maya/MItSurfaceCV.h>
#include <maya/MItMeshVertex.h>
#include <maya/MItMeshEdge.h>
#include <maya/MItMeshPolygon.h>
#ifdef _WIN32
LPCSTR lassoToolCursor = "lassoToolCursor.cur";
#else
#include "lassoToolCursor.h"
#include "lassoToolCursorMask.h"
#define lassoToolCursor_x_hot 1
#define lassoToolCursor_y_hot 16
#endif
class coord {
public:
    short h;
    short v;
};
extern "C" int xycompare( coord *p1, coord *p2 );
int xycompare( coord *p1, coord *p2 )
{
    if ( p1->v > p2->v )
        return 1;
    if ( p2->v > p1->v )
        return -1;
    if ( p1->h > p2->h )
        return 1;
    if ( p2->h > p1->h )
        return -1;
    return 0;
}
const int initialSize       = 1024;
const int increment         =  256;
const char helpString[]     = "drag mouse to select points by encircling";
{
public:
                    lassoTool();
    virtual         ~lassoTool();
    void*           creator();
private:
    void                    append_lasso( short x, short y );
    void                    draw_lasso();
    bool                    point_in_lasso( coord pt );
    bool                    firstDraw;
    coord                   min;
    coord                   max;
    unsigned                maxSize;
    unsigned                num_points;
    coord*                  lasso;
};
lassoTool::lassoTool()
    : maxSize(0)
    , num_points(0)
    , lasso(NULL)
#ifdef _WIN32
    , lassoCursor(lassoToolCursor)
#else
    , lassoCursor(lassoToolCursor_width,
              lassoToolCursor_height,
              lassoToolCursor_x_hot,
              lassoToolCursor_y_hot,
              lassoToolCursor_bits,
              lassoToolCursorMask_bits)
#endif
{
    setTitleString ( "Lasso Pick" );
    
    
    setCursor(lassoCursor);
    
    
}
lassoTool::~lassoTool() {}
void* lassoTool::creator()
{
    return new lassoTool;
}
void lassoTool::toolOnSetup ( 
MEvent & )
 
{
    setHelpString( helpString );
}
{
    
    
                
            } else {
                
            }
            
        }
    } else {
    }
    
    
    
    maxSize = initialSize;
    lasso = (coord*) malloc (sizeof(coord) * maxSize);
    coord start;
    event.getPosition( start.h, start.v );
    num_points = 1;
    lasso[0] = min = max = start;
    firstDraw = true;
}
{
    if (!firstDraw) {
        
        draw_lasso();
    } else {
        firstDraw = false;
    }
    coord currentPos;
    event.getPosition( currentPos.h, currentPos.v );
    append_lasso( currentPos.h, currentPos.v );
    
    draw_lasso();
    view.endXorDrawing();
}
{
    if (!firstDraw) {
        
        draw_lasso();
        view.endXorDrawing();
    }
    
    
    append_lasso(lasso[0].h, lasso[0].v);
    qsort( &(lasso[0]), num_points, sizeof( coord  ),
        (int (*)(const void *, const void *))xycompare);
    
    
    
    
    
    
    
    
    
    
    
    bool    foundEntireObjects = false;
    bool    foundComponents = false;
    for ( ; !iter.isDone(); iter.next() ) {
        coord       pt;
        iter.getDagPath( dagPath, component );
            foundEntireObjects = true;
            continue; 
        }
        foundComponents = true;
            {
                MItCurveCV curveCVIter( dagPath, component, &stat );
 
                for ( ; !curveCVIter.isDone(); curveCVIter.next() ) {
                    view.worldToView( point, pt.h, pt.v, &stat );
                    if (!stat) {
                        stat.
perror(
"Could not get position");
                        continue;
                    }
                    if ( point_in_lasso( pt ) ) {
                        singleComponent = curveCVIter.cv();
                        newList.
add (dagPath, singleComponent);
                    }
                }
                break;
            }
            {
                for ( ; !surfCVIter.isDone(); surfCVIter.next() ) {
                    view.worldToView( point, pt.h, pt.v, &stat );
                    if (!stat) {
                        stat.
perror(
"Could not get position");
                        continue;
                    }
                    if ( point_in_lasso( pt ) ) {
                        singleComponent = surfCVIter.cv();
                        newList.
add (dagPath, singleComponent);
                    }
                }
                break;
            }
            {
                for ( ; !vertexIter.isDone(); vertexIter.next() ) {
                    view.worldToView( point, pt.h, pt.v, &stat );
                    if (!stat) {
                        stat.
perror(
"Could not get position");
                        continue;
                    }
                    if ( point_in_lasso( pt ) ) {
                        singleComponent = vertexIter.vertex();
                        newList.
add (dagPath, singleComponent);
                    }
                }
                break;
            }
            {
                for ( ; !edgeIter.isDone(); edgeIter.next() ) {
                    view.worldToView( point, pt.h, pt.v, &stat );
                    if (!stat) {
                        stat.
perror(
"Could not get position");
                        continue;
                    }
                    if ( point_in_lasso( pt ) ) {
                        singleComponent = edgeIter.edge();
                        newList.
add (dagPath, singleComponent);
                    }
                }
                break;
            }
            {
                for ( ; !polygonIter.isDone(); polygonIter.next() ) {
                    view.worldToView( point, pt.h, pt.v, &stat );
                    if (!stat) {
                        stat.
perror(
"Could not get position");
                        continue;
                    }
                    if ( point_in_lasso( pt ) ) {
                        singleComponent = polygonIter.polygon();
                        newList.
add (dagPath, singleComponent);
                    }
                }
                break;
            }
        default:
#ifdef DEBUG
            cerr << 
"Selected unsupported type: (" << component.
apiType()
#endif 
            continue;
        }
    }
    
    if (foundEntireObjects && !foundComponents) {
    }
    
    
    free(lasso);
    lasso = (coord*) 0;
    maxSize = 0;
    num_points = 0;
 
}
void lassoTool::append_lasso( short x, short y )
{
    int     cy, iy, ix, ydif, yinc, i;
    float   fx, xinc;
    iy = (int)lasso[num_points-1].v;
    ix = (int)lasso[num_points-1].h;
    ydif = abs( y - iy ); 
    if ( ydif == 0 )
        return;
    
    
    if ( min.h > x )
        min.h = x;
    if ( max.h < x )
        max.h = x;
    if ( min.v > y )
        min.v = y;
    if ( max.v < y )
        max.v = y;
    if ( ((int)y - iy ) < 0 )
        yinc = -1;
    else
        yinc = 1;
    xinc = (float)((int)x - ix)/(float)ydif;
    fx = (float)ix + xinc;
    cy = iy + yinc;
    for ( i = 0; i < ydif; i++ ) {
        if ( num_points >= maxSize ) {
            
            maxSize += increment;
            
            
            
            coord* newLasso = (coord*) realloc (lasso, sizeof(coord) * maxSize);
            if (newLasso == NULL) return;
            lasso = newLasso;
        }
        lasso[num_points].h = (short) fx;
        lasso[num_points].v = (short) cy;
        fx += xinc;
        cy += yinc;
        num_points++;
    }
    return;
}
void lassoTool::draw_lasso()
{
    glBegin( GL_LINE_LOOP );
    for ( unsigned i = 0; i < num_points ; i++ ){
        glVertex2i( lasso[i].h, lasso[i].v );
    }
    glEnd();
}
bool lassoTool::point_in_lasso( coord pt )
{
    unsigned i, sides;
    for ( i = 0; i < num_points; i++ ) {
        if ( lasso[i].v == pt.v ) {
            while ( (lasso[i].v == pt.v ) && (lasso[i].h < pt.h) )
                i++;
            if ( lasso[i].v != pt.v )
                return( false );
            sides = 0;
            i++;
            while ( lasso[i].v == pt.v  ) {
                i++;
                sides++;
            }
            if ( sides % 2 )
                return( false );
            else
                return( true );
        }
    }
    return( false );
}
{
public: 
                        lassoContextCmd() {};
    static void*        creator();
};
{
    return new lassoTool;
}
void* lassoContextCmd::creator()
{
    return new lassoContextCmd;
}
{
    MFnPlugin   plugin( obj, PLUGIN_COMPANY, 
"3.0", 
"Any");
 
    status = plugin.registerContextCommand( "lassoToolContext",
                                            lassoContextCmd::creator );
    if (!status) {
        status.
perror(
"registerContextCommand");
        return status;
    }
    
    status = plugin.registerUI("lassoToolCreateUI", "lassoToolDeleteUI");
    if (!status) {
        status.
perror(
"registerUIScripts");
        return status;
    }
    return status;
}
{
    status = plugin.deregisterContextCommand( "lassoToolContext" );
    return status;
}