//-
// ==========================================================================
// Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk
// license agreement provided at the time of installation or download,
// or which otherwise accompanies this software in either electronic
// or hard copy form.
// ==========================================================================
//+

////////////////////////////////////////////////////////////////////////
//
// swissArmyManip.cpp
//
// This plug-in is an example of a user-defined manipulator,
// which is comprised of a variety of the base manipulators:
// - MFnCircleSweepManip.h
// - MFnDirectionManip.h
// - MFnDiscManip.h
// - MFnDistanceManip.h
// - MFnFreePointTriadManip.h
// - MFnStateManip.h
// - MFnToggleManip.h
// - MFnRotateManip.h
// - MFnScaleManip.h
//
// To use this plug-in:
//
//     mel: loadPlugin "swissArmyManip.so";
//     mel: createNode swissArmyLocator;
//     click on the showManipTool
//
////////////////////////////////////////////////////////////////////////

#include <math.h>
#include <maya/MIOStream.h>

#include <maya/MPxNode.h>

#include <maya/MPxLocatorNode.h>

#include <maya/MString.h>
#include <maya/MTypeId.h>
#include <maya/MPlug.h>
#include <maya/MVector.h>
#include <maya/MAngle.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>
#include <maya/MColor.h>
#include <maya/M3dView.h>
#include <maya/MFnPlugin.h>
#include <maya/MDistance.h>

#include <maya/MFnUnitAttribute.h>
#include <maya/MFnNumericAttribute.h>

#include <maya/MFn.h>
#include <maya/MPxNode.h>
#include <maya/MPxManipContainer.h>

#include <maya/MFnCircleSweepManip.h>
#include <maya/MFnDirectionManip.h>
#include <maya/MFnDiscManip.h>
#include <maya/MFnDistanceManip.h>
#include <maya/MFnFreePointTriadManip.h>
#include <maya/MFnStateManip.h>
#include <maya/MFnToggleManip.h>
#include <maya/MFnRotateManip.h>
#include <maya/MFnScaleManip.h>

#include <maya/MPxContext.h>
#include <maya/MPxSelectionContext.h>

#include <maya/MPxDrawOverride.h>
#include <maya/MStateManager.h>
#include <maya/MDrawRegistry.h>
#include <maya/MDrawContext.h>
#include <maya/MHWGeometryUtilities.h>

#include <maya/MFnNumericData.h>
#include <maya/MManipData.h>

#define e \
    counter++; \
    if (MS::kSuccess != s) { \
        cerr << "Status Error in method " << method \
             << " at checkpoint #" << counter << "." << "\n" \
			 << "    in File: " << __FILE__ \
			 << ", at Line: " << __LINE__ << endl; \
        s.perror("Error"); \
    }

const double delta1 = 0.01;
const double delta2 = 0.02;
const double delta3 = 0.03;
const double delta4 = 0.04;

// Locator Data
//
static float centre[][3] = { {  0.10f, 0.0f,  0.10f},
							 {  0.10f, 0.0f, -0.10f },
							 { -0.10f, 0.0f, -0.10f },
							 { -0.10f, 0.0f,  0.10f },
							 {  0.10f, 0.0f,  0.10f } };
static float state1[][3] = { {  1.00f, 0.0f,  1.00f},
							 {  1.00f, 0.0f,  0.50f },
							 {  0.50f, 0.0f,  0.50f },
							 {  0.50f, 0.0f,  1.00f },
							 {  1.00f, 0.0f,  1.00f } };
static float state2[][3] = { {  1.00f, 0.0f, -1.00f},
							 {  1.00f, 0.0f, -0.50f },
							 {  0.50f, 0.0f, -0.50f },
							 {  0.50f, 0.0f, -1.00f },
							 {  1.00f, 0.0f, -1.00f } };
static float state3[][3] = { { -1.00f, 0.0f, -1.00f},
							 { -1.00f, 0.0f, -0.50f },
							 { -0.50f, 0.0f, -0.50f },
							 { -0.50f, 0.0f, -1.00f },
							 { -1.00f, 0.0f, -1.00f } };
static float state4[][3] = { { -1.00f, 0.0f,  1.00f},
							 { -1.00f, 0.0f,  0.50f },
							 { -0.50f, 0.0f,  0.50f },
							 { -0.50f, 0.0f,  1.00f },
							 { -1.00f, 0.0f,  1.00f } };
static float arrow1[][3] = { {  0.00f, 0.0f,  1.00f},
							 {  0.10f, 0.0f,  0.20f },
							 { -0.10f, 0.0f,  0.20f },
							 {  0.00f, 0.0f,  1.00f } };
static float arrow2[][3] = { {  1.00f, 0.0f,  0.00f},
							 {  0.20f, 0.0f,  0.10f },
							 {  0.20f, 0.0f, -0.10f },
							 {  1.00f, 0.0f,  0.00f } };
static float arrow3[][3] = { {  0.00f, 0.0f, -1.00f},
							 {  0.10f, 0.0f, -0.20f },
							 { -0.10f, 0.0f, -0.20f },
							 {  0.00f, 0.0f, -1.00f } };
static float arrow4[][3] = { { -1.00f, 0.0f,  0.00f},
							 { -0.20f, 0.0f,  0.10f },
							 { -0.20f, 0.0f, -0.10f },
							 { -1.00f, 0.0f,  0.00f } };
static float perimeter[][3] = { {  1.10f, 0.0f,  1.10f},
								{  1.10f, 0.0f, -1.10f },
								{ -1.10f, 0.0f, -1.10f },
								{ -1.10f, 0.0f,  1.10f },
								{  1.10f, 0.0f,  1.10f } };

static int centreCount = 5;
static int state1Count = 5;
static int state2Count = 5;
static int state3Count = 5;
static int state4Count = 5;
static int arrow1Count = 4;
static int arrow2Count = 4;
static int arrow3Count = 4;
static int arrow4Count = 4;
static int perimeterCount = 5;


class swissArmyLocatorManip : public MPxManipContainer
{
public:
    swissArmyLocatorManip();
    virtual ~swissArmyLocatorManip();

    static void * creator();
    static MStatus initialize();
    virtual MStatus createChildren();
    virtual MStatus connectToDependNode(const MObject & node);

	//Viewport 2.0 manipulator draw overrides
	virtual void		preDrawUI( const M3dView &view );
	virtual void		drawUI( MHWRender::MUIDrawManager& drawManager, 
		const MHWRender::MFrameContext& frameContext) const;

    virtual void draw(M3dView & view,
					  const MDagPath & path,
					  M3dView::DisplayStyle style,
					  M3dView::DisplayStatus status);
	MManipData startPointCallback(unsigned index) const;
	MVector nodeTranslation() const;

    MDagPath fCircleSweepManip;
    MDagPath fDirectionManip;
    MDagPath fDiscManip;
    MDagPath fDistanceManip;
    MDagPath fFreePointTriadManip;
    MDagPath fStateManip;
    MDagPath fToggleManip;
	MDagPath fRotateManip;
    MDagPath fScaleManip;

	MDagPath fNodePath;

	//Value prepared for Viewport 2.0 draw
	MPoint fTextPosition;

public:
    static MTypeId id;
};


MManipData swissArmyLocatorManip::startPointCallback(unsigned /*index*/)
const
{
	MManipData manipData;
	MFnNumericData numData;
	MObject numDataObj = numData.create(MFnNumericData::k3Double);
	MVector vec = nodeTranslation();
	numData.setData(vec.x, vec.y, vec.z);
	manipData = numDataObj;
	return manipData;
}


MVector swissArmyLocatorManip::nodeTranslation() const
{
	MFnDagNode dagFn(fNodePath);
	MDagPath path;
	dagFn.getPath(path);
	path.pop();  // pop from the shape to the transform
	MFnTransform transformFn(path);
	return transformFn.translation(MSpace::kWorld);
}


MTypeId swissArmyLocatorManip::id( 0x8001e );


swissArmyLocatorManip::swissArmyLocatorManip()
{
    // Do not call createChildren from here
}


swissArmyLocatorManip::~swissArmyLocatorManip()
{
}


void* swissArmyLocatorManip::creator()
{
     return new swissArmyLocatorManip();
}


MStatus swissArmyLocatorManip::initialize()
{
    MStatus stat;
    stat = MPxManipContainer::initialize();
    return stat;
}


MStatus swissArmyLocatorManip::createChildren()
{
    MStatus stat = MStatus::kSuccess;
	MString method("swissArmyLocatorManip::createChildren");
	MStatus s;
	int counter = 0;

	// FreePointTriadManip
	//
	fFreePointTriadManip = addFreePointTriadManip("freePointTriadManip",
												  "point");
	MFnFreePointTriadManip freePointTriadManipFn(fFreePointTriadManip);

	// DirectionManip
	//
	fDirectionManip = addDirectionManip("directionManip",
										"direction");
	MFnDirectionManip directionManipFn(fDirectionManip);

	// ToggleManip
	//
	fToggleManip = addToggleManip("toggleManip", "toggle");
	MFnToggleManip toggleManipFn(fToggleManip);

	// StateManip
	//
	fStateManip = addStateManip("stateManip", "state");
	MFnStateManip stateManipFn(fStateManip);

	// DiscManip
	//
	fDiscManip = addDiscManip("discManip",
							  "angle");
	MFnDiscManip discManipFn(fDiscManip);

	// CircleSweepManip
	//
	fCircleSweepManip = addCircleSweepManip("circleSweepManip",
											"angle");
	MFnCircleSweepManip circleSweepManipFn(fCircleSweepManip, &s); e;
	circleSweepManipFn.setCenterPoint(MPoint(0, 0, 0));
	circleSweepManipFn.setNormal(MVector(0, 1, 0));
	circleSweepManipFn.setRadius(2.0);
	circleSweepManipFn.setDrawAsArc(true);

	// DistanceManip
	//
    MString manipName("distanceManip");
    MString distanceName("distance");
    MPoint startPoint(0.0, 0.0, 0.0);
    MVector direction(0.0, 1.0, 0.0);
    fDistanceManip = addDistanceManip(manipName,
									  distanceName);
	MFnDistanceManip distanceManipFn(fDistanceManip);
	distanceManipFn.setStartPoint(startPoint);
	distanceManipFn.setDirection(direction);

	// RotateManip
	//
    MString RotateManipName("RotateManip");
    MString rotateName("rotation");
    fRotateManip = addRotateManip(RotateManipName, rotateName);
	MFnRotateManip rotateManipFn(fRotateManip);

	// ScaleManip
	//
    MString scaleManipName("scaleManip");
    MString scaleName("scale");
    fScaleManip = addScaleManip(scaleManipName, scaleName);
	MFnScaleManip scaleManipFn(fScaleManip);

    return stat;
}


MStatus swissArmyLocatorManip::connectToDependNode(const MObject &node)
{
    MStatus stat;

	// Get the DAG path
	//
	MFnDagNode dagNodeFn(node);
	dagNodeFn.getPath(fNodePath);
	MObject parentNode = dagNodeFn.parent(0);
	MFnDagNode parentNodeFn(parentNode);

    // Connect the plugs
    //
    MFnDependencyNode nodeFn;
    nodeFn.setObject(node);

	// FreePointTriadManip
	//
    MFnFreePointTriadManip freePointTriadManipFn(fFreePointTriadManip);
	MPlug translationPlug = parentNodeFn.findPlug("t", &stat);
    if (MStatus::kFailure != stat) {
	    freePointTriadManipFn.connectToPointPlug(translationPlug);
	}

	// DirectionManip
	//
    MFnDirectionManip directionManipFn;
    directionManipFn.setObject(fDirectionManip);
	MPlug directionPlug = nodeFn.findPlug("arrow2Direction", &stat);
    if (MStatus::kFailure != stat) {
	    directionManipFn.connectToDirectionPlug(directionPlug);
		unsigned startPointIndex = directionManipFn.startPointIndex();
	    addPlugToManipConversionCallback(startPointIndex,
										 (plugToManipConversionCallback)
										 &swissArmyLocatorManip::startPointCallback);
	}

	// DistanceManip
	//
    MFnDistanceManip distanceManipFn;
    distanceManipFn.setObject(fDistanceManip);
	MPlug sizePlug = nodeFn.findPlug("size", &stat);
    if (MStatus::kFailure != stat) {
	    distanceManipFn.connectToDistancePlug(sizePlug);
		unsigned startPointIndex = distanceManipFn.startPointIndex();
	    addPlugToManipConversionCallback(startPointIndex,
										 (plugToManipConversionCallback)
										 &swissArmyLocatorManip::startPointCallback);
	}

	// CircleSweepManip
	//
	MFnCircleSweepManip circleSweepManipFn(fCircleSweepManip);
	MPlug arrow1AnglePlug = nodeFn.findPlug("arrow1Angle", &stat);
    if (MStatus::kFailure != stat) {
	    circleSweepManipFn.connectToAnglePlug(arrow1AnglePlug);
		unsigned centerIndex = circleSweepManipFn.centerIndex();
	    addPlugToManipConversionCallback(centerIndex,
										 (plugToManipConversionCallback)
										 &swissArmyLocatorManip::startPointCallback);
	}

	// DiscManip
	//
	MFnDiscManip discManipFn(fDiscManip);
	MPlug arrow3AnglePlug = nodeFn.findPlug("arrow3Angle", &stat);
    if (MStatus::kFailure != stat) {
	    discManipFn.connectToAnglePlug(arrow3AnglePlug);
		unsigned centerIndex = discManipFn.centerIndex();
	    addPlugToManipConversionCallback(centerIndex,
										 (plugToManipConversionCallback)
										 &swissArmyLocatorManip::startPointCallback);
	}

	// StateManip
	//
	MFnStateManip stateManipFn(fStateManip);

	MPlug statePlug = nodeFn.findPlug("state", &stat);
    if (MStatus::kFailure != stat) {
	    stateManipFn.connectToStatePlug(statePlug);
		unsigned positionIndex = stateManipFn.positionIndex();
	    addPlugToManipConversionCallback(positionIndex,
										 (plugToManipConversionCallback)
										 &swissArmyLocatorManip::startPointCallback);
	}

	// ToggleManip
	//
	MFnToggleManip toggleManipFn(fToggleManip);

	MPlug togglePlug = nodeFn.findPlug("toggle", &stat);
    if (MStatus::kFailure != stat) {
	    toggleManipFn.connectToTogglePlug(togglePlug);
		unsigned startPointIndex = toggleManipFn.startPointIndex();
	    addPlugToManipConversionCallback(startPointIndex,
										 (plugToManipConversionCallback)
										 &swissArmyLocatorManip::startPointCallback);
	}

	// Determine the transform node for the locator
	//
	MDagPath transformPath(fNodePath);
	transformPath.pop();

	MFnTransform transformNode(transformPath);

	// RotateManip
	//
	MFnRotateManip rotateManipFn(fRotateManip);

	MPlug rotatePlug = transformNode.findPlug("rotate", &stat);
	if (MStatus::kFailure != stat) {
	    rotateManipFn.connectToRotationPlug(rotatePlug);
		rotateManipFn.displayWithNode(node);
	}

	// ScaleManip
	//
	MFnScaleManip scaleManipFn(fScaleManip);

	MPlug scalePlug = transformNode.findPlug("scale", &stat);
    if (MStatus::kFailure != stat) {
	    scaleManipFn.connectToScalePlug(scalePlug);
		scaleManipFn.displayWithNode(node);
	}

	finishAddingManips();
	MPxManipContainer::connectToDependNode(node);

    return stat;
}


void swissArmyLocatorManip::draw(M3dView & view,
								 const MDagPath &path,
								 M3dView::DisplayStyle style,
								 M3dView::DisplayStatus status)
{
    MPxManipContainer::draw(view, path, style, status);
    view.beginGL();

    MPoint textPos = nodeTranslation();
	char str[100];
    sprintf(str, "Swiss Army Manipulator");
    MString distanceText(str);
    view.drawText(distanceText, textPos, M3dView::kLeft);
    view.endGL();
}

// Viewport 2.0 manipulator draw overrides
void swissArmyLocatorManip::preDrawUI( const M3dView &view )
{
	// Update text drawing position
	fTextPosition = nodeTranslation();
}

void swissArmyLocatorManip::drawUI( MHWRender::MUIDrawManager& drawManager, 
	const MHWRender::MFrameContext& frameContext) const
{
	drawManager.beginDrawable();

	drawManager.setColor( MColor( 0.0f, 1.0f, 0.1f ) );
	drawManager.text( fTextPosition, "Swiss Army Manipulator", MHWRender::MUIDrawManager::kLeft );

	drawManager.endDrawable();
}

class swissArmyLocator : public MPxLocatorNode
{
public:
	swissArmyLocator();
	virtual ~swissArmyLocator();

    virtual MStatus   		compute(const MPlug& plug, MDataBlock &data);

	virtual void            draw(M3dView &view, const MDagPath &path,
								 M3dView::DisplayStyle style,
								 M3dView::DisplayStatus status);

	virtual bool            isBounded() const;
	virtual MBoundingBox    boundingBox() const;

	// Access data from node for drawing, reused by VP1 and VP2 implementations
	struct DrawData
	{
		float multiplier;
		double angle1;
		double angle2;
		double angle3;
		int state;
		bool toggle;
	};
	void getDrawData(DrawData& data);

	// Draw helper methods
	static void drawOutline(const DrawData& data);

	static  void *          creator();
	static  MStatus         initialize();
	static  MObject         aSize;         // The size of the locator
	static	MObject			aPoint;
	static	MObject			aPointX;
	static	MObject			aPointY;
	static	MObject			aPointZ;
	static	MObject			aArrow1Angle;
	static	MObject			aArrow2Direction;
	static	MObject			aArrow2DirectionX;
	static	MObject			aArrow2DirectionY;
	static	MObject			aArrow2DirectionZ;
	static	MObject			aArrow3Angle;
	static	MObject			aArrow4Distance;
	static	MObject			aState;
	static	MObject			aToggle;

public:
	static	MTypeId		id;
	static	MString		classification;
	static	MString		registrantId;
};


MTypeId swissArmyLocator::id( 0x8001f );
MString swissArmyLocator::classification("drawdb/geometry/swissArmyLocator");
MString	swissArmyLocator::registrantId("SwissArmyLocatorNodePlugin");
MObject swissArmyLocator::aSize;
MObject swissArmyLocator::aPoint;
MObject swissArmyLocator::aPointX;
MObject swissArmyLocator::aPointY;
MObject swissArmyLocator::aPointZ;
MObject swissArmyLocator::aArrow1Angle;
MObject swissArmyLocator::aArrow2Direction;
MObject swissArmyLocator::aArrow2DirectionX;
MObject swissArmyLocator::aArrow2DirectionY;
MObject swissArmyLocator::aArrow2DirectionZ;
MObject swissArmyLocator::aArrow3Angle;
MObject swissArmyLocator::aArrow4Distance;
MObject swissArmyLocator::aState;
MObject swissArmyLocator::aToggle;


swissArmyLocator::swissArmyLocator()
{}


swissArmyLocator::~swissArmyLocator()
{}


MStatus swissArmyLocator::compute(const MPlug &/*plug*/, MDataBlock &/*data*/)
{
	return MS::kUnknownParameter;
}

void swissArmyLocator::getDrawData(DrawData& data)
{
	MObject node = thisMObject();

	MPlug plug(node, swissArmyLocator::aSize);
	MDistance sizeVal;
	plug.getValue(sizeVal);
	data.multiplier = (float)sizeVal.asCentimeters();

	MPlug arrow1AnglePlug(node, swissArmyLocator::aArrow1Angle);
	MAngle arrow1Angle;
	arrow1AnglePlug.getValue(arrow1Angle);
	data.angle1 = -arrow1Angle.asRadians() - 3.1415927/2.0;

	MPlug directionXPlug(node, swissArmyLocator::aArrow2DirectionX);
	MPlug directionZPlug(node, swissArmyLocator::aArrow2DirectionZ);
	double dirX, dirZ;
	directionXPlug.getValue(dirX);
	directionZPlug.getValue(dirZ);
	data.angle2 = atan2(dirZ,dirX) + 3.1415927;

	MPlug arrow3AnglePlug(node, swissArmyLocator::aArrow3Angle);
	MAngle arrow3Angle;
	arrow3AnglePlug.getValue(arrow3Angle);
	data.angle3 = arrow3Angle.asRadians();

	MPlug statePlug(node, swissArmyLocator::aState);
	statePlug.getValue(data.state);

	MPlug togglePlug(node, swissArmyLocator::aToggle);
	togglePlug.getValue(data.toggle);
}

void swissArmyLocator::draw(M3dView &view, const MDagPath &/*path*/,
							M3dView::DisplayStyle style,
							M3dView::DisplayStatus status)
{
	// Get draw data
	//
	DrawData data;
	getDrawData(data);

	view.beginGL();

	if ((style == M3dView::kFlatShaded) ||
		(style == M3dView::kGouraudShaded))
	{
		// Push the color settings
		//
		glPushAttrib(GL_CURRENT_BIT);

		if (status == M3dView::kActive) {
			view.setDrawColor(13, M3dView::kActiveColors);
		} else {
			view.setDrawColor(13, M3dView::kDormantColors);
		}

		int i;
		int last;

		if (data.toggle) {
		if (status == M3dView::kActive)
			view.setDrawColor(15, M3dView::kActiveColors);
		else
			view.setDrawColor(15, M3dView::kDormantColors);
		glBegin(GL_TRIANGLE_FAN);
			last = centreCount - 1;
			for (i = 0; i < last; ++i) {
				glVertex3f(centre[i][0] * data.multiplier,
						   centre[i][1] * data.multiplier,
						   centre[i][2] * data.multiplier);
			}
		glEnd();
		}

		if (data.state == 0) {
		if (status == M3dView::kActive)
			view.setDrawColor(19, M3dView::kActiveColors);
		else
			view.setDrawColor(19, M3dView::kDormantColors);
		glBegin(GL_TRIANGLE_FAN);
			last = state1Count - 1;
			for (i = 0; i < last; ++i) {
				glVertex3f(state1[i][0] * data.multiplier,
						   state1[i][1] * data.multiplier,
						   state1[i][2] * data.multiplier);
			}
		glEnd();
		}

		if (data.state == 1) {
		if (status == M3dView::kActive)
			view.setDrawColor(21, M3dView::kActiveColors);
		else
			view.setDrawColor(21, M3dView::kDormantColors);
		glBegin(GL_TRIANGLE_FAN);
			last = state2Count - 1;
			for (i = 0; i < last; ++i) {
				glVertex3f(state2[i][0] * data.multiplier,
						   state2[i][1] * data.multiplier,
						   state2[i][2] * data.multiplier);
			}
		glEnd();
		}

		if (data.state == 2) {
		if (status == M3dView::kActive)
			view.setDrawColor(18, M3dView::kActiveColors);
		else
			view.setDrawColor(18, M3dView::kDormantColors);
		glBegin(GL_TRIANGLE_FAN);
			last = state3Count - 1;
			for (i = 0; i < last; ++i) {
				glVertex3f(state3[i][0] * data.multiplier,
						   state3[i][1] * data.multiplier,
						   state3[i][2] * data.multiplier);
			}
		glEnd();
		}

		if (data.state == 3) {
		if (status == M3dView::kActive)
			view.setDrawColor(17, M3dView::kActiveColors);
		else
			view.setDrawColor(17, M3dView::kDormantColors);
		glBegin(GL_TRIANGLE_FAN);
			last = state4Count - 1;
			for (i = 0; i < last; ++i) {
				glVertex3f(state4[i][0] * data.multiplier,
						   state4[i][1] * data.multiplier,
						   state4[i][2] * data.multiplier);
			}
		glEnd();
		}

		if (status == M3dView::kActive)
			view.setDrawColor(12, M3dView::kActiveColors);
		else
			view.setDrawColor(12, M3dView::kDormantColors);
		glBegin(GL_TRIANGLE_FAN);
			last = arrow1Count - 1;
			for (i = 0; i < last; ++i) {
				glVertex3f( (float) (- arrow1[i][0] * data.multiplier * cos(data.angle1)
						   - arrow1[i][2] * data.multiplier * sin(data.angle1)),
						   (float) (arrow1[i][1] * data.multiplier + delta1),
						   (float) (arrow1[i][2] * data.multiplier * cos(data.angle1) -
						   arrow1[i][0] * data.multiplier * sin(data.angle1)));
			}
		glEnd();

		if (status == M3dView::kActive)
			view.setDrawColor(16, M3dView::kActiveColors);
		else
			view.setDrawColor(16, M3dView::kDormantColors);
		glBegin(GL_TRIANGLE_FAN);
			last = arrow2Count - 1;
			for (i = 0; i < last; ++i) {
				glVertex3f( (float) (- arrow2[i][0] * data.multiplier * cos(data.angle2)
						   - arrow2[i][2] * data.multiplier * sin(data.angle2)),
						   (float) (arrow2[i][1] * data.multiplier + delta2),
						   (float) (arrow2[i][2] * data.multiplier * cos(data.angle2) -
						   arrow2[i][0] * data.multiplier * sin(data.angle2)));
			}
		glEnd();

		if (status == M3dView::kActive)
			view.setDrawColor(13, M3dView::kActiveColors);
		else
			view.setDrawColor(13, M3dView::kDormantColors);
		glBegin(GL_TRIANGLE_FAN);
			last = arrow3Count - 1;
			for (i = 0; i < last; ++i) {
				glVertex3f( (float) (- arrow3[i][0] * data.multiplier * cos(data.angle3)
						   - arrow3[i][2] * data.multiplier * sin(data.angle3)),
						   (float) (arrow3[i][1] * data.multiplier + delta3),
						   (float) (arrow3[i][2] * data.multiplier * cos(data.angle3) -
						   arrow3[i][0] * data.multiplier * sin(data.angle3)));
			}
		glEnd();

		if (status == M3dView::kActive)
			view.setDrawColor(5, M3dView::kActiveColors);
		else
			view.setDrawColor(5, M3dView::kDormantColors);
		glBegin(GL_TRIANGLE_FAN);
			last = arrow4Count - 1;
			for (i = 0; i < last; ++i) {
				glVertex3f((float) (arrow4[i][0] * data.multiplier),
						   (float) (arrow4[i][1] * data.multiplier + delta4),
						   (float) (arrow4[i][2] * data.multiplier));
			}
		glEnd();

		glPopAttrib();
	}

	// Draw the outline of the locator
	//
	drawOutline(data);

	view.endGL();
}

void swissArmyLocator::drawOutline(const DrawData& data)
{
	glBegin(GL_LINES);
	    int i;
		int last;

		if (data.toggle) {
		last = centreCount - 1;
	    for (i = 0; i < last; ++i) {
			glVertex3f(centre[i][0] * data.multiplier,
					   centre[i][1] * data.multiplier,
					   centre[i][2] * data.multiplier);
			glVertex3f(centre[i+1][0] * data.multiplier,
					   centre[i+1][1] * data.multiplier,
					   centre[i+1][2] * data.multiplier);
		}
		}

		if (data.state == 0) {
		last = state1Count - 1;
	    for (i = 0; i < last; ++i) {
			glVertex3f(state1[i][0] * data.multiplier,
					   state1[i][1] * data.multiplier,
					   state1[i][2] * data.multiplier);
			glVertex3f(state1[i+1][0] * data.multiplier,
					   state1[i+1][1] * data.multiplier,
					   state1[i+1][2] * data.multiplier);
		}
		}

		if (data.state == 1) {
		last = state2Count - 1;
	    for (i = 0; i < last; ++i) {
			glVertex3f(state2[i][0] * data.multiplier,
					   state2[i][1] * data.multiplier,
					   state2[i][2] * data.multiplier);
			glVertex3f(state2[i+1][0] * data.multiplier,
					   state2[i+1][1] * data.multiplier,
					   state2[i+1][2] * data.multiplier);
		}
		}

		if (data.state == 2) {
		last = state3Count - 1;
	    for (i = 0; i < last; ++i) {
			glVertex3f(state3[i][0] * data.multiplier,
					   state3[i][1] * data.multiplier,
					   state3[i][2] * data.multiplier);
			glVertex3f(state3[i+1][0] * data.multiplier,
					   state3[i+1][1] * data.multiplier,
					   state3[i+1][2] * data.multiplier);
		}
		}

		if (data.state == 3) {
		last = state4Count - 1;
	    for (i = 0; i < last; ++i) {
			glVertex3f(state4[i][0] * data.multiplier,
					   state4[i][1] * data.multiplier,
					   state4[i][2] * data.multiplier);
			glVertex3f(state4[i+1][0] * data.multiplier,
					   state4[i+1][1] * data.multiplier,
					   state4[i+1][2] * data.multiplier);
		}
		}

		last = arrow1Count - 1;
	    for (i = 0; i < last; ++i) {
			glVertex3f((float) (- arrow1[i][0] * data.multiplier * cos(data.angle1)
					   - arrow1[i][2] * data.multiplier * sin(data.angle1)),
					   (float) (arrow1[i][1] * data.multiplier + delta1),
					   (float) (arrow1[i][2] * data.multiplier * cos(data.angle1) -
					   arrow1[i][0] * data.multiplier * sin(data.angle1)));
			glVertex3f((float) (- arrow1[i+1][0] * data.multiplier * cos(data.angle1)
					   - arrow1[i+1][2] * data.multiplier * sin(data.angle1)),
					   (float) (arrow1[i+1][1] * data.multiplier + delta1),
					   (float) (arrow1[i+1][2] * data.multiplier * cos(data.angle1) -
					   arrow1[i+1][0] * data.multiplier * sin(data.angle1)));
		}

		last = arrow2Count - 1;
	    for (i = 0; i < last; ++i) {
			glVertex3f((float) (- arrow2[i][0] * data.multiplier * cos(data.angle2)
					   - arrow2[i][2] * data.multiplier * sin(data.angle2)),
					   (float) (arrow2[i][1] * data.multiplier + delta2),
					   (float) (arrow2[i][2] * data.multiplier * cos(data.angle2) -
					   arrow2[i][0] * data.multiplier * sin(data.angle2)));
			glVertex3f((float) (- arrow2[i+1][0] * data.multiplier * cos(data.angle2)
					   - arrow2[i+1][2] * data.multiplier * sin(data.angle2)),
					   (float) (arrow2[i+1][1] * data.multiplier + delta2),
					   (float) (arrow2[i+1][2] * data.multiplier * cos(data.angle2) -
					   arrow2[i+1][0] * data.multiplier * sin(data.angle2)));
		}

		last = arrow3Count - 1;
	    for (i = 0; i < last; ++i) {
			glVertex3f((float) (- arrow3[i][0] * data.multiplier * cos(data.angle3)
					   - arrow3[i][2] * data.multiplier * sin(data.angle3)),
					   (float) (arrow3[i][1] * data.multiplier + delta3),
					   (float) (arrow3[i][2] * data.multiplier * cos(data.angle3) -
					   arrow3[i][0] * data.multiplier * sin(data.angle3)));
			glVertex3f((float) (- arrow3[i+1][0] * data.multiplier * cos(data.angle3)
					   - arrow3[i+1][2] * data.multiplier * sin(data.angle3)),
					   (float) (arrow3[i+1][1] * data.multiplier + delta3),
					   (float) (arrow3[i+1][2] * data.multiplier * cos(data.angle3) -
					   arrow3[i+1][0] * data.multiplier * sin(data.angle3)));
		}

		last = arrow4Count - 1;
	    for (i = 0; i < last; ++i) {
			glVertex3f((float) (arrow4[i][0] * data.multiplier),
					   (float) (arrow4[i][1] * data.multiplier + delta4),
					   (float) (arrow4[i][2] * data.multiplier));
			glVertex3f((float) (arrow4[i+1][0] * data.multiplier),
					   (float) (arrow4[i+1][1] * data.multiplier + delta4),
					   (float) (arrow4[i+1][2] * data.multiplier));
		}

		last = perimeterCount - 1;
	    for (i = 0; i < last; ++i) {
			glVertex3f(perimeter[i][0] * data.multiplier,
					   perimeter[i][1] * data.multiplier,
					   perimeter[i][2] * data.multiplier);
			glVertex3f(perimeter[i+1][0] * data.multiplier,
					   perimeter[i+1][1] * data.multiplier,
					   perimeter[i+1][2] * data.multiplier);
		}
	glEnd();
}


bool swissArmyLocator::isBounded() const
{
	return true;
}


MBoundingBox swissArmyLocator::boundingBox() const
{
	// Get the size
	//
	MObject thisNode = thisMObject();
	MPlug plug(thisNode, aSize);
	MDistance sizeVal;
	plug.getValue(sizeVal);

	double multiplier = sizeVal.asCentimeters();

	MPoint corner1(-1.1, 0.0, -1.1);
	MPoint corner2(1.1, 0.0, 1.1);

	corner1 = corner1 * multiplier;
	corner2 = corner2 * multiplier;

	return MBoundingBox(corner1, corner2);
}


void* swissArmyLocator::creator()
{
	return new swissArmyLocator();
}


MStatus swissArmyLocator::initialize()
{
	MFnUnitAttribute unitFn;
	MFnNumericAttribute numericFn;
	MStatus			 stat;
	MString method("swissArmyLocator::initialize");
	MStatus s;
	int counter = 0;

	// aSize
	aSize = unitFn.create("size", "sz", MFnUnitAttribute::kDistance,
						  0.0, &s); e;
	unitFn.setDefault(10.0);
	unitFn.setStorable(true);
	unitFn.setWritable(true);

	// aPoint
	aPointX = numericFn.create("pointX", "ptx",
							   MFnNumericData::kDouble, 0.0, &s); e;
	aPointY = numericFn.create("pointY", "pty",
							   MFnNumericData::kDouble, 0.0, &s); e;
	aPointZ = numericFn.create("pointZ", "ptz",
							   MFnNumericData::kDouble, 0.0, &s); e;
	aPoint = numericFn.create("point", "pt",
							  aPointX,
							  aPointY,
							  aPointZ, &s); e;

	// aArrow1Angle
	aArrow1Angle = unitFn.create("arrow1Angle", "a1a",
								 MFnUnitAttribute::kAngle, 0.0, &s); e;

	// aArrow2Direction
	aArrow2DirectionX = numericFn.create("arrow2DirectionX", "a2x",
										 MFnNumericData::kDouble, 1.0, &s); e;
	aArrow2DirectionY = numericFn.create("arrow2DirectionY", "a2y",
										 MFnNumericData::kDouble, 0.0, &s); e;
	aArrow2DirectionZ = numericFn.create("arrow2DirectionZ", "a2z",
										 MFnNumericData::kDouble, 0.0, &s); e;
	aArrow2Direction = numericFn.create("arrow2Direction", "dir",
										aArrow2DirectionX,
										aArrow2DirectionY,
										aArrow2DirectionZ, &s); e;

	// aArrow3Angle
	aArrow3Angle = unitFn.create("arrow3Angle", "a3a",
								 MFnUnitAttribute::kAngle, 0.0, &s); e;
	// aArrow4Distance
	aArrow4Distance = unitFn.create("arrow2Distance", "dis",
									MFnUnitAttribute::kDistance, 0.0, &s); e;

	// aState;
	aState = numericFn.create("state", "s",
							  MFnNumericData::kLong, 0, &s); e;

	// aToggle;
	aToggle = numericFn.create("toggle", "t",
							   MFnNumericData::kBoolean, false, &s); e;

	s = addAttribute(aPoint); e;
	s = addAttribute(aArrow1Angle); e;
	s = addAttribute(aArrow2Direction); e;
	s = addAttribute(aArrow3Angle); e;
	s = addAttribute(aArrow4Distance); e;
	s = addAttribute(aState); e;
	s = addAttribute(aToggle); e;

	stat = addAttribute(aSize);
	if (!stat) {
		stat.perror("addAttribute");
		return stat;
	}

	MPxManipContainer::addToManipConnectTable(id);

	return MS::kSuccess;
}


//
// Draw override class for drawing manip in VP2.0
//
class swissArmyLocatorOverride : public MHWRender::MPxDrawOverride
{
public:
	static MHWRender::MPxDrawOverride* Creator(const MObject& obj)
	{
		return new swissArmyLocatorOverride(obj);
	}

	virtual ~swissArmyLocatorOverride();

	virtual MHWRender::DrawAPI supportedDrawAPIs() const;

    virtual bool isBounded(
        const MDagPath& objPath,
        const MDagPath& cameraPath) const;

	virtual MBoundingBox boundingBox(
		const MDagPath& objPath,
		const MDagPath& cameraPath) const;

	virtual MUserData* prepareForDraw(
		const MDagPath& objPath,
		const MDagPath& cameraPath,
		const MHWRender::MFrameContext& frameContext,
		MUserData* oldData);

	static void draw(
		const MHWRender::MDrawContext& context,
		const MUserData* data);

private:
	swissArmyLocatorOverride(const MObject& obj);
};

class swissArmyLocatorData : public MUserData
{
public:
	swissArmyLocatorData() : MUserData(false) {} // don't delete after draw
	virtual ~swissArmyLocatorData() {}
	swissArmyLocator::DrawData drawData;
	float color[4];
};

swissArmyLocatorOverride::swissArmyLocatorOverride(const MObject& obj)
: MHWRender::MPxDrawOverride(obj, swissArmyLocatorOverride::draw)
{
}

swissArmyLocatorOverride::~swissArmyLocatorOverride()
{
}

MHWRender::DrawAPI swissArmyLocatorOverride::supportedDrawAPIs() const
{
	return MHWRender::kOpenGL;
}

bool swissArmyLocatorOverride::isBounded(
	const MDagPath& objPath,
	const MDagPath& cameraPath) const
{
	return true;
}

MBoundingBox swissArmyLocatorOverride::boundingBox(
	const MDagPath& objPath,
	const MDagPath& cameraPath) const
{
	MStatus status;
	MFnDependencyNode node(objPath.node(), &status);
	if (!status) return MBoundingBox();
	swissArmyLocator* swissNode =
		dynamic_cast<swissArmyLocator*>(node.userNode());
	if (!swissNode) return MBoundingBox();
	return swissNode->boundingBox();
}

MUserData* swissArmyLocatorOverride::prepareForDraw(
	const MDagPath& objPath,
	const MDagPath& cameraPath,
	const MHWRender::MFrameContext& frameContext,
	MUserData* oldData)
{
	// get the node
	MStatus status;
	MFnDependencyNode node(objPath.node(), &status);
	if (!status) return NULL;
	swissArmyLocator* swissNode =
		dynamic_cast<swissArmyLocator*>(node.userNode());
	if (!swissNode) return NULL;

	// access/create user data for draw callback
	swissArmyLocatorData* data =
		dynamic_cast<swissArmyLocatorData*>(oldData);
	if (!data)
	{
		data = new swissArmyLocatorData();
	}

	// compute data and cache it
	swissNode->getDrawData(data->drawData);
    MColor color = MHWRender::MGeometryUtilities::wireframeColor(objPath);
	data->color[0] = color.r;
	data->color[1] = color.g;
	data->color[2] = color.b;
	data->color[3] = 1.0f;

	return data;
}

void swissArmyLocatorOverride::draw(
	const MHWRender::MDrawContext& context,
	const MUserData* data)
{
	// data
	MStatus status;
	MHWRender::MStateManager* stateMgr = context.getStateManager();
	const swissArmyLocatorData* locatorData =
		dynamic_cast<const swissArmyLocatorData*>(data);
	if (!stateMgr || !locatorData) return;

	// matrices
	const MMatrix transform =
		context.getMatrix(MHWRender::MFrameContext::kWorldViewMtx, &status);
	if (status != MStatus::kSuccess) return;
	const MMatrix projection =
		context.getMatrix(MHWRender::MFrameContext::kProjectionMtx, &status);
	if (status != MStatus::kSuccess) return;

	// do drawing of outline only for now
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadMatrixd(transform.matrix[0]);
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadMatrixd(projection.matrix[0]);

	glPushAttrib(GL_CURRENT_BIT);
	glColor4fv(locatorData->color);
	swissArmyLocator::drawOutline(locatorData->drawData);
	glPopAttrib();

	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
}


//
//	Entry points
//
MStatus initializePlugin(MObject obj)
{
	MStatus status;
	MFnPlugin plugin(obj, PLUGIN_COMPANY, "6.0", "Any");

	status = plugin.registerNode("swissArmyLocator",
								 swissArmyLocator::id,
								 &swissArmyLocator::creator,
								 &swissArmyLocator::initialize,
								 MPxNode::kLocatorNode,
								 &swissArmyLocator::classification);
	if (!status) {
		status.perror("registerNode");
		return status;
	}

    status = MHWRender::MDrawRegistry::registerDrawOverrideCreator(
		swissArmyLocator::classification,
		swissArmyLocator::registrantId,
		swissArmyLocatorOverride::Creator);
	if (!status) {
		status.perror("registerDrawOverrideCreator");
		return status;
	}

    status = plugin.registerNode("swissArmyLocatorManip",
								 swissArmyLocatorManip::id,
								 &swissArmyLocatorManip::creator,
								 &swissArmyLocatorManip::initialize,
								 MPxNode::kManipContainer);
    if (!status) {
        status.perror("registerNode");
        return status;
    }

	return status;
}


MStatus uninitializePlugin(MObject obj)
{
	MStatus status;
	MFnPlugin plugin(obj);

	status = plugin.deregisterNode(swissArmyLocator::id);
	if (!status) {
		status.perror("deregisterNode");
		return status;
	}

	status = MHWRender::MDrawRegistry::deregisterDrawOverrideCreator(
		swissArmyLocator::classification,
		swissArmyLocator::registrantId);
	if (!status) {
		status.perror("deregisterDrawOverrideCreator");
		return status;
	}

	status = plugin.deregisterNode(swissArmyLocatorManip::id);
	if (!status) {
		status.perror("deregisterNode");
		return status;
	}

	return status;
}
