//-
// ==========================================================================
// 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.
// ==========================================================================
//+

//
// Description:
//   This is an example of a command that creates a clip for a character.
//
//

#include <math.h>
#include <maya/MPxCommand.h>
#include <maya/MStatus.h>
#include <maya/MArgList.h>
#include <maya/MFnPlugin.h>
#include <maya/MObject.h>
#include <maya/MGlobal.h>
#include <maya/MSelectionList.h>
#include <maya/MItSelectionList.h>
#include <maya/MObjectArray.h>
#include <maya/MPlugArray.h>
#include <maya/MFnCharacter.h>
#include <maya/MFnClip.h>
#include <maya/MFnAnimCurve.h>
#include <maya/MDGModifier.h>

#include <maya/MIOStream.h>

#define CheckError(stat,msg)		\
	if ( MS::kSuccess != stat ) {	\
		displayError(msg);			\
		continue;					\
	}


class createClip : public MPxCommand
{
public:
                createClip();
    virtual     ~createClip();

	MStatus		parseArgs( const MArgList& args );
    MStatus     doIt ( const MArgList& args );
    MStatus     redoIt ();
    MStatus     undoIt ();
    bool        isUndoable() const;

    static      void* creator();

private:
	MObject		fCharacter;
	MDGModifier fMod;
};

createClip::createClip()
{
}

createClip::~createClip() {}

void* createClip::creator()
{
    return new createClip;
}

bool createClip::isUndoable() const
{
    return true;
}

MStatus createClip::undoIt()
{
	MStatus status = fMod.undoIt();
    return status;
}

MStatus createClip::parseArgs( const MArgList& args )
//
// No arguments to parse.
//
{
	MStatus     	stat = MS::kSuccess;
	MString     	arg;
	MSelectionList	list;
	bool			charNameUsed = 0;
	MString			charName;
	const MString	charFlag			("-c");
	const MString	charFlagLong		("-char");

	// Parse the arguments.
	for ( unsigned int i = 0; i < args.length(); i++ ) {
		arg = args.asString( i, &stat );
		if (!stat)              
			continue;
				
		if ( arg == charFlag || arg == charFlagLong ) {
			// get the char name
			//
			if (i == args.length()-1) {
				arg += ": must specify a character name";
				displayError(arg);
				return MS::kFailure;
			}
			i++;
			args.get(i, charName);
			list.add(charName);
			charNameUsed = 1;
		}
		else {
			arg += ": unknown argument";
			displayError(arg);
			return MS::kFailure;
		}
	}

	if (charNameUsed) {
		// get the character corresponding to the node name
		//
		MItSelectionList iter (list);
		for ( /* nothing */ ; !iter.isDone(); iter.next() ) {
			MObject node;
			iter.getDependNode(node);
			if (node.apiType() == MFn::kCharacter) {
				fCharacter = node;
				break;
			}
		}
		if (fCharacter.isNull()) {
			MString errMsg("Character flag must specify a character node.");
			displayError(errMsg);
			return MS::kFailure;
		}
	}
	
	return stat;
}


MStatus createClip::doIt( const MArgList& args )
{
	// parse the command arguments
	//
	MStatus stat = parseArgs(args);
	if (stat != MS::kSuccess) {
		return stat;
	}
	
	unsigned int count = 0;

	// if the character flag was used, create the clip on the specified
	// character, otherwise, create a character
	//
	MFnCharacter fnCharacter;
	if (fCharacter.isNull()) {
		MSelectionList activeList;
		MGlobal::getActiveSelectionList (activeList);
		if (0 == activeList.length()) {
			MString errMsg("No character was specified, and no objects were selected.");
			displayError(errMsg);
			return MS::kFailure;
		}
		// create a character using the selection list
		//
		fCharacter = fnCharacter.create(activeList,MFnSet::kNone,&stat);
		if (stat != MS::kSuccess) {
			MString errMsg("Failed to create character using the selection.");
			displayError(errMsg);
			return MS::kFailure;
		}
	} else {
		fnCharacter.setObject(fCharacter);
	}

	// Get the array of members of the character. We will create a clip
	// for them.
	//
	MPlugArray plugs;
	fnCharacter.getMemberPlugs(plugs);

	// Now create a animCurves to use as a clip for the character.
	// The curves will be set up between frames 0 and 10;
	//
	MTime start(0.0);
	MTime duration(10.0);
	MObjectArray clipCurves;
	
	for (count = 0; count < plugs.length(); ++count) {
		// Now create a bunch of animCurves to use as a clip for the
		// character
		//
		MFnAnimCurve fnCurve;
//		AnimCurveType plugType = fnCurve.timedAnimCurveTypeForPlug(plugs[count]);
		MObject curve = fnCurve.create(MFnAnimCurve::kAnimCurveTL); // plugType);
		fnCurve.addKeyframe(start,5.0);
		fnCurve.addKeyframe(duration,15.0);
		clipCurves.append(curve);
	}
	
	// Create a source clip node and add the animation to it
	//
	MFnClip fnClipCreate;
	MObject sourceClip = fnClipCreate.createSourceClip(start,duration,fMod,&stat);
	fnCharacter.attachSourceToCharacter(sourceClip,fMod);	
	for (count = 0; count < plugs.length(); ++count) {
		MPlug animPlug = plugs[count];
		fnCharacter.addCurveToClip(clipCurves[count],sourceClip,animPlug,fMod);
	}

	// instance the clip
	//
	MTime schedStart(15.0);
	MObject instancedClip = fnClipCreate.createInstancedClip(sourceClip,
															 schedStart,
															 fMod,
															 &stat);
	fnCharacter.attachInstanceToCharacter(instancedClip,fMod);

	// instance the clip a second time, at time 30
	//
	schedStart.setValue(30.0);
	MObject instancedClip2 = fnClipCreate.createInstancedClip(sourceClip,
															  schedStart,
															  fMod,
															  &stat);
	fnCharacter.attachInstanceToCharacter(instancedClip2,fMod);

	return stat;
}

MStatus createClip::redoIt()
{
    MStatus status = fMod.doIt();
    return status;
}

MStatus initializePlugin( MObject obj )
{
	MStatus   status;
    MFnPlugin plugin( obj, PLUGIN_COMPANY, "4.0", "Any");

    status = plugin.registerCommand( "createClip", createClip::creator );
	if (!status) {
		status.perror("registerCommand");
		return status;
	}

    return status;
}

MStatus uninitializePlugin( MObject obj )
{
	MStatus   status;
    MFnPlugin plugin( obj );

    status = plugin.deregisterCommand( "createClip" );
	if (!status) {
		status.perror("deregisterCommand");
	}
    return status;
}
