//-
// ==========================================================================
// 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.
// ==========================================================================
//+

////////////////////////////////////////////////////////////////////////
//
// pfxInfoCmd
//
// Demonstrates the use of the MFnPfxGeometry class for retrieving
// pfx information.  The paint effects tube data is baked down into
// arrays of line data, including position, color, twist, flatness,
// u parameters, color, incandescence and transparency. This command
// lets one ask for line data, line and width data, or all. However
// MFnPfxGeometry::getLineData allows one to ask for any combination
// of the total data. The node name passed into the the pfxInfo command
// must be a valid pfxGeometry node.. currently stroke and pfxHair
// nodetypes are instances of the pfxGeometry node class.
//
//  Syntax:
//  	pfxInfo $pfxNodeName $dataToReturn;
//
//  Example:
// 	pfxInfo strokeShape2; // defaults to all
// 	pfxInfo strokeShape2 "all";
// 	pfxInfo pfxHairShape2 "lineAndWidth";
// 	pfxInfo strokeShape1 "line";
//
////////////////////////////////////////////////////////////////////////

#include <assert.h>

#include <maya/MGlobal.h>
#include <maya/MString.h>
#include <maya/MPoint.h>
#include <maya/MVector.h>
#include <maya/MVectorArray.h>
#include <maya/MDoubleArray.h>
#include <maya/MArgList.h>
#include <maya/MFnDependencyNode.h>
#include <maya/MSelectionList.h>
#include <maya/MPxCommand.h>
#include <maya/MFnDagNode.h>
#include <maya/MRenderLine.h>
#include <maya/MRenderLineArray.h>
#include <maya/MFnPfxGeometry.h>
#include <maya/MFnPlugin.h>


#define mCommandName "pfxInfo"	// Command name

class pfxInfoCmd : public MPxCommand
{
public:
				 pfxInfoCmd();
    virtual		~pfxInfoCmd();

    virtual MStatus doIt ( const MArgList& args );

    static void* creator();

private:

	static MStatus nodeFromName(MString name, MObject & obj);

	MStatus parseArgs ( const MArgList& args );

	MStatus printDoubleArray( MDoubleArray &array, char *title );
	MStatus printVectorArray( MVectorArray &array, char *title );
	MStatus printRenderLine( MRenderLine &renderLine );
	MStatus printRenderLineArray( MRenderLineArray &lineArray, char *title );
	MString pfxName;
	MObject pfxNode;
	MString pfxOutput;
};

pfxInfoCmd::pfxInfoCmd()
{
}

pfxInfoCmd::~pfxInfoCmd() 
{
}


//
//
///////////////////////////////////////////////////////////////////////////////

MStatus pfxInfoCmd::nodeFromName(MString name, MObject & obj)
{
	MSelectionList tempList;
    tempList.add( name );
    if ( tempList.length() > 0 ) 
	{
		tempList.getDependNode( 0, obj );
		return MS::kSuccess;
	}
	return MS::kFailure;
}

MStatus pfxInfoCmd::parseArgs( const MArgList& args )
{
	// Parse the arguments.
	MStatus stat = MS::kSuccess;

	// defaults for output if early return
	pfxOutput = "none";

	if( args.length() < 1 )
	{
		MGlobal::displayError( "Missing pfx node name argument(stroke or pfxHair)." );
		return MS::kFailure;
	}
	else if( args.length() > 2 )
	{
		MGlobal::displayError( "Too many arguments." );
		return MS::kFailure;
	}

	pfxName = args.asString( 0, &stat );
	if (stat != MS::kSuccess)
	{
		MGlobal::displayError( "Failed to parse pfx node name argument." );
		return MS::kFailure;
	}

	if(args.length() == 1) {
		// assume that the user wants to print all the voxels
		// they probably won't do this more than once
		pfxOutput = "all";
	} else {
		pfxOutput = args.asString( 1, &stat );
		if (stat != MS::kSuccess)
		{
			MGlobal::displayError( "Failed to parse num voxels to pribt argument." );
			return MS::kFailure;
		}
	}

	nodeFromName( pfxName, pfxNode );

	if( pfxNode.isNull() )
	{
		MGlobal::displayError( "There is no pfx node with the given name." );
		return MS::kFailure;
	}

	if( ! (pfxNode.hasFn( MFn::kPfxGeometry )
	   || pfxNode.hasFn( MFn::kStroke ) 
	   || pfxNode.hasFn( MFn::kPfxHair )) )
	{
		MGlobal::displayError( "The named node is not a stroke or pfxHair node." );
		return MS::kFailure;
	}

	return MS::kSuccess;
}

MStatus pfxInfoCmd::printDoubleArray( MDoubleArray &array, char *title )
{
	int i;
	char buffer[256];
	int len = array.length();
	sprintf( buffer, "    %s(%d entries):", title, len);
	MGlobal::displayInfo( buffer );

	for( i = 0; i < len; i++ ){
		sprintf( buffer, "      %f", array[i]);
		MGlobal::displayInfo( buffer );
	}
	MGlobal::displayInfo( "  " );
	return MS::kSuccess;
}

MStatus pfxInfoCmd::printVectorArray( MVectorArray &array, char *title )
{
	int i;
	char buffer[256];
	int len = array.length();
	sprintf( buffer, "    %s(%d entries):", title, len);
	MGlobal::displayInfo( buffer );

	for( i = 0; i < len; i++ ){
		MVector vec = array[i];
		sprintf( buffer, "      (%f, %f, %f)", vec.x, vec.y, vec.z);
		MGlobal::displayInfo( buffer );
	}
	MGlobal::displayInfo( "  " );
	return MS::kSuccess;
}

MStatus pfxInfoCmd::printRenderLine( MRenderLine &renderLine )
{
	MVectorArray line = renderLine.getLine();
	MVectorArray twist = renderLine.getTwist();
	MDoubleArray width = renderLine.getWidth();
	MDoubleArray flatness = renderLine.getFlatness();
	MDoubleArray parameter = renderLine.getParameter();
	MVectorArray color = renderLine.getColor();
	MVectorArray incandescence = renderLine.getIncandescence();
	MVectorArray transparency = renderLine.getTransparency();
	if( line.length() > 0 ){
		printVectorArray( line, "line" );
	}
	if( twist.length() > 0 ){
		printVectorArray( twist, "twist" );
	}
	if( width.length() > 0 ){
		printDoubleArray( width, "width" );
	}
	if( flatness.length() > 0 ){
		printDoubleArray( flatness, "flatness" );
	}
	if( parameter.length() > 0 ){
		printDoubleArray( parameter, "parameter" );
	}
	if( color.length() > 0 ){
		printVectorArray( color, "color" );
	}
	if( incandescence.length() > 0 ){
		printVectorArray( incandescence, "incandescence" );
	}
	if( transparency.length() > 0 ){
		printVectorArray( transparency, "transparency" );
	}
	MGlobal::displayInfo( "  " );
	return MS::kSuccess;
}

MStatus pfxInfoCmd::printRenderLineArray( MRenderLineArray &lineArray, char *title )
{
	char buffer[256];

	int length = lineArray.length();
	sprintf( buffer, "=%s (%d lines) =============================================", title, length);
	MGlobal::displayInfo( buffer );

	MStatus stat = MS::kSuccess;
	for( int i = 0; i < length; i++ ){
		sprintf( buffer, "  LINE %d .......................", (i+1));
		MGlobal::displayInfo( buffer );
		MRenderLine renderLine = lineArray.renderLine( i, &stat );
		// the renderline array is sparce, so some entries may be 
		// blank (or NULL). MS:kSuccess is only true when the entry
		// is not blank, although this is not an error.
		if( stat == MS::kSuccess ){
			printRenderLine( renderLine );
		}
	} 
	MGlobal::displayInfo( "  " );
	return MS::kSuccess;
}

//
// Main routine
///////////////////////////////////////////////////////////////////////////////
MStatus pfxInfoCmd::doIt( const MArgList& args )
{
	MStatus stat = parseArgs( args );
	if( stat != MS::kSuccess ) {
		return stat;
	}
	if( pfxOutput == "none" ){
		return stat;
	}

	MFnPfxGeometry node( pfxNode );

	MRenderLineArray mainLines, leafLines, flowerLines;

	if( pfxOutput == "all" ){
		stat = node.getLineData( mainLines, leafLines, flowerLines,
		true, true, true, true,
		true, true, true, true, true );
	} else if( pfxOutput == "line" ){
		stat = node.getLineData( mainLines, leafLines, flowerLines,
		true, false, false, false,
		false, false, false, false, true );
	} else if( pfxOutput == "lineAndWidth" ){
		stat = node.getLineData( mainLines, leafLines, flowerLines,
		true, false, true, false,
		false, false, false, false, true );
	}	
	if( stat != MS::kSuccess ){
		return stat;
	}

	stat = printRenderLineArray( mainLines, "MainLines" );
	if( stat != MS::kSuccess ){
		return stat;
	}

	stat = printRenderLineArray( leafLines, "LeafLines" );
	if( stat != MS::kSuccess ){
		return stat;
	}

	stat = printRenderLineArray( flowerLines, "FlowerLines" );
	if( stat != MS::kSuccess ){
		return stat;
	}
	// If getLineData was successfully called then at some point
	// the arrays create for it must be deleted. Delete array
	// will remove all memory internally created for the renderLineArray.
	// Be careful: If you have any MRenderLines or arrays that were returned by this class,
	// you should not access them after calling DeleteArray. The internal
	// wrapped data is currently copied by pointer, with no reference counting. 
	// Thus if you assign one MRenderLineArray to another, you should only call
	// the deleteArray once( and not access the other MRenderLineArray after doing so ).
	mainLines.deleteArray();
	leafLines.deleteArray();
	flowerLines.deleteArray();

	return MS::kSuccess;
}


//
//
///////////////////////////////////////////////////////////////////////////////

void * pfxInfoCmd::creator() { return new pfxInfoCmd(); }


MStatus initializePlugin( MObject obj )
{
    MFnPlugin plugin( obj, PLUGIN_COMPANY, "6.5", "Any");

    MStatus status = plugin.registerCommand(mCommandName,
											pfxInfoCmd::creator );
	if (!status) 
		status.perror("registerCommand");

    return status;
}


MStatus uninitializePlugin( MObject obj )
{
    MFnPlugin plugin( obj );

    MStatus status = plugin.deregisterCommand(mCommandName);
	if (!status) 
		status.perror("deregisterCommand");

    return status;
}
