/** 
 * @file llmotioncontroller.cpp
 * @brief Implementation of LLMotionController class.
 *
 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

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

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

// This is why LL_CHARACTER_MAX_ANIMATED_JOINTS needs to be a multiple of 4.
const S32 NUM_JOINT_SIGNATURE_STRIDES = LL_CHARACTER_MAX_ANIMATED_JOINTS / 4;
const U32 MAX_MOTION_INSTANCES = 32;

//-----------------------------------------------------------------------------
// Constants and statics
//-----------------------------------------------------------------------------
F32 LLMotionController::sCurrentTimeFactor = 1.f;
LLMotionRegistry LLMotionController::sRegistry;

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

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


//-----------------------------------------------------------------------------
// addMotion()
//-----------------------------------------------------------------------------
BOOL LLMotionRegistry::registerMotion( const LLUUID& id, LLMotionConstructor constructor )
{
	//	LL_INFOS() << "Registering motion: " << name << LL_ENDL;
	if (!is_in_map(mMotionTable, id))
	{
		mMotionTable[id] = constructor;
		return TRUE;
	}
	
	return FALSE;
}

//-----------------------------------------------------------------------------
// markBad()
//-----------------------------------------------------------------------------
void LLMotionRegistry::markBad( const LLUUID& id )
{
	mMotionTable[id] = LLMotionConstructor(NULL);
}

//-----------------------------------------------------------------------------
// createMotion()
//-----------------------------------------------------------------------------
LLMotion::ptr_t LLMotionRegistry::createMotion( const LLUUID &id )
{
	LLMotionConstructor constructor = get_if_there(mMotionTable, id, LLMotionConstructor(NULL));
	LLMotion::ptr_t motion;

	if ( constructor == NULL )
	{
		// *FIX: need to replace with a better default scheme. RN
		motion = LLKeyframeMotion::create(id);
	}
	else
	{
		motion = constructor(id);
	}

	return motion;
}

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

//-----------------------------------------------------------------------------
// LLMotionController()
// Class Constructor
//-----------------------------------------------------------------------------
LLMotionController::LLMotionController()
	: mTimeFactor(sCurrentTimeFactor),
	  mCharacter(NULL),
	  mAnimTime(0.f),
	  mPrevTimerElapsed(0.f),
	  mLastTime(0.0f),
	  mHasRunOnce(FALSE),
	  mPaused(FALSE),
	  mPausedFrame(0),
	  mTimeStep(0.f),
	  mTimeStepCount(0),
	  mLastInterp(0.f),
	  mIsSelf(FALSE),
	  mLastCountAfterPurge(0)
{
}


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

void LLMotionController::incMotionCounts(S32& num_motions, S32& num_loading_motions, S32& num_loaded_motions, S32& num_active_motions, S32& num_deprecated_motions)
{
	num_motions += mAllMotions.size();
	num_loading_motions += mLoadingMotions.size();
	num_loaded_motions += mLoadedMotions.size();
	num_active_motions += mActiveMotions.size();
	num_deprecated_motions += mDeprecatedMotions.size();
}

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

	mAllMotions.clear();

	// stinson 05/12/20014 : Ownership of the LLMotion pointers is transferred from
	// mAllMotions to mDeprecatedMotions in method
	// LLMotionController::deprecateMotionInstance().  Thus, we should also clean
	// up the mDeprecatedMotions list as well.
	mDeprecatedMotions.clear();
}

//-----------------------------------------------------------------------------
// purgeExcessMotion()
//-----------------------------------------------------------------------------
void LLMotionController::purgeExcessMotions()
{
	if (mLoadedMotions.size() > MAX_MOTION_INSTANCES)
	{
		// clean up deprecated motions
		for (motion_set_t::iterator deprecated_motion_it = mDeprecatedMotions.begin(); 
			 deprecated_motion_it != mDeprecatedMotions.end(); )
		{
			motion_set_t::iterator cur_iter = deprecated_motion_it++;
			LLMotion::ptr_t cur_motionp(*cur_iter);
			if (!isMotionActive(cur_motionp))
			{
				// Motion is deprecated so we know it's not cannonical,
				//  we can safely remove the instance
				removeMotionInstance(cur_motionp); // modifies mDeprecatedMotions
				mDeprecatedMotions.erase(cur_iter);
			}
		}
	}

	std::vector<LLUUID> motions_to_kill;
	if (mLoadedMotions.size() > MAX_MOTION_INSTANCES)
	{
        motions_to_kill.reserve(100);
        
		// too many motions active this frame, kill all blenders
		mPoseBlender.clearBlenders();
		for (motion_set_t::iterator loaded_motion_it = mLoadedMotions.begin(); 
			 loaded_motion_it != mLoadedMotions.end(); 
			 ++loaded_motion_it)
		{
			LLMotion::ptr_t cur_motionp(*loaded_motion_it);
			// motion isn't playing, delete it
			if (!isMotionActive(cur_motionp))
			{
				motions_to_kill.push_back(cur_motionp->getID());
			}
		}
	}
	
	// clean up all inactive, loaded motions
	for (const LLUUID& motion_id : motions_to_kill)
	{
		// look up the motion again by ID to get canonical instance
		// and kill it only if that one is inactive
		LLMotion::ptr_t motionp(findMotion(motion_id));
		if (motionp && !isMotionActive(motionp))
		{
			removeMotion(motion_id);
		}
	}

	U32 loaded_count = mLoadedMotions.size();
	if (loaded_count > (2 * MAX_MOTION_INSTANCES) && loaded_count > mLastCountAfterPurge)
	{
		LL_WARNS_ONCE("Animation") << loaded_count << " Loaded Motions. Amount of motions is over limit." << LL_ENDL;
	}
	mLastCountAfterPurge = loaded_count;
}

//-----------------------------------------------------------------------------
// deactivateStoppedMotions()
//-----------------------------------------------------------------------------
void LLMotionController::deactivateStoppedMotions()
{
	// Since we're hidden, deactivate any stopped motions.
	for (motion_list_t::iterator iter = mActiveMotions.begin();
		 iter != mActiveMotions.end(); )
	{
		motion_list_t::iterator curiter = iter++;
		LLMotion::ptr_t motionp(*curiter);
		if (motionp->isStopped())
		{
			deactivateMotionInstance(motionp);
		}
	}
}

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

	if (step != 0.f)
	{
		// make sure timestamps conform to new quantum
		for (motion_list_t::iterator iter = mActiveMotions.begin();
			 iter != mActiveMotions.end(); ++iter)
		{
			LLMotion::ptr_t motionp(*iter);
			F32 activation_time = motionp->mActivationTimestamp;
			motionp->mActivationTimestamp = (F32)(llfloor(activation_time / 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)
{ 
	mTimeFactor = time_factor; 
}

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


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

//-----------------------------------------------------------------------------
// removeMotion()
//-----------------------------------------------------------------------------
void LLMotionController::removeMotion( const LLUUID& id)
{
	LLMotion::ptr_t motionp(findMotion(id));
	mAllMotions.erase(id);
	removeMotionInstance(motionp);
}

// removes instance of a motion from all runtime structures, but does
// not erase entry by ID, as this could be a duplicate instance
// use removeMotion(id) to remove all references to a given motion by id.
void LLMotionController::removeMotionInstance(LLMotion::ptr_t &motionp)
{
	if (motionp)
	{
		llassert(findMotion(motionp->getID()) != motionp);
		if (motionp->isActive())
			motionp->deactivate();
		mLoadingMotions.erase(motionp);
		mLoadedMotions.erase(motionp);
		mActiveMotions.remove(motionp);
        motionp.reset();
	}
}

//-----------------------------------------------------------------------------
// createMotion()
//-----------------------------------------------------------------------------
LLMotion::ptr_t LLMotionController::createMotion( const LLUUID &id )
{
	if (id.isNull())
	{
		return NULL;
	}

	// do we have an instance of this motion for this character?
	LLMotion::ptr_t 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 LLMotion::ptr_t();
		}

		// 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:
			LL_INFOS() << "Motion " << id << " init failed." << LL_ENDL;
			sRegistry.markBad(id);
			motion.reset();
            return LLMotion::ptr_t();
        case LLMotion::STATUS_HOLD:
			mLoadingMotions.insert(motion);
			break;
		case LLMotion::STATUS_SUCCESS:
		    // add motion to our list
		    mLoadedMotions.insert(motion);
			break;
		default:
			LL_ERRS() << "Invalid initialization status" << LL_ENDL;
			break;
		}

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

//-----------------------------------------------------------------------------
// startMotion()
//-----------------------------------------------------------------------------
BOOL LLMotionController::startMotion(const LLUUID &id, F32 start_offset)
{
	// do we have an instance of this motion for this character?
	LLMotion::ptr_t motion(findMotion(id));

	// motion that is stopping will be allowed to stop but
	// replaced by a new instance of that motion
	if (motion
		&& !mPaused
		&& motion->canDeprecate()
		&& motion->getFadeWeight() > 0.01f // not LOD-ed out
		&& (motion->isBlending() || motion->getStopTime() != 0.f))
	{
		deprecateMotionInstance(motion);
		// force creation of new instance
		motion.reset();
	}

	// create new motion instance
	if (!motion)
	{
		motion = createMotion(id);
	}

	if (!motion)
	{
		return FALSE;
	}
	//if the motion is already active and allows deprecation, then let it keep playing
	else if (motion->canDeprecate() && isMotionActive(motion))
	{
		return TRUE;
	}

//	LL_INFOS() << "Starting motion " << name << LL_ENDL;
	return activateMotionInstance(motion, mAnimTime - start_offset);
}


//-----------------------------------------------------------------------------
// stopMotionLocally()
//-----------------------------------------------------------------------------
BOOL LLMotionController::stopMotionLocally(const LLUUID &id, BOOL stop_immediate)
{
	// if already inactive, return false
	LLMotion::ptr_t motion(findMotion(id));
    // SL-1290: always stop immediate if paused 
	return stopMotionInstance(motion, stop_immediate||mPaused);
}

BOOL LLMotionController::stopMotionInstance(LLMotion::ptr_t &motion, BOOL stop_immediate)
{
	if (!motion)
	{
		return FALSE;
	}

	// If on active list, stop it
	if (isMotionActive(motion) && !motion->isStopped())
	{
		motion->setStopTime(mAnimTime);
		if (stop_immediate)
		{
			deactivateMotionInstance(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_ANIMATED_JOINTS);
	memset(&mJointSignature[1][0], 0, sizeof(U8) * LL_CHARACTER_MAX_ANIMATED_JOINTS);
}

//-----------------------------------------------------------------------------
// updateIdleMotion()
// minimal updates for active motions
//-----------------------------------------------------------------------------
void LLMotionController::updateIdleMotion(LLMotion::ptr_t &motionp)
{
    LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
	if (motionp->isStopped() && mAnimTime > motionp->getStopTime() + motionp->getEaseOutDuration())
	{
		deactivateMotionInstance(motionp);
	}
	else if (motionp->isStopped() && mAnimTime > 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 (mAnimTime > 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 );
			stopMotionInstance(motionp, FALSE);
		}
	}
	else if (mAnimTime >= motionp->mActivationTimestamp)
	{
		if (mLastTime < motionp->mActivationTimestamp)
		{
			motionp->mResidualWeight = motionp->getPose()->getWeight();
		}
	}
}

//-----------------------------------------------------------------------------
// updateIdleActiveMotions()
// Call this instead of updateMotionsByType for hidden avatars
//-----------------------------------------------------------------------------
void LLMotionController::updateIdleActiveMotions()
{
    LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
	for (motion_list_t::iterator iter = mActiveMotions.begin();
		 iter != mActiveMotions.end(); )
	{
		motion_list_t::iterator curiter = iter++;
		LLMotion::ptr_t motionp(*curiter);
		updateIdleMotion(motionp);
	}
}

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

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

	// iterate through active motions in chronological order
	for (motion_list_t::iterator iter = mActiveMotions.begin();
		iter != mActiveMotions.end(); )
	{
		motion_list_t::iterator curiter = iter++;
		LLMotion::ptr_t motionp(*curiter);
		if (motionp->getBlendType() != anim_type)
		{
			continue;
		}

		if (!motionp->needsUpdate())
		{
			// as far as the motion knows: it doesn't need an update
			// but we still update it if its "joint signature"
			// causes a change to the accumulated signature stored in
			// 2D arraymJointSignature[][] (whatever that means)
			BOOL update_motion = FALSE;
			for (S32 i = 0; i < NUM_JOINT_SIGNATURE_STRIDES; i++)
			{
				// position = 0
		 		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;
				}

				// rotation  = 1
				*((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)
			{
				updateIdleMotion(motionp);
				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 (mAnimTime > 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 );
					stopMotionInstance(motionp, FALSE);
				}
			}

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

		//**********************
		// MOTION INACTIVE
		//**********************
		if (motionp->isStopped() && mAnimTime > 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);
				deactivateMotionInstance(motionp);
				continue;
			}
		}

		//**********************
		// MOTION EASE OUT
		//**********************
		else if (motionp->isStopped() && mAnimTime > 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 - ((mAnimTime - motionp->getStopTime()) / motionp->getEaseOutDuration())));
			}

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

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

			//should we notify the simulator that this motion should be stopped?
			if (mAnimTime > 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 );
					stopMotionInstance(motionp, FALSE);
				}
			}

			// perform motion update
			{
//				LL_RECORD_BLOCK_TIME(FTM_MOTION_ON_UPDATE);
				update_result = motionp->onUpdate(mAnimTime - motionp->mActivationTimestamp, last_joint_signature);
			}
		}

		//**********************
		// MOTION EASE IN
		//**********************
		else if (mAnimTime >= 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((mAnimTime - motionp->mActivationTimestamp) / motionp->getEaseInDuration()));
			}
			// perform motion update
			update_result = motionp->onUpdate(mAnimTime - 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() > mAnimTime)
			{
				// 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 );
				stopMotionInstance(motionp, FALSE);
			}

		}

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

//-----------------------------------------------------------------------------
// updateLoadingMotions()
//-----------------------------------------------------------------------------
void LLMotionController::updateLoadingMotions()
{
    LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
	// query pending motions for completion
	for (motion_set_t::iterator iter = mLoadingMotions.begin();
		 iter != mLoadingMotions.end(); )
	{
		motion_set_t::iterator curiter = iter++;
		LLMotion::ptr_t motionp(*curiter);
		if( !motionp)
		{
			continue; // maybe shouldn't happen but i've seen it -MG
		}
		LLMotion::LLMotionInitStatus status = motionp->onInitialize(mCharacter);
		if (status == LLMotion::STATUS_SUCCESS)
		{
			mLoadingMotions.erase(curiter);
			// add motion to our loaded motion list
			mLoadedMotions.insert(motionp);
			// this motion should be playing
			if (!motionp->isStopped())
			{
				activateMotionInstance(motionp, mAnimTime);
			}
		}
		else if (status == LLMotion::STATUS_FAILURE)
		{
			LL_INFOS() << "Motion " << motionp->getID() << " init failed." << LL_ENDL;
			sRegistry.markBad(motionp->getID());
			mLoadingMotions.erase(curiter);
			motion_set_t::iterator found_it = mDeprecatedMotions.find(motionp);
			if (found_it != mDeprecatedMotions.end())
			{
				mDeprecatedMotions.erase(found_it);
			}
			mAllMotions.erase(motionp->getID());
			motionp.reset();
		}
	}
}

//-----------------------------------------------------------------------------
// call updateMotion() or updateMotionsMinimal() every frame
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// updateMotion()
//-----------------------------------------------------------------------------
void LLMotionController::updateMotions(bool force_update)
{
    LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
    // SL-763: "Distant animated objects run at super fast speed"
    // The use_quantum optimization or possibly the associated code in setTimeStamp()
    // does not work as implemented.
    // Currently setting mTimeStep to nonzero is disabled elsewhere.
	BOOL use_quantum = (mTimeStep != 0.f);

	// Always update mPrevTimerElapsed
	F32 cur_time = mTimer.getElapsedTimeF32();
	F32 delta_time = cur_time - mPrevTimerElapsed;
	mPrevTimerElapsed = cur_time;
	mLastTime = mAnimTime;

	// Always cap the number of loaded motions
	purgeExcessMotions();
		
	// Update timing info for this time step.
	if (!mPaused)
	{
		F32 update_time = mAnimTime + delta_time * 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;
				}

				updateLoadingMotions();
				
				return;
			}
			
			// is calculating a new keyframe pose, make sure the last one gets applied
			mPoseBlender.interpolate(1.f);
	        mPoseBlender.clearBlenders();

			mTimeStepCount = quantum_count;
			mAnimTime = (F32)quantum_count * mTimeStep;
			mLastInterp = 0.f;
		}
		else
		{
			mAnimTime = llmax(mAnimTime, update_time);
		}
	}

	updateLoadingMotions();
	
	resetJointSignatures();

	if (mPaused && !force_update)
	{
		updateIdleActiveMotions();
	}
	else
	{
		// update additive motions
		updateAdditiveMotions();
				
		resetJointSignatures();
		
		// update all regular motions
		updateRegularMotions();
		
		if (use_quantum)
		{
			mPoseBlender.blendAndCache(TRUE);
		}
		else
		{
			mPoseBlender.blendAndApply();
		}
	}

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

//-----------------------------------------------------------------------------
// updateMotionsMinimal()
// minimal update (e.g. while hidden)
//-----------------------------------------------------------------------------
void LLMotionController::updateMotionsMinimal()
{
    LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
	// Always update mPrevTimerElapsed
	mPrevTimerElapsed = mTimer.getElapsedTimeF32();

	purgeExcessMotions();
	updateLoadingMotions();
	resetJointSignatures();

	deactivateStoppedMotions();

	mHasRunOnce = TRUE;
}

//-----------------------------------------------------------------------------
// activateMotionInstance()
//-----------------------------------------------------------------------------
BOOL LLMotionController::activateMotionInstance(const LLMotion::ptr_t &motion, F32 time)
{
    LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
	// It's not clear why the getWeight() line seems to be crashing this, but
	// hopefully this fixes it.
	if (motion == NULL || motion->getPose() == NULL)
	{
		return FALSE;	
	}

	if (mLoadingMotions.find(motion) != mLoadingMotions.end())
	{
		// 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();

	// 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;
	}
	
	if (motion->isActive())
	{
		mActiveMotions.remove(motion);
	}
	mActiveMotions.push_front(motion);

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

	if (mAnimTime >= motion->mSendStopTimestamp)
	{
		motion->setStopTime(motion->mSendStopTimestamp);
		if (motion->mResidualWeight == 0.0f)
		{
			// bit of a hack; if newly activating a motion while easing out, weight should = 1
			motion->mResidualWeight = 1.f;
		}
	}
	
	return TRUE;
}

//-----------------------------------------------------------------------------
// deactivateMotionInstance()
//-----------------------------------------------------------------------------
BOOL LLMotionController::deactivateMotionInstance(LLMotion::ptr_t &motion)
{
	motion->deactivate();

	motion_set_t::iterator found_it = mDeprecatedMotions.find(motion);
	if (found_it != mDeprecatedMotions.end())
	{
		// deprecated motions need to be completely excised
		removeMotionInstance(motion);	
		mDeprecatedMotions.erase(found_it);
	}
	else
	{
		// for motions that we are keeping, simply remove from active queue
		mActiveMotions.remove(motion);
	}

	return TRUE;
}

void LLMotionController::deprecateMotionInstance(LLMotion::ptr_t &motion)
{
	llassert(motion);
	mDeprecatedMotions.insert(motion);

	//fade out deprecated motion
	stopMotionInstance(motion, FALSE);
	//no longer canonical
	mAllMotions.erase(motion->getID());
}

//-----------------------------------------------------------------------------
// isMotionActive()
//-----------------------------------------------------------------------------
bool LLMotionController::isMotionActive(const LLMotion::ptr_t &motion)
{
	return (motion && motion->isActive());
}

//-----------------------------------------------------------------------------
// isMotionLoading()
//-----------------------------------------------------------------------------
bool LLMotionController::isMotionLoading(const LLMotion::ptr_t &motion)
{
	return (mLoadingMotions.find(motion) != mLoadingMotions.end());
}


//-----------------------------------------------------------------------------
// findMotion()
//-----------------------------------------------------------------------------
LLMotion::ptr_t LLMotionController::findMotion(const LLUUID& id) const
{
	motion_map_t::const_iterator iter = mAllMotions.find(id);
	if(iter == mAllMotions.end())
	{
		return LLMotion::ptr_t();
	}
	else
	{
		return iter->second;
	}
}

//-----------------------------------------------------------------------------
// dumpMotions()
//-----------------------------------------------------------------------------
void LLMotionController::dumpMotions()
{
	LL_INFOS() << "=====================================" << LL_ENDL;
	for (motion_map_t::iterator iter = mAllMotions.begin();
		 iter != mAllMotions.end(); iter++)
	{
		LLUUID id = iter->first;
		std::string state_string;
		LLMotion::ptr_t motion(iter->second);
		if (mLoadingMotions.find(motion) != mLoadingMotions.end())
			state_string += std::string("l");
		if (mLoadedMotions.find(motion) != mLoadedMotions.end())
			state_string += std::string("L");
		if (std::find(mActiveMotions.begin(), mActiveMotions.end(), motion)!=mActiveMotions.end())
			state_string += std::string("A");
		if (mDeprecatedMotions.find(motion) != mDeprecatedMotions.end())
			state_string += std::string("D");
		LL_INFOS() << gAnimLibrary.animationName(id) << " " << state_string << LL_ENDL;
		
	}
}

//-----------------------------------------------------------------------------
// deactivateAllMotions()
//-----------------------------------------------------------------------------
void LLMotionController::deactivateAllMotions()
{
	for (motion_map_t::iterator iter = mAllMotions.begin();
		 iter != mAllMotions.end(); iter++)
	{
		LLMotion::ptr_t motionp(iter->second);
		deactivateMotionInstance(motionp);
	}
}


//-----------------------------------------------------------------------------
// flushAllMotions()
//-----------------------------------------------------------------------------
void LLMotionController::flushAllMotions()
{
	std::vector<std::pair<LLUUID,F32> > active_motions;
	active_motions.reserve(mActiveMotions.size());
	for (motion_list_t::iterator iter = mActiveMotions.begin();
		 iter != mActiveMotions.end(); )
	{
		motion_list_t::iterator curiter = iter++;
		LLMotion::ptr_t motionp(*curiter);
		F32 dtime = mAnimTime - motionp->mActivationTimestamp;
		active_motions.push_back(std::make_pair(motionp->getID(),dtime));
		motionp->deactivate(); // don't call deactivateMotionInstance() because we are going to reactivate it
	}
 	mActiveMotions.clear();
	
	// delete all motion instances
	deleteAllMotions();

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

	// restart motions
	for (const auto& motion_pair : active_motions)
	{
		startMotion(motion_pair.first, motion_pair.second);
	}
}

//-----------------------------------------------------------------------------
// pause()
//-----------------------------------------------------------------------------
void LLMotionController::pauseAllMotions()
{
	if (!mPaused)
	{
		//LL_INFOS() << "Pausing animations..." << LL_ENDL;
		mPaused = TRUE;
        mPausedFrame = LLFrameTimer::getFrameCount();
	}
	
}

//-----------------------------------------------------------------------------
// unpause()
//-----------------------------------------------------------------------------
void LLMotionController::unpauseAllMotions()
{
	if (mPaused)
	{
		//LL_INFOS() << "Unpausing animations..." << LL_ENDL;
		mPaused = FALSE;
	}
}
// End
