#include <maya/MIOStream.h>
#include <maya/MPxNode.h>
#include <maya/MPxManipulatorNode.h>
#include <maya/MPxSelectionContext.h>
#include <maya/MPxContextCommand.h>
#include <maya/MModelMessage.h>
#include <maya/MFnPlugin.h>
#include <maya/MGlobal.h>
#include <maya/MItSelectionList.h>
#include <maya/MPoint.h>
#include <maya/MVector.h>
#include <maya/MDagPath.h>
#include <maya/MManipData.h>
#include <maya/MHardwareRenderer.h>
#include <maya/MFnDependencyNode.h>
#include <maya/MFnTransform.h>
#include <maya/MMatrix.h>
#include <maya/MTemplateManipulator.h>
#include "squareScaleManipContext.h"
MTypeId squareScaleManipulator::id( 0x81048 );
class squareGeometry
{
public:
        static MPoint topLeft() {
                return MPoint( -0.5f, 0.5f, 0.0f );
        }
        static MPoint topRight() {
                return MPoint( 0.5f, 0.5f, 0.0f );
        }
        static MPoint bottomLeft() {
                return MPoint( -0.5f, -0.5f, 0.0f );
        }
        static MPoint bottomRight() {
                return MPoint( 0.5f, -0.5f, 0.0f );
        }
};
squareScaleManipulator::squareScaleManipulator()
{
        
        
        MPoint pointOnPlane(squareGeometry::topLeft());
        
        MVector normalToPlane = (MVector(squareGeometry::topLeft()) - MVector(squareGeometry::topRight())) ^ 
                                        (MVector(squareGeometry::topRight()) - MVector(squareGeometry::bottomRight()));
        
        normalToPlane.normalize();
        plane.setPlane( pointOnPlane, normalToPlane );
        
        topIndex = rightIndex = bottomIndex = leftIndex = -1;
        
        rotateX = rotateY = rotateZ = 0.0f;
        translateX = translateY = translateZ = 0.0f;
}
squareScaleManipulator::~squareScaleManipulator()
{
        
}
void squareScaleManipulator::postConstructor()
{
        
        
        
        MStatus status;
        status = addDoubleValue( "topValue", 0, topIndex );
        if ( !status )
                return;
        status = addDoubleValue( "rightValue", 0, rightIndex );
        if ( !status )
                return;
        status = addDoubleValue( "bottomValue", 0, bottomIndex );
        if ( !status )
                return;
        status = addDoubleValue( "leftValue", 0, leftIndex );
        if ( !status )
                return;
}
MStatus squareScaleManipulator::connectToDependNode(const MObject &dependNode)
{
        
        
        
        MStatus status;
        MFnDependencyNode nodeFn(dependNode,&status);
        if ( ! status )
                return MS::kFailure;
        MPlug scaleXPlug = nodeFn.findPlug("scaleX", &status);
        if ( ! status )
                return MS::kFailure;
        int plugIndex = 0;
        status = connectPlugToValue(scaleXPlug,rightIndex, plugIndex);
        if ( !status )
                return MS::kFailure;
        finishAddingManips();
        return MPxManipulatorNode::connectToDependNode( dependNode );
}
void squareScaleManipulator::draw(M3dView &view, const MDagPath &path,
                                M3dView::DisplayStyle style, M3dView::DisplayStatus status)
{
        static MGLFunctionTable *gGLFT = 0;
        if ( 0 == gGLFT )
                gGLFT = MHardwareRenderer::theRenderer()->glFunctionTable();
        
        float tl[4],tr[4],br[4],bl[4];
        squareGeometry::topLeft().get(tl);
        squareGeometry::topRight().get(tr);
        squareGeometry::bottomLeft().get(bl);
        squareGeometry::bottomRight().get(br);
        
        
        
        MGLuint active = 0;
        if ( glActiveName( active ) )
        {
                float *a = 0,*b = 0;
                if ( active == topName )
                {
                        a = &tl[0]; b = &tr[0];
                }
                if ( active == bottomName )
                {
                        a = &bl[0]; b = &br[0];
                }
                if ( active == rightName )
                {
                        a = &tr[0]; b = &br[0];
                }
                if ( active == leftName )
                {
                        a = &tl[0]; b = &bl[0];
                }
                if ( active != 0 )
                {
                        a[0] += (float) mousePointGlName.x; a[1] += (float) mousePointGlName.y; a[2] += (float) mousePointGlName.z;
                        b[0] += (float) mousePointGlName.x; b[1] += (float) mousePointGlName.y; b[2] += (float) mousePointGlName.z;
                }
        }
        
        view.beginGL();
        
        
        DegreeRadianConverter convert;
        gGLFT->glPushMatrix();
        gGLFT->glTranslatef( translateX, translateY, translateZ );
        gGLFT->glRotatef( (float) convert.radiansToDegrees(rotateZ), 0.0f, 0.0f, 1.0f );
        gGLFT->glRotatef( (float) convert.radiansToDegrees(rotateY), 0.0f, 1.0f, 0.0f );
        gGLFT->glRotatef( (float) convert.radiansToDegrees(rotateX), 1.0f, 0.0f, 0.0f );
        
        MGLuint glPickableItem;
        glFirstHandle( glPickableItem );
        
        topName = glPickableItem;
        
        
        colorAndName( view, glPickableItem, false, mainColor() );
        gGLFT->glBegin( MGL_LINES );
                gGLFT->glVertex3fv( tl );
                gGLFT->glVertex3fv( tr );
        gGLFT->glEnd();
        
        glPickableItem++;
        rightName = glPickableItem;
        colorAndName( view, glPickableItem, true, mainColor() );
        gGLFT->glBegin( MGL_LINES );
                gGLFT->glVertex3fv( tr );
                gGLFT->glVertex3fv( br );
        gGLFT->glEnd();
        
        glPickableItem++;
        bottomName = glPickableItem;
        colorAndName( view, glPickableItem, false, mainColor() );
        gGLFT->glBegin( MGL_LINES );
                gGLFT->glVertex3fv( br );
                gGLFT->glVertex3fv( bl );
        gGLFT->glEnd();
        
        glPickableItem++;
        leftName = glPickableItem;
        colorAndName( view, glPickableItem, true, mainColor() );
        gGLFT->glBegin( MGL_LINES );
                gGLFT->glVertex3fv( bl );
                gGLFT->glVertex3fv( tl );
        gGLFT->glEnd();
        
        gGLFT->glPopMatrix();
        
        view.endGL();
}
MStatus squareScaleManipulator::doPress( M3dView& view )
{
        
        
        mousePointGlName = MPoint::origin;
        updateDragInformation();
        return MS::kSuccess;
}
MStatus squareScaleManipulator::doDrag( M3dView& view )
{
        updateDragInformation();
        return MS::kSuccess;
}
 MStatus squareScaleManipulator::doRelease( M3dView& view )
{
        return MS::kSuccess;
}
void squareScaleManipulator::setDrawTransformInfo( double rotation[3], MVector translation )
{
        rotateX = (float) rotation[0]; rotateY = (float) rotation[1]; rotateZ =  (float) rotation[2];
        translateX = (float) translation.x; translateY = (float) translation.y; translateZ = (float) translation.z;
}
MStatus squareScaleManipulator::updateDragInformation()
{
        
        MPoint localMousePoint;
        MVector localMouseDirection;
        if ( MS::kFailure == mouseRay( localMousePoint, localMouseDirection) )
                return MS::kFailure;
        
        
        MPoint mouseIntersectionWithManipPlane;
        if ( ! plane.intersect( localMousePoint, localMouseDirection,   mouseIntersectionWithManipPlane ) )
                return MS::kFailure;
        mousePointGlName = mouseIntersectionWithManipPlane;
        MGLuint active = 0;
        if ( glActiveName( active ) )
    {
                float start[4],end[4];
            if ( active == topName )
            {
                    squareGeometry::topLeft().get(start);
                    squareGeometry::topRight().get(end);
            }
            if ( active == bottomName )
            {
                    squareGeometry::bottomLeft().get(start);
                    squareGeometry::bottomRight().get(end);
            }
            if ( active == rightName )
            {
                                        squareGeometry::topRight().get(start);
                    squareGeometry::bottomRight().get(end);
            }
            if ( active == leftName )
            {
                                        squareGeometry::topLeft().get(start);
                    squareGeometry::bottomLeft().get(end);
            }
            if ( active != 0 )
            {
                                lineMath line;
                                
                                MPoint a( start[0], start[1], start[2] );
                                MPoint b( end[0], end[1], end[2] );
                                MPoint vab = a - b;
                                
                                line.setLine( start, vab );
                                MPoint cpt;
                                
                                
                                if ( line.closestPoint( mousePointGlName, cpt ) )
                                {
                                        mousePointGlName.x -= cpt.x;
                                        mousePointGlName.y -= cpt.y;
                                        mousePointGlName.z -= cpt.z;
                                }
 
                                double minChangeValue = minOfThree( mousePointGlName.x, mousePointGlName.y, mousePointGlName.z );
                                double maxChangeValue = maxOfThree( mousePointGlName.x, mousePointGlName.y, mousePointGlName.z );
                                if ( active == rightName )
                                {
                                        setDoubleValue( rightIndex, maxChangeValue );
                                }
                                if ( active == leftName )
                                {
                                        setDoubleValue( rightIndex, minChangeValue );
                                }
                        }
                        return MS::kSuccess;
    }
        return MS::kFailure;    
}
class squareScaleManipContext;
char contextName[] = "squareScaleManipContext";
char manipulatorNodeName[] = "squareScaleContextManipulator";
class squareScaleManipContext : 
        public MTemplateSelectionContext<contextName, squareScaleManipContext, 
                MFn::kTransform, squareScaleManipulator, manipulatorNodeName >
{
public:
        squareScaleManipContext() {}
        virtual ~squareScaleManipContext() {}
        
        virtual void namesOfAttributes(MStringArray& namesOfAttributes)
        {
                namesOfAttributes.append("scaleX");
        }
        
        
        
        
        virtual void setInitialState()
        {
                MStatus status;
                MFnTransform xform( firstObjectSelected, &status );
                if ( MS::kSuccess != status )
                        return;
                MTransformationMatrix xformMatrix = xform.transformation(&status);
                if ( MS::kSuccess != status )
                        return;
                MTransformationMatrix rotateTranslateMatrix = xformMatrix.asRotateMatrix();
                rotateTranslateMatrix.setTranslation( xformMatrix.getTranslation(MSpace::kWorld), MSpace::kWorld );
                MMatrix matrix = rotateTranslateMatrix.asMatrix();
                cout << matrix << endl;
                double rotation[3];
                MTransformationMatrix::RotationOrder ro;
                xformMatrix.getRotation( rotation, ro );
                MVector translation;
                translation = xformMatrix.getTranslation( MSpace::kWorld );
                manipulatorClassPtr->setDrawTransformInfo( rotation, translation );
        }
};
class squareScaleManipContextCommand;
char contextCommandName[] = "squareScaleManipContext";
class squareScaleManipContextCommand : 
        public MTemplateContextCommand<contextCommandName, squareScaleManipContextCommand, squareScaleManipContext >
{
public:
        squareScaleManipContextCommand() {}
        virtual ~squareScaleManipContextCommand() {}
};
static squareScaleManipContextCommand _squareScaleManipContextCommand;
void* squareScaleManipulator::creator()
{
        return new squareScaleManipulator();
}
MStatus squareScaleManipulator::initialize()
{
        return MS::kSuccess;
}
MStatus initializePlugin(MObject obj)
{
        MStatus status;
        MFnPlugin plugin(obj, PLUGIN_COMPANY, "2009", "Any");
        status = _squareScaleManipContextCommand.registerContextCommand( obj );
        if (!status) 
        {
                MString errorInfo("Error: registering context command : ");
                errorInfo += contextCommandName;
                MGlobal::displayError(errorInfo);
                return status;
        }
        status = plugin.registerNode(manipulatorNodeName, squareScaleManipulator::id, 
                                                                 &squareScaleManipulator::creator, &squareScaleManipulator::initialize,
                                                                 MPxNode::kManipulatorNode);
        if (!status) 
        {
                MString str("Error registering node: ");
                str+= manipulatorNodeName;
                MGlobal::displayError(str);
                return status;
        }
        return status;
}
MStatus uninitializePlugin(MObject obj)
{
        MStatus status;
        MFnPlugin plugin(obj);
        status = _squareScaleManipContextCommand.deregisterContextCommand( obj );
        if (!status) 
        {
                MString errorInfo("Error: deregistering context command : ");
                errorInfo += contextCommandName;
                MGlobal::displayError(errorInfo);
                return status;
        }
        status = plugin.deregisterNode(squareScaleManipulator::id);
        if (!status) 
        {
                MString str("Error deregistering node: ");
                str+= manipulatorNodeName;
                MGlobal::displayError(str);
                return status;
        }
        return status;
}