//-
// ==========================================================================
// 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.
// ==========================================================================
//+

#include "meshOpCmd.h"
#include "meshOpNode.h"

// Function Sets
//
#include <maya/MFnDependencyNode.h>
#include <maya/MFnMesh.h>
#include <maya/MFnSingleIndexedComponent.h>

// Iterators
//
#include <maya/MItSelectionList.h>
#include <maya/MItMeshPolygon.h>

// General Includes
//
#include <maya/MGlobal.h>
#include <maya/MSelectionList.h>
#include <maya/MPlug.h>
#include <maya/MArgList.h>

#include <maya/MIOStream.h>

// Status Checking Macro - MCheckStatus (Debugging tool)
//
#define MCheckStatus(status,message)	\
	if( MS::kSuccess != status ) {		\
		cerr << message << "\n";		\
		return status;					\
	}


meshOp::meshOp()
//
//	Description:
//		meshOp constructor
//
{
	fOperation = (MeshOperation) 0;
}

meshOp::~meshOp()
//
//	Description:
//		meshOp destructor
//
{}

void* meshOp::creator()
//
//	Description:
//		this method exists to give Maya a way to create new objects
//      of this type. 
//
//	Return Value:
//		a new object of this type
//
{
	return new meshOp();
}

bool meshOp::isUndoable() const
//
//	Description:
//		this method tells Maya this command is undoable.  It is added to the 
//		undo queue if it is.
//
//	Return Value:
//		true if this command is undoable.
//
{
	return true;
}

MStatus meshOp::doIt( const MArgList& argList )
//
//	Description:
//		implements the MEL meshOp command.
//
//	Arguments:
//		argList - the argument list that was passes to the command from MEL
//
//	Return Value:
//		MS::kSuccess - command succeeded
//		MS::kFailure - command failed (returning this value will cause the 
//                     MEL script that is being run to terminate unless the
//                     error is caught using a "catch" statement.
//
{
	MStatus status;
	bool badArgument = false;

	// Only one parameter is expected to be passed to this command: the mesh
	// operation type. Get it, validate it or stop prematurely
	//
	if (argList.length() == 1)
	{
		int operationTypeArgument = argList.asInt(0);
		if (operationTypeArgument < 0
			|| operationTypeArgument > kMeshOperationCount - 1)
		{
			badArgument = true;
		}
		else
		{
			fOperation = (MeshOperation)operationTypeArgument;
		}
	}
	else badArgument = true;

	if (badArgument)
	{
		cerr << "Expecting one parameter: the operation type." << endl;
		cerr << "Valid types are: " << endl;
		cerr << "   0 - Subdivide edge(s)." << endl;
		cerr << "   1 - Subdivide face(s)." << endl;
		cerr << "   2 - Extrude edge(s)." << endl;
		cerr << "   3 - Extrude face(s)." << endl;
		cerr << "   4 - Collapse edge(s)." << endl;
		cerr << "   5 - Collapse face(s)." << endl;
		cerr << "   6 - Duplicate face(s)." << endl;
		cerr << "   7 - Extract face(s)." << endl;
		cerr << "   8 - Split face(s)." << endl;
		displayError(" Expecting one parameter: the operation type.");
		return MS::kFailure;
	}

	// Each mesh operation only supports one type of components
	// 
	MFn::Type componentType = meshOpFty::getExpectedComponentType(fOperation);

	// Parse the selection list for selected components of the right type.
	// To simplify things, we only take the first object that we find with
	// selected components and operate on that object alone.
	//
	// All other objects are ignored and return warning messages indicating
	// this limitation.
	//
	MSelectionList selList;
	MGlobal::getActiveSelectionList( selList );
	MItSelectionList selListIter( selList );
	selListIter.setFilter( MFn::kMesh );

	// The meshOperation node only accepts a component list input, so we build
	// a component list using MFnComponentListData.
	//
	// MIntArrays could also be passed into the node to represent the ids,
	// but are less storage efficient than component lists, since consecutive 
	// components are bundled into a single entry in component lists.
	//
	MFnComponentListData compListFn;
	compListFn.create();
	bool found = false;
	bool foundMultiple = false;

	for( ; !selListIter.isDone(); selListIter.next() )
	{
		MDagPath dagPath;
		MObject component;
		selListIter.getDagPath( dagPath, component );

		// Check for selected components of the right type
		//
		if( component.apiType() == componentType )
		{
			if( !found )
			{
				// The variable 'component' holds all selected components 
				// on the selected object, thus only a single call to 
				// MFnComponentListData::add() is needed to store the selected
				// components for a given object.
				//
				compListFn.add( component );

				// Copy the component list created by MFnComponentListData
				// into our local component list MObject member.
				//
				fComponentList = compListFn.object();

				// Locally store the actual ids of the selected components so 
				// that this command can directly modify the mesh in the case 
				// when there is no history and history is turned off.
				//
				MFnSingleIndexedComponent compFn( component );

				// Ensure that this DAG path will point to the shape 
				// of our object. Set the DAG path for the polyModifierCmd.
				//
				dagPath.extendToShape();
				setMeshNode( dagPath );
				found = true;
			}
			else
			{
				// Break once we have found a multiple object holding 
				// selected components, since we are not interested in how 
				// many multiple objects there are, only the fact that there
				// are multiple objects.
				//
				foundMultiple = true;
				break;
			}
		}
	}
	if( foundMultiple )
	{
		displayWarning("Found more than one object with selected components.");
		displayWarning("Only operating on first found object.");
	}

	// Initialize the polyModifierCmd node type - mesh node already set
	//
	setModifierNodeType( meshOpNode::id );

	if( found )
	{
		// Now, pass control over to the polyModifierCmd::doModifyPoly() method
		// to handle the operation.
		//
		status = doModifyPoly();
		
		if( status == MS::kSuccess )
		{
			setResult( "meshOp command succeeded!" );
		}
		else
		{
			displayError( "meshOp command failed!" );
		}
	}
	else
	{
		displayError(
			"meshOp command failed: Unable to find selected components" );
		status = MS::kFailure;
	}
	
	return status;
}

MStatus meshOp::redoIt()
//
//	Description:
//		Implements redo for the MEL meshOp command. 
//
//		This method is called when the user has undone a command of this type
//		and then redoes it.  No arguments are passed in as all of the necessary
//		information is cached by the doIt method.
//
//	Return Value:
//		MS::kSuccess - command succeeded
//		MS::kFailure - redoIt failed.  this is a serious problem that will
//                     likely cause the undo queue to be purged
//
{
	MStatus status;

	// Process the polyModifierCmd
	//
	status = redoModifyPoly();

	if( status == MS::kSuccess )
	{
		setResult( "meshOp command succeeded!" );
	}
	else
	{
		displayError( "meshOp command failed!" );
	}

	return status;
}

MStatus meshOp::undoIt()
//
//	Description:
//		implements undo for the MEL meshOp command.  
//
//		This method is called to undo a previous command of this type.  The 
//		system should be returned to the exact state that it was it previous 
//		to this command being executed.  That includes the selection state.
//
//	Return Value:
//		MS::kSuccess - command succeeded
//		MS::kFailure - redoIt failed.  this is a serious problem that will
//                     likely cause the undo queue to be purged
//
{
	MStatus status;

	status = undoModifyPoly();

	if( status == MS::kSuccess )
	{
		setResult( "meshOp undo succeeded!" );
	}
	else
	{
		setResult( "meshOp undo failed!" );
	}
    
	return status;
}

MStatus meshOp::initModifierNode( MObject modifierNode )
{
	MStatus status;

	// We need to tell the meshOp node which components to operate on.
	// By overriding, the polyModifierCmd::initModifierNode() method,
	// we can insert our own modifierNode initialization code.
	//
	MFnDependencyNode depNodeFn( modifierNode );
	MObject cpListAttr = depNodeFn.attribute( "inputComponents" );

	// Pass the component list down to the meshOp node
	//
	MPlug cpListPlug( modifierNode, cpListAttr );
	status = cpListPlug.setValue( fComponentList );
	if (status != MS::kSuccess) return status;

	// Similarly for the operation type
	//
	MObject opTypeAttr = depNodeFn.attribute( "operationType" );
	MPlug opTypePlug( modifierNode, opTypeAttr );
	status = opTypePlug.setValue( fOperation );

	return status;
}

MStatus meshOp::directModifier( MObject mesh )
{
	MStatus status;

	fmeshOpFactory.setMesh( mesh );
	fmeshOpFactory.setComponentList( fComponentList );
	fmeshOpFactory.setComponentIDs( fComponentIDs );
	fmeshOpFactory.setMeshOperation( fOperation );

	// Now, perform the meshOp
	//
	status = fmeshOpFactory.doIt();

	return status;
}

