/** 
 * @file llmotioncontroller.cpp
 * @brief Implementation of LLMotionController class.
 *
 * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
 * $License$
 */

//-----------------------------------------------------------------------------
// Header Files
//-----------------------------------------------------------------------------
#include "linden_common.h"

#include "llmotioncontroller.h"
#include "llkeyframemotion.h"
#include "llmath.h"
#include "lltimer.h"
#include "llanimationstates.h"
#include "llstl.h"

const S32 NUM_JOINT_SIGNATURE_STRIDES = LL_CHARACTER_MAX_JOINTS / 4;
const U32 MAX_MOTION_INSTANCES = 32;

//-----------------------------------------------------------------------------
// Constants and statics
//-----------------------------------------------------------------------------
LLMotionRegistry LLMotionController::sRegistry;

//-----------------------------------------------------------------------------
// LLMotionTableEntry()
//-----------------------------------------------------------------------------
LLMotionTableEntry::LLMotionTableEntry()
{ 
	mConstructor = NULL; 
	mID.setNull(); 
}

LLMotionTableEntry::LLMotionTableEntry(LLMotionConstructor constructor, const LLUUID& id)
		: mConstructor(constructor), mID(id)
{

}

//-----------------------------------------------------------------------------
// create()
//-----------------------------------------------------------------------------
LLMotion* LLMotionTableEntry::create(const LLUUID &id)
{
	LLMotion* motionp = mConstructor(id);

	return motionp;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// LLMotionRegistry class
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// LLMotionRegistry()
// Class Constructor
//-----------------------------------------------------------------------------
LLMotionRegistry::LLMotionRegistry() : mMotionTable(LLMotionTableEntry::uuidEq, LLMotionTableEntry())
{
	
}


//-----------------------------------------------------------------------------
// ~LLMotionRegistry()
// Class Destructor
//-----------------------------------------------------------------------------
LLMotionRegistry::~LLMotionRegistry()
{
	mMotionTable.removeAll();
}


//-----------------------------------------------------------------------------
// addMotion()
//-----------------------------------------------------------------------------
BOOL LLMotionRegistry::addMotion( const LLUUID& id, LLMotionConstructor constructor )
{
//	llinfos << "Registering motion: " << name << llendl;
	if (!mMotionTable.check(id))
	{
		mMotionTable.set(id, LLMotionTableEntry(constructor, id));
		return TRUE;
	}
	
	return FALSE;
}

//-----------------------------------------------------------------------------
// markBad()
//-----------------------------------------------------------------------------
void LLMotionRegistry::markBad( const LLUUID& id )
{
	mMotionTable.set(id, LLMotionTableEntry());
}

//-----------------------------------------------------------------------------
// createMotion()
//-----------------------------------------------------------------------------
LLMotion *LLMotionRegistry::createMotion( const LLUUID &id )
{
	LLMotionTableEntry motion_entry = mMotionTable.get(id);
	LLMotion* motion = NULL;

	if ( motion_entry.getID().isNull() )
	{
		//FIXME - RN: need to replace with a better default scheme
		motion = LLKeyframeMotion::create(id);
	}
	else
	{
		motion = motion_entry.create(id);
	}

	return motion;
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// LLMotionController class
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// LLMotionController()
// Class Constructor
//-----------------------------------------------------------------------------
LLMotionController::LLMotionController(  )
{
	mTime = 0.f;
	mTimeOffset = 0.f;
	mLastTime = 0.0f;
	mHasRunOnce = FALSE;
	mPaused = FALSE;
	mPauseTime = 0.f;
	mTimeStep = 0.f;
	mTimeStepCount = 0;
	mLastInterp = 0.f;
	mTimeFactor = 1.f;
}


//-----------------------------------------------------------------------------
// ~LLMotionController()
// Class Destructor
//-----------------------------------------------------------------------------
LLMotionController::~LLMotionController()
{
	deleteAllMotions();
}

//-----------------------------------------------------------------------------
// deleteAllMotions()
//-----------------------------------------------------------------------------
void LLMotionController::deleteAllMotions()
{
	mLoadingMotions.removeAllNodes();
	mLoadedMotions.clear();
	mActiveMotions.removeAllNodes();

	for_each(mAllMotions.begin(), mAllMotions.end(), DeletePairedPointer());
	mAllMotions.clear();
}

//-----------------------------------------------------------------------------
// addLoadedMotion()
//-----------------------------------------------------------------------------
void LLMotionController::addLoadedMotion(LLMotion* motionp)
{
	if (mLoadedMotions.size() > MAX_MOTION_INSTANCES)
	{
		// too many motions active this frame, kill all blenders
		mPoseBlender.clearBlenders();

		for (U32 i = 0; i < mLoadedMotions.size(); i++)
		{
			LLMotion* cur_motionp = mLoadedMotions.front();
			mLoadedMotions.pop_front();
			
			// motion isn't playing, delete it
			if (!isMotionActive(cur_motionp))
			{
				mCharacter->requestStopMotion(cur_motionp);
				mAllMotions.erase(cur_motionp->getID());
				delete cur_motionp;
				if (mLoadedMotions.size() <= MAX_MOTION_INSTANCES)
				{
					break;
				}
			}
			else
			{
				// put active motion on back
				mLoadedMotions.push_back(cur_motionp);
			}
		}
	}
	mLoadedMotions.push_back(motionp);
}

//-----------------------------------------------------------------------------
// setTimeStep()
//-----------------------------------------------------------------------------
void LLMotionController::setTimeStep(F32 step)
{
	mTimeStep = step;

	if (step != 0.f)
	{
		// make sure timestamps conform to new quantum
		for(	LLMotion* motionp = mActiveMotions.getFirstData();
			motionp != NULL;
			motionp = mActiveMotions.getNextData() )
			{
				motionp->mActivationTimestamp = (F32)llfloor(motionp->mActivationTimestamp / step) * step;
				BOOL stopped = motionp->isStopped();
				motionp->setStopTime((F32)llfloor(motionp->getStopTime() / step) * step);
				motionp->setStopped(stopped);
				motionp->mSendStopTimestamp = (F32)llfloor(motionp->mSendStopTimestamp / step) * step;
			}
	}
}

//-----------------------------------------------------------------------------
// setTimeFactor()
//-----------------------------------------------------------------------------
void LLMotionController::setTimeFactor(F32 time_factor)
{ 
	mTimeOffset += mTimer.getElapsedTimeAndResetF32() * mTimeFactor; 
	mTimeFactor = time_factor; 
}

//-----------------------------------------------------------------------------
// getFirstActiveMotion()
//-----------------------------------------------------------------------------
LLMotion* LLMotionController::getFirstActiveMotion() 
{ 
	return mActiveMotions.getFirstData();
}

//-----------------------------------------------------------------------------
// getNextActiveMotion()
//-----------------------------------------------------------------------------
LLMotion* LLMotionController::getNextActiveMotion()
{ 
	return mActiveMotions.getNextData();
}


//-----------------------------------------------------------------------------
// setCharacter()
//-----------------------------------------------------------------------------
void LLMotionController::setCharacter(LLCharacter *character)
{
	mCharacter = character;
}


//-----------------------------------------------------------------------------
// addMotion()
//-----------------------------------------------------------------------------
BOOL LLMotionController::addMotion( const LLUUID& id, LLMotionConstructor constructor )
{
	return sRegistry.addMotion(id, constructor);
}

//-----------------------------------------------------------------------------
// removeMotion()
//-----------------------------------------------------------------------------
void LLMotionController::removeMotion( const LLUUID& id)
{
	LLMotion* motionp = findMotion(id);
	if (motionp)
	{
		stopMotionLocally(id, TRUE);

		mLoadingMotions.deleteData(motionp);
		std::deque<LLMotion*>::iterator motion_it;
		for (motion_it = mLoadedMotions.begin(); motion_it != mLoadedMotions.end(); ++motion_it)
		{
			if(*motion_it == motionp)
			{
				mLoadedMotions.erase(motion_it);
				break;
			}
		}
		mActiveMotions.deleteData(motionp);
		mAllMotions.erase(id);
		delete motionp;
	}
}

//-----------------------------------------------------------------------------
// createMotion()
//-----------------------------------------------------------------------------
LLMotion* LLMotionController::createMotion( const LLUUID &id )
{
	// do we have an instance of this motion for this character?
	LLMotion *motion = findMotion(id);

	// if not, we need to create one
	if (!motion)
	{
		// look up constructor and create it
		motion = sRegistry.createMotion(id);
		if (!motion)
		{
			return NULL;
		}

		// look up name for default motions
		const char* motion_name = gAnimLibrary.animStateToString(id);
		if (motion_name)
		{
			motion->setName(motion_name);
		}

		// initialize the new instance
		LLMotion::LLMotionInitStatus stat = motion->onInitialize(mCharacter);
		switch(stat)
		{
		case LLMotion::STATUS_FAILURE:
			llinfos << "Motion " << id << " init failed." << llendl;
			sRegistry.markBad(id);
			delete motion;
			return NULL;
		case LLMotion::STATUS_HOLD:
			mLoadingMotions.addData(motion);
			break;
		case LLMotion::STATUS_SUCCESS:
			// add motion to our list
			addLoadedMotion(motion);
			break;
		default:
			llerrs << "Invalid initialization status" << llendl;
			break;
		}

		mAllMotions[id] = motion;
	}
	return motion;
}

//-----------------------------------------------------------------------------
// startMotion()
//-----------------------------------------------------------------------------
BOOL LLMotionController::startMotion(const LLUUID &id, F32 start_offset)
{
	// look for motion in our list of created motions
	LLMotion *motion = createMotion(id);

	if (!motion)
	{
		return FALSE;
	}
	//if the motion is already active, then we're done
	else if (isMotionActive(motion)) // motion is playing and...
	{	
		if (motion->isStopped()) // motion has been stopped
		{
			deactivateMotion(motion);
		}
		else if (mTime < motion->mSendStopTimestamp)	// motion is still active
		{
			return TRUE;
		}
	}

//	llinfos << "Starting motion " << name << llendl;
	return activateMotion(motion, mTime - start_offset);
}


//-----------------------------------------------------------------------------
// stopMotionLocally()
//-----------------------------------------------------------------------------
BOOL LLMotionController::stopMotionLocally(const LLUUID &id, BOOL stop_immediate)
{
	// if already inactive, return false
	LLMotion *motion = findMotion(id);
	if (!motion)
	{
		return FALSE;
	}

	// If on active list, stop it
	if (isMotionActive(motion) && !motion->isStopped())
	{
		// when using timesteps, set stop time to last frame's time, otherwise grab current timer value
		// FIXME: should investigate this inconsistency...hints of obscure bugs

		F32 stop_time = (mTimeStep != 0.f || mPaused) ? (mTime) : mTimeOffset + (mTimer.getElapsedTimeF32() * mTimeFactor);
		motion->setStopTime(stop_time);

		if (stop_immediate)
		{
			deactivateMotion(motion);
		}
		return TRUE;
	}
	else if (isMotionLoading(motion))
	{
		motion->setStopped(TRUE);
		return TRUE;
	}

	return FALSE;
}


//-----------------------------------------------------------------------------
// updateRegularMotions()
//-----------------------------------------------------------------------------
void LLMotionController::updateRegularMotions()
{
	updateMotionsByType(LLMotion::NORMAL_BLEND);
}

//-----------------------------------------------------------------------------
// updateAdditiveMotions()
//-----------------------------------------------------------------------------
void LLMotionController::updateAdditiveMotions()
{
	updateMotionsByType(LLMotion::ADDITIVE_BLEND);
}

//-----------------------------------------------------------------------------
// resetJointSignatures()
//-----------------------------------------------------------------------------
void LLMotionController::resetJointSignatures()
{
	memset(&mJointSignature[0][0], 0, sizeof(U8) * LL_CHARACTER_MAX_JOINTS);
	memset(&mJointSignature[1][0], 0, sizeof(U8) * LL_CHARACTER_MAX_JOINTS);
}

//-----------------------------------------------------------------------------
// updateMotionsByType()
//-----------------------------------------------------------------------------
void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_type)
{
	BOOL update_result = TRUE;
	U8 last_joint_signature[LL_CHARACTER_MAX_JOINTS];

	memset(&last_joint_signature, 0, sizeof(U8) * LL_CHARACTER_MAX_JOINTS);

	// iterate through active motions in chronological order
	for(LLMotion* motionp = mActiveMotions.getFirstData();
		motionp != NULL;
		motionp = mActiveMotions.getNextData())
	{
		if (motionp->getBlendType() != anim_type)
		{
			continue;
		}

		BOOL update_motion = FALSE;

		if (motionp->getPose()->getWeight() < 1.f)
		{
			update_motion = TRUE;
		}
		else
		{
			S32 i;
			// NUM_JOINT_SIGNATURE_STRIDES should be multiple of 4
			for (i = 0; i < NUM_JOINT_SIGNATURE_STRIDES; i++)
			{
		 		U32 *current_signature = (U32*)&(mJointSignature[0][i * 4]);
				U32 test_signature = *(U32*)&(motionp->mJointSignature[0][i * 4]);
				
				if ((*current_signature | test_signature) > (*current_signature))
				{
					*current_signature |= test_signature;
					update_motion = TRUE;
				}

				*((U32*)&last_joint_signature[i * 4]) = *(U32*)&(mJointSignature[1][i * 4]);
				current_signature = (U32*)&(mJointSignature[1][i * 4]);
				test_signature = *(U32*)&(motionp->mJointSignature[1][i * 4]);

				if ((*current_signature | test_signature) > (*current_signature))
				{
					*current_signature |= test_signature;
					update_motion = TRUE;
				}
			}
		}

		if (!update_motion)
		{
			if (motionp->isStopped() && mTime > motionp->getStopTime() + motionp->getEaseOutDuration())
			{
				deactivateMotion(motionp);
			}
			else if (motionp->isStopped() && mTime > motionp->getStopTime())
			{
				// is this the first iteration in the ease out phase?
				if (mLastTime <= motionp->getStopTime())
				{
					// store residual weight for this motion
					motionp->mResidualWeight = motionp->getPose()->getWeight();
				}
			}
			else if (mTime > motionp->mSendStopTimestamp)
			{
				// notify character of timed stop event on first iteration past sendstoptimestamp
				// this will only be called when an animation stops itself (runs out of time)
				if (mLastTime <= motionp->mSendStopTimestamp)
				{
					mCharacter->requestStopMotion( motionp );
					stopMotionLocally(motionp->getID(), FALSE);
				}
			}
			else if (mTime >= motionp->mActivationTimestamp)
			{
				if (mLastTime < motionp->mActivationTimestamp)
				{
					motionp->mResidualWeight = motionp->getPose()->getWeight();
				}
			}
			continue;
		}

		LLPose *posep = motionp->getPose();

		// only filter by LOD after running every animation at least once (to prime the avatar state)
		if (mHasRunOnce && motionp->getMinPixelArea() > mCharacter->getPixelArea())
		{
			motionp->fadeOut();

			//should we notify the simulator that this motion should be stopped (check even if skipped by LOD logic)
			if (mTime > motionp->mSendStopTimestamp)
			{
				// notify character of timed stop event on first iteration past sendstoptimestamp
				// this will only be called when an animation stops itself (runs out of time)
				if (mLastTime <= motionp->mSendStopTimestamp)
				{
					mCharacter->requestStopMotion( motionp );
					stopMotionLocally(motionp->getID(), FALSE);
				}
			}

			if (motionp->getFadeWeight() < 0.01f)
			{
				if (motionp->isStopped() && mTime > motionp->getStopTime() + motionp->getEaseOutDuration())
				{
					posep->setWeight(0.f);
					deactivateMotion(motionp);
				}
				continue;
			}
		}
		else
		{
			motionp->fadeIn();
		}

		//**********************
		// MOTION INACTIVE
		//**********************
		if (motionp->isStopped() && mTime > motionp->getStopTime() + motionp->getEaseOutDuration())
		{
			// this motion has gone on too long, deactivate it
			// did we have a chance to stop it?
			if (mLastTime <= motionp->getStopTime())
			{
				// if not, let's stop it this time through and deactivate it the next

				posep->setWeight(motionp->getFadeWeight());
				motionp->onUpdate(motionp->getStopTime() - motionp->mActivationTimestamp, last_joint_signature);
			}
			else
			{
				posep->setWeight(0.f);
				deactivateMotion(motionp);
				continue;
			}
		}

		//**********************
		// MOTION EASE OUT
		//**********************
		else if (motionp->isStopped() && mTime > motionp->getStopTime())
		{
			// is this the first iteration in the ease out phase?
			if (mLastTime <= motionp->getStopTime())
			{
				// store residual weight for this motion
				motionp->mResidualWeight = motionp->getPose()->getWeight();
			}

			if (motionp->getEaseOutDuration() == 0.f)
			{
				posep->setWeight(0.f);
			}
			else
			{
				posep->setWeight(motionp->getFadeWeight() * motionp->mResidualWeight * cubic_step(1.f - ((mTime - motionp->getStopTime()) / motionp->getEaseOutDuration())));
			}

			// perform motion update
			update_result = motionp->onUpdate(mTime - motionp->mActivationTimestamp, last_joint_signature);
		}

		//**********************
		// MOTION ACTIVE
		//**********************
		else if (mTime > motionp->mActivationTimestamp + motionp->getEaseInDuration())
		{
			posep->setWeight(motionp->getFadeWeight());

			//should we notify the simulator that this motion should be stopped?
			if (mTime > motionp->mSendStopTimestamp)
			{
				// notify character of timed stop event on first iteration past sendstoptimestamp
				// this will only be called when an animation stops itself (runs out of time)
				if (mLastTime <= motionp->mSendStopTimestamp)
				{
					mCharacter->requestStopMotion( motionp );
					stopMotionLocally(motionp->getID(), FALSE);
				}
			}

			// perform motion update
			update_result = motionp->onUpdate(mTime - motionp->mActivationTimestamp, last_joint_signature);
		}

		//**********************
		// MOTION EASE IN
		//**********************
		else if (mTime >= motionp->mActivationTimestamp)
		{
			if (mLastTime < motionp->mActivationTimestamp)
			{
				motionp->mResidualWeight = motionp->getPose()->getWeight();
			}
			if (motionp->getEaseInDuration() == 0.f)
			{
				posep->setWeight(motionp->getFadeWeight());
			}
			else
			{
				// perform motion update
				posep->setWeight(motionp->getFadeWeight() * motionp->mResidualWeight + (1.f - motionp->mResidualWeight) * cubic_step((mTime - motionp->mActivationTimestamp) / motionp->getEaseInDuration()));
			}
			// perform motion update
			update_result = motionp->onUpdate(mTime - motionp->mActivationTimestamp, last_joint_signature);
		}
		else
		{
			posep->setWeight(0.f);
			update_result = motionp->onUpdate(0.f, last_joint_signature);
		}
		
		// allow motions to deactivate themselves 
		if (!update_result)
		{
			if (!motionp->isStopped() || motionp->getStopTime() > mTime)
			{
				// animation has stopped itself due to internal logic
				// propagate this to the network
				// as not all viewers are guaranteed to have access to the same logic
				mCharacter->requestStopMotion( motionp );
				stopMotionLocally(motionp->getID(), FALSE);
			}

		}

		// even if onupdate returns FALSE, add this motion in to the blend one last time
		mPoseBlender.addMotion(motionp);
	}
}


//-----------------------------------------------------------------------------
// updateMotion()
//-----------------------------------------------------------------------------
void LLMotionController::updateMotion()
{
	BOOL use_quantum = (mTimeStep != 0.f);

	// Update timing info for this time step.
	if (!mPaused)
	{
		F32 update_time = mTimeOffset + (mTimer.getElapsedTimeF32() * mTimeFactor);
		if (use_quantum)
		{
			F32 time_interval = fmodf(update_time, mTimeStep);

			// always animate *ahead* of actual time
			S32 quantum_count = llmax(0, llfloor((update_time - time_interval) / mTimeStep)) + 1;
			if (quantum_count == mTimeStepCount)
			{
				// we're still in same time quantum as before, so just interpolate and exit
				if (!mPaused)
				{
					F32 interp = time_interval / mTimeStep;
					mPoseBlender.interpolate(interp - mLastInterp);
					mLastInterp = interp;
				}
				
				return;
			}
			
			// is calculating a new keyframe pose, make sure the last one gets applied
			mPoseBlender.interpolate(1.f);
			mPoseBlender.clearBlenders();

			mTimeStepCount = quantum_count;
			mLastTime = mTime;
			mTime = (F32)quantum_count * mTimeStep;
			mLastInterp = 0.f;
		}
		else
		{
			mLastTime = mTime;
			mTime = update_time;
		}
	}

	// query pending motions for completion
	LLMotion* motionp;

	for (	motionp = mLoadingMotions.getFirstData();
			motionp != NULL;
			motionp = mLoadingMotions.getNextData() )
	{
		LLMotion::LLMotionInitStatus status = motionp->onInitialize(mCharacter);
		if (status == LLMotion::STATUS_SUCCESS)
		{
			mLoadingMotions.removeCurrentData();
			// add motion to our loaded motion list
			addLoadedMotion(motionp);
			// this motion should be playing
			if (!motionp->isStopped())
			{
				activateMotion(motionp, mTime);
			}
		}
		else if (status == LLMotion::STATUS_FAILURE)
		{
			llinfos << "Motion " << motionp->getID() << " init failed." << llendl;
			sRegistry.markBad(motionp->getID());
			mLoadingMotions.removeCurrentData();
			mAllMotions.erase(motionp->getID());
			delete motionp;
		}
	}

	resetJointSignatures();

	if (!mPaused)
	{
		// update additive motions
		updateAdditiveMotions();
		resetJointSignatures();

		// update all regular motions
		updateRegularMotions();

		if (use_quantum)
		{
			mPoseBlender.blendAndCache(TRUE);
		}
		else
		{
			mPoseBlender.blendAndApply();
		}
	}

	mHasRunOnce = TRUE;
//	llinfos << "Motion controller time " << motionTimer.getElapsedTimeF32() << llendl;
}


//-----------------------------------------------------------------------------
// activateMotion()
//-----------------------------------------------------------------------------
BOOL LLMotionController::activateMotion(LLMotion *motion, F32 time)
{
	if (mLoadingMotions.checkData(motion))
	{
		// we want to start this motion, but we can't yet, so flag it as started
		motion->setStopped(FALSE);
		// report pending animations as activated
		return TRUE;
	}

	motion->mResidualWeight = motion->getPose()->getWeight();
	motion->mActivationTimestamp = time;

	// set stop time based on given duration and ease out time
	if (motion->getDuration() != 0.f && !motion->getLoop())
	{
		F32 ease_out_time;
		F32 motion_duration;

		// should we stop at the end of motion duration, or a bit earlier 
		// to allow it to ease out while moving?
		ease_out_time = motion->getEaseOutDuration();

		// is the clock running when the motion is easing in?
		// if not (POSTURE_EASE) then we need to wait that much longer before triggering the stop
		motion_duration = llmax(motion->getDuration() - ease_out_time, 0.f);
		motion->mSendStopTimestamp = time + motion_duration;
	} 
	else
	{
		motion->mSendStopTimestamp = F32_MAX;
	}

	mActiveMotions.addData(motion);

	motion->activate();
	motion->onUpdate(0.f, mJointSignature[1]);

	return TRUE;
}

//-----------------------------------------------------------------------------
// deactivateMotion()
//-----------------------------------------------------------------------------
BOOL LLMotionController::deactivateMotion(LLMotion *motion)
{
	motion->deactivate();
	mActiveMotions.removeData(motion);

	return TRUE;
}

//-----------------------------------------------------------------------------
// isMotionActive()
//-----------------------------------------------------------------------------
BOOL LLMotionController::isMotionActive(LLMotion *motion)
{
	if (motion && motion->isActive())
	{
		return TRUE;
	}

	return FALSE;
}

//-----------------------------------------------------------------------------
// isMotionLoading()
//-----------------------------------------------------------------------------
BOOL LLMotionController::isMotionLoading(LLMotion* motion)
{
	return mLoadingMotions.checkData(motion);
}


//-----------------------------------------------------------------------------
// findMotion()
//-----------------------------------------------------------------------------
LLMotion *LLMotionController::findMotion(const LLUUID& id)
{
	return mAllMotions[id];
}


//-----------------------------------------------------------------------------
// flushAllMotions()
//-----------------------------------------------------------------------------
void LLMotionController::flushAllMotions()
{
	LLDynamicArray<LLUUID> active_motions;
	LLDynamicArray<F32> active_motion_times;

	for (LLMotion* motionp = mActiveMotions.getFirstData();
		motionp;
		motionp = mActiveMotions.getNextData())
	{
		active_motions.put(motionp->getID());
		active_motion_times.put(mTime - motionp->mActivationTimestamp);
		motionp->deactivate();
	}

	// delete all motion instances
	deleteAllMotions();

	// kill current hand pose that was previously called out by
	// keyframe motion
	mCharacter->removeAnimationData("Hand Pose");

	// restart motions
	for (S32 i = 0; i < active_motions.count(); i++)
	{
		startMotion(active_motions[i], active_motion_times[i]);
	}
}

//-----------------------------------------------------------------------------
// pause()
//-----------------------------------------------------------------------------
void LLMotionController::pause()
{
	if (!mPaused)
	{
		//llinfos << "Pausing animations..." << llendl;
		mPauseTime = mTimer.getElapsedTimeF32();
		mPaused = TRUE;
	}
	
}

//-----------------------------------------------------------------------------
// unpause()
//-----------------------------------------------------------------------------
void LLMotionController::unpause()
{
	if (mPaused)
	{
		//llinfos << "Unpausing animations..." << llendl;
		mTimer.reset();
		mTimer.setAge(mPauseTime);
		mPaused = FALSE;
	}
}
// End
