//-
// ==========================================================================
// 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 <maya/MFnDagNode.h>
#include <maya/MItDag.h>
#include <maya/MObject.h>
#include <maya/MDagPath.h>
#include <maya/MPxCommand.h>
#include <maya/MSyntax.h>
#include <maya/MArgDatabase.h>
#include <maya/MStatus.h>
#include <maya/MString.h>
#include <maya/MFnPlugin.h>
#include <maya/MArgList.h>
#include <maya/MFnCamera.h>
#include <maya/MPoint.h>
#include <maya/MVector.h>
#include <maya/MMatrix.h>
#include <maya/MTransformationMatrix.h>
#include <maya/MFnLight.h>
#include <maya/MColor.h>
#include <maya/MFnNurbsSurface.h>
#include <maya/MIOStream.h>

#define kBreadthFlag			"-b"
#define kBreadthFlagLong		"-breadthFirst"
#define kDepthFlag				"-d"
#define kDepthFlagLong			"-depthFirst"
#define kCameraFlag				"-c"
#define kCameraFlagLong			"-cameras"
#define kLightFlag				"-l"
#define kLightFlagLong			"-lights"
#define kNurbsSurfaceFlag		"-n"
#define kNurbsSurfaceFlagLong	"-nurbsSurfaces"
#define kQuietFlag				"-q"
#define kQuietFlagLong			"-quiet"

class scanDagSyntax: public MPxCommand
{
public:
					scanDagSyntax() {};
	virtual			~scanDagSyntax();
	static void*	creator();
	static MSyntax	newSyntax();
	virtual MStatus	doIt( const MArgList& );

private:
	MStatus			parseArgs( const MArgList& args,
							   MItDag::TraversalType& traversalType,
							   MFn::Type& filter, bool & quiet);
	MStatus			doScan( const MItDag::TraversalType traversalType,
							MFn::Type filter, bool quiet);
	void			printTransformData(const MDagPath& dagPath, bool quiet);
};

scanDagSyntax::~scanDagSyntax() {}

void* scanDagSyntax::creator()
{
	return new scanDagSyntax;
}

MSyntax scanDagSyntax::newSyntax()
{
	MSyntax syntax;

	syntax.addFlag(kBreadthFlag, kBreadthFlagLong);
	syntax.addFlag(kDepthFlag, kDepthFlagLong);
	syntax.addFlag(kCameraFlag, kCameraFlagLong);
	syntax.addFlag(kLightFlag, kLightFlagLong);
	syntax.addFlag(kNurbsSurfaceFlag, kNurbsSurfaceFlagLong);
	syntax.addFlag(kQuietFlag, kQuietFlagLong);

	return syntax;
}


MStatus	scanDagSyntax::doIt( const MArgList& args )
{
	MItDag::TraversalType	traversalType = MItDag::kDepthFirst;
	MFn::Type				filter        = MFn::kInvalid;
	MStatus					status;
	bool					quiet = false;

	status = parseArgs ( args, traversalType, filter, quiet );
	if (!status)
		return status;

	return doScan( traversalType, filter, quiet);
};

MStatus scanDagSyntax::parseArgs( const MArgList& args,
								  MItDag::TraversalType& traversalType,
								  MFn::Type& filter,
								  bool & quiet)
{
	MStatus     	stat;
	MArgDatabase	argData(syntax(), args);

	MString     	arg;

	if (argData.isFlagSet(kBreadthFlag))
		traversalType = MItDag::kBreadthFirst;
	else if (argData.isFlagSet(kDepthFlag))
		traversalType = MItDag::kDepthFirst;

	if (argData.isFlagSet(kCameraFlag))
		filter = MFn::kCamera;
	else if (argData.isFlagSet(kLightFlag))
		filter = MFn::kLight;
	else if (argData.isFlagSet(kNurbsSurfaceFlag))
		filter = MFn::kNurbsSurface;
		
	if (argData.isFlagSet(kQuietFlag))
		quiet = true;
		
	return stat;
}

MStatus scanDagSyntax::doScan( const MItDag::TraversalType traversalType,
							   MFn::Type filter,
							   bool quiet)
{   
	MStatus status;

	MItDag dagIterator( traversalType, filter, &status);

	if ( !status) {
		status.perror("MItDag constructor");
		return status;
	}

	//	Scan the entire DAG and output the name and depth of each node

	if (traversalType == MItDag::kBreadthFirst)
	{
		if (!quiet)
			cout << endl << "Starting Breadth First scan of the Dag";
	} else {
		if (!quiet)
			cout << endl << "Starting Depth First scan of the Dag";
	}

	switch (filter) {
		case MFn::kCamera:
			if (!quiet)
				cout << ": Filtering for Cameras\n";
			break;
		case MFn::kLight:
			if (!quiet)
				cout << ": Filtering for Lights\n";
			break;
		case MFn::kNurbsSurface:
			if (!quiet)
				cout << ": Filtering for Nurbs Surfaces\n";
			break;
		default:
			cout << endl;
	}
	
	int objectCount = 0;
	for ( ; !dagIterator.isDone(); dagIterator.next() ) {

		MDagPath dagPath;

		status = dagIterator.getPath(dagPath);
		if ( !status ) {
			status.perror("MItDag::getPath");
			continue;
		}

		MFnDagNode dagNode(dagPath, &status);
		if ( !status ) {
			status.perror("MFnDagNode constructor");
			continue;
		}

		if (!quiet)
			cout << dagNode.name() << ": " << dagNode.typeName() << endl;

		if (!quiet)
			cout << "  dagPath: " << dagPath.fullPathName() << endl;

		objectCount += 1;
		if (dagPath.hasFn(MFn::kCamera)) {
			MFnCamera camera (dagPath, &status);
			if ( !status ) {
				status.perror("MFnCamera constructor");
				continue;
			}

			// Get the translation/rotation/scale data
			printTransformData(dagPath, quiet);

			// Extract some interesting Camera data
			if (!quiet)
			{
				cout << "  eyePoint: "
					 << camera.eyePoint(MSpace::kWorld) << endl;
				cout << "  upDirection: "
					 << camera.upDirection(MSpace::kWorld) << endl;
				cout << "  viewDirection: "
					 << camera.viewDirection(MSpace::kWorld) << endl;
				cout << "  aspectRatio: " << camera.aspectRatio() << endl;
				cout << "  horizontalFilmAperture: "
					 << camera.horizontalFilmAperture() << endl;
				cout << "  verticalFilmAperture: "
					 << camera.verticalFilmAperture() << endl;
			}
		} else if (dagPath.hasFn(MFn::kLight)) {
			MFnLight light (dagPath, &status);
			if ( !status ) {
				status.perror("MFnLight constructor");
				continue;
			}

			// Get the translation/rotation/scale data
			printTransformData(dagPath, quiet);

			// Extract some interesting Light data
			MColor color;

			color = light.color();
			if (!quiet)
			{
				cout << "  color: ["
					 << color.r << ", "
					 << color.g << ", "
					 << color.b << "]\n";
			}
			color = light.shadowColor();
			if (!quiet)
			{
				cout << "  shadowColor: ["
					 << color.r << ", "
					 << color.g << ", "
					 << color.b << "]\n";

				cout << "  intensity: " << light.intensity() << endl;
			}
		} else if (dagPath.hasFn(MFn::kNurbsSurface)) {
			MFnNurbsSurface surface (dagPath, &status);
			if ( !status ) {
				status.perror("MFnNurbsSurface constructor");
				continue;
			}

			// Get the translation/rotation/scale data
			printTransformData(dagPath, quiet);

			// Extract some interesting Surface data
			if (!quiet)
			{
				cout << "  numCVs: "
					 << surface.numCVsInU()
					 << " * "
					 << surface.numCVsInV()
					 << endl;
				cout << "  numKnots: "
					 << surface.numKnotsInU()
					 << " * "
					 << surface.numKnotsInV()
					 << endl;
				cout << "  numSpans: "
					 << surface.numSpansInU()
					 << " * "
					 << surface.numSpansInV()
					 << endl;
			}
		} else {
			// Get the translation/rotation/scale data
			printTransformData(dagPath, quiet);
		}
	}

	if (!quiet)
	{
		cout.flush();
	}
	setResult(objectCount);
	return MS::kSuccess;
}

void scanDagSyntax::printTransformData(const MDagPath& dagPath, bool quiet)
{
	MStatus		status;
	MObject		transformNode = dagPath.transform(&status);
	// This node has no transform - i.e., it's the world node
	if (!status && status.statusCode () == MStatus::kInvalidParameter)
		return;
	MFnDagNode	transform (transformNode, &status);
	if (!status) {
		status.perror("MFnDagNode constructor");
		return;
	}
	MTransformationMatrix	matrix (transform.transformationMatrix());

	if (!quiet)
	{
		cout << "  translation: " << matrix.translation(MSpace::kWorld)
			 << endl;
	}
	double									threeDoubles[3];
	MTransformationMatrix::RotationOrder	rOrder;

	matrix.getRotation (threeDoubles, rOrder, MSpace::kWorld);
	if (!quiet)
	{
		cout << "  rotation: ["
			 << threeDoubles[0] << ", "
			 << threeDoubles[1] << ", "
			 << threeDoubles[2] << "]\n";
	}
	matrix.getScale (threeDoubles, MSpace::kWorld);
	if (!quiet)
	{
		cout << "  scale: ["
			 << threeDoubles[0] << ", "
			 << threeDoubles[1] << ", "
			 << threeDoubles[2] << "]\n";
	}
}

MStatus initializePlugin( MObject obj )
{ 
	MStatus status;

	MFnPlugin plugin ( obj, PLUGIN_COMPANY, "3.0", "Any" );
	status = plugin.registerCommand( "scanDagSyntax", 
									 scanDagSyntax::creator,
									 scanDagSyntax::newSyntax ); 
	
	return status;
}

MStatus uninitializePlugin( MObject obj )
{
	MStatus status;

	MFnPlugin plugin( obj );
	status = plugin.deregisterCommand( "scanDagSyntax" );

	return status;
}
