#include "lineManipContainer.h"
#include <maya/MHardwareRenderer.h>
#include <maya/MIOStream.h>
#include <maya/MMatrix.h>
#include <maya/MPoint.h>
#include <maya/MVector.h>
#include <maya/MSelectionList.h>
#include <maya/MItSelectionList.h>
#include <maya/MFnTransform.h>
#include <maya/MGlobal.h>
#include <maya/MFnCamera.h>
#include <maya/MTemplateManipulator.h>
MTypeId lineManip::id( 0x81049 );
MTypeId lineManipContainer::id( 0x8104A );
class lineGeometry
{
public:
        static MPoint topPoint( bool rightLine = true ) {
                if ( rightLine )
                        return MPoint( 1.0f, 1.0f, 0.0f );
                return MPoint( -1.0f, 1.0f, 0.0f );
        }
        static MPoint bottomPoint( bool rightLine = true ) {
                if ( rightLine )
                        return MPoint( 1.0f, -1.0f, 0.0f );
                return MPoint( -1.0f, -1.0f, 0.0f );
        }
        static MPoint otherPoint() {
                return MPoint( 2.0f, -1.0f, 0.0f );
        }
};
lineManip::lineManip()
{
        
        
        MPoint pointOnPlane(lineGeometry::topPoint());
        
        MVector normalToPlane = (MVector(lineGeometry::topPoint()) - MVector(lineGeometry::otherPoint())) ^ 
                                        (MVector(lineGeometry::otherPoint()) - MVector(lineGeometry::bottomPoint()));
        
        normalToPlane.normalize();
        plane.setPlane( pointOnPlane, normalToPlane );
        
        affectScale = affectTranslate = false;
}
lineManip::~lineManip()
{
        
}
void lineManip::draw(M3dView &view, const MDagPath &path,
                                M3dView::DisplayStyle style, M3dView::DisplayStatus status)
{
        static MGLFunctionTable *gGLFT = 0;
        if ( 0 == gGLFT )
                gGLFT = MHardwareRenderer::theRenderer()->glFunctionTable();
        
        MDagPath dpath;
        view.getCamera(dpath);
        MFnCamera viewCamera(dpath);
        const char *nameBuffer = viewCamera.name().asChar();
        if ( 0 == nameBuffer )
                return;
        if ( ( 0 == strstr(nameBuffer,"persp") ) && ( 0 == strstr(nameBuffer,"front") ) )
                return;
        
        bool rightLine = true;
        if ( affectTranslate )
                        rightLine = false;
        
        
        
        float top[4],bottom[4];
        lineGeometry::topPoint(rightLine).get(top);
        lineGeometry::bottomPoint(rightLine).get(bottom);
        
        
        
        MGLuint active = 0;
        if ( glActiveName( active ) )
        {
                float *a = 0,*b = 0;
                if ( active == lineName )
                {
                        a = &top[0]; b = &bottom[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();
        
        MGLuint glPickableItem;
        glFirstHandle( glPickableItem );
        
        lineName = glPickableItem;
        
        
        colorAndName( view, glPickableItem, true, mainColor() );
        gGLFT->glBegin( MGL_LINES );
                gGLFT->glVertex3fv( top );
                gGLFT->glVertex3fv( bottom );
        gGLFT->glEnd();
        
        view.endGL();
}
MStatus lineManip::doPress( M3dView& view )
{
        
        
        mousePointGlName = MPoint::origin;
        updateDragInformation();
        return MS::kSuccess;
}
MStatus lineManip::doDrag( M3dView& view )
{
        updateDragInformation();
        return MS::kSuccess;
}
 MStatus lineManip::doRelease( M3dView& view )
{
        
        
        MSelectionList list;
        MGlobal::getActiveSelectionList( list );
        for ( MItSelectionList iter( list ); !iter.isDone(); iter.next() ) 
        {
                MObject node;
                MStatus status;
                iter.getDependNode( node );
                MFnTransform xform( node, &status );
                if ( MS::kSuccess == status )
                {
                        if ( affectScale )
                        {
                                double newScale[3];
                                newScale[0] = mousePointGlName.x + 1;
                                newScale[1] = mousePointGlName.y + 1;
                                newScale[2] = mousePointGlName.z + 1;
                                xform.setScale( newScale );
                        }
                        if ( affectTranslate )
                        {
                                double newTranslate[3];
                                newTranslate[0] = mousePointGlName.x;
                                newTranslate[1] = mousePointGlName.y;
                                newTranslate[2] = mousePointGlName.z;
                                xform.setTranslation( newTranslate, MSpace::kObject );
                        }
                }
        }
        return MS::kSuccess;
}
 
MStatus lineManip::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] = {0,0,0,0},end[4] = {0,0,0,0};
                if ( active == lineName )
                {
                        
                        bool rightLine = true;
                        if ( affectTranslate )
                                        rightLine = false;
                        lineGeometry::topPoint(rightLine).get(start);
                        lineGeometry::bottomPoint(rightLine).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;
                        }
                }
        }
        return MS::kFailure;
}
lineManipContainer::lineManipContainer() 
{ 
    
}
lineManipContainer::~lineManipContainer() 
{
    
}
MStatus lineManipContainer::createChildren()
{
    MStatus status;
        MPxManipulatorNode *proxyManip = 0;
        MString manipTypeName("singleLineManip");
        MString manipName("rightLineManip");
        status = addMPxManipulatorNode( manipTypeName, manipName, proxyManip );
        if ( !status )
        {
                return MS::kFailure;
        }
        lineManip *rightManipPtr = (lineManip *) proxyManip;
        if ( rightManipPtr )
        {
                rightManipPtr->affectScale = true;
        }
        proxyManip = 0;
        manipTypeName = "singleLineManip";
        manipName = "leftLineManip";
        status = addMPxManipulatorNode( manipTypeName, manipName, proxyManip );
        if ( !status )
        {
                return MS::kFailure;
        }
        lineManip *leftManipPtr = (lineManip *) proxyManip;
        if ( leftManipPtr )
        {
                leftManipPtr->affectTranslate = true;
        }
    return status;
}
MStatus lineManipContainer::connectToDependNode(const MObject &node)
{
        
        return MS::kSuccess;
}
void lineManipContainer::draw(M3dView & view, 
                                                                 const MDagPath &path, 
                                                                 M3dView::DisplayStyle style,
                                                                 M3dView::DisplayStatus status)
{ 
    MPxManipContainer::draw(view, path, style, status);
        
    view.beginGL(); 
    MPoint textPos = nodeTranslation();
    MString str( "Two custom line manipulators"); 
    MString distanceText(str.asChar());
    view.drawText(distanceText, textPos, M3dView::kLeft);
        
    view.endGL();
}
MVector lineManipContainer::nodeTranslation() const
{
        MFnDagNode dagFn(fNodePath);
        MDagPath path;
        dagFn.getPath(path);
        path.pop();  
        MFnTransform transformFn(path);
        return transformFn.translation(MSpace::kWorld);
}
class lineManipContainerContext;
char contextName[] = "lineManipContainerContext";
char manipulatorContainerNodeName[] = "lineManipContainer";
class lineManipContainerContext : 
        public MTemplateSelectionContext<contextName, lineManipContainerContext, 
                MFn::kTransform, lineManipContainer, manipulatorContainerNodeName >
{
public:
        lineManipContainerContext() {}
        virtual ~lineManipContainerContext() {}
        virtual void namesOfAttributes(MStringArray& namesOfAttributes)
        {
                namesOfAttributes.append("scaleX");
                namesOfAttributes.append("translateX");
        }
};
class lineManipContainerContextCommand;
char contextCommandName[] = "lineManipContainerContext";
class lineManipContainerContextCommand : 
        public MTemplateContextCommand<contextCommandName, lineManipContainerContextCommand, lineManipContainerContext >
{
public:
        lineManipContainerContextCommand() {}
        virtual ~lineManipContainerContextCommand() {}
};
static lineManipContainerContextCommand _lineManipContainerContextCommand;
void* lineManip::creator()
{
        return new lineManip();
}
MStatus lineManip::initialize()
{
        return MS::kSuccess;
}
void* lineManipContainer::creator()
{
     return new lineManipContainer();
}
MStatus lineManipContainer::initialize()
{ 
    MStatus status;
    status = MPxManipContainer::initialize();
    return status;
}
MStatus initializePlugin( MObject obj )
{ 
        MStatus   status;
        MFnPlugin plugin( obj, PLUGIN_COMPANY, "2009", "Any");
        status = plugin.registerNode( "singleLineManip", lineManip::id, lineManip::creator,
                lineManip::initialize, MPxNode::kManipulatorNode );
        if (!status) 
        {
                status.perror("registerNode");
                return status;
        }
        status = plugin.registerNode( manipulatorContainerNodeName, lineManipContainer::id, lineManipContainer::creator,
                         &lineManipContainer::initialize, MPxNode::kManipContainer);
        if (!status) 
        {
                status.perror("registerNode");
                return status;
        }
        status = _lineManipContainerContextCommand.registerContextCommand( obj );
        if (!status) 
        {
                status.perror("registerContextCommand");
                return status;
        }
        return status;
}
MStatus uninitializePlugin( MObject obj )
{
        MStatus   status;
        MFnPlugin plugin( obj );
        status = plugin.deregisterNode( lineManip::id );
        if (!status) 
        {
                status.perror("deregisterNode");
                return status;
        }
        status = plugin.deregisterNode(lineManipContainer::id);;
        if (!status) 
        {
                status.perror("deregisterNode");
                return status;
        }
        status = _lineManipContainerContextCommand.deregisterContextCommand( obj );
        if (!status) 
        {
                status.perror("deregisterContextCommand");
                return status;
        }
        return status;
}