/** 
 * @file llmotioncontroller.h
 * @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$
 */

#ifndef LL_LLMOTIONCONTROLLER_H
#define LL_LLMOTIONCONTROLLER_H

//-----------------------------------------------------------------------------
// Header files
//-----------------------------------------------------------------------------
#include <string>
#include <map>
#include <deque>

#include "llmotion.h"
#include "llpose.h"
#include "llframetimer.h"
#include "llstring.h"

#include "boost/unordered/unordered_map.hpp"
#include "boost/unordered/unordered_flat_map.hpp"
#include "boost/unordered/unordered_flat_set.hpp"

//-----------------------------------------------------------------------------
// Class predeclaration
// This is necessary because llcharacter.h includes this file.
//-----------------------------------------------------------------------------
class LLCharacter;

//-----------------------------------------------------------------------------
// LLMotionRegistry
//-----------------------------------------------------------------------------
typedef std::function<LLMotion::ptr_t(const LLUUID &)> LLMotionConstructor;

class LLMotionRegistry
{
public:
	// Constructor
	LLMotionRegistry() = default;

	// Destructor
	~LLMotionRegistry();

	// adds motion classes to the registry
	// returns true if successful
	BOOL registerMotion( const LLUUID& id, LLMotionConstructor create);

	// creates a new instance of a named motion
	// returns NULL motion is not registered
	LLMotion::ptr_t createMotion( const LLUUID &id );

	// initialization of motion failed, don't try to create this motion again
	void markBad( const LLUUID& id );


protected:
	typedef boost::unordered_map<LLUUID, LLMotionConstructor> motion_map_t;
	motion_map_t mMotionTable;
};

//-----------------------------------------------------------------------------
// class LLMotionController
//-----------------------------------------------------------------------------
class LLMotionController
{
public:
    // ?should these be weak?
	typedef std::list<LLMotion::ptr_t> motion_list_t;
	typedef std::set<LLMotion::ptr_t> motion_set_t;
	BOOL mIsSelf;
	
public:
	// Constructor
	LLMotionController();

	// Destructor
	virtual ~LLMotionController();

	// set associated character
	// this must be called exactly once by the containing character class.
	// this is generally done in the Character constructor
	void setCharacter( LLCharacter *character );

	// registers a motion with the controller
	// (actually just forwards call to motion registry)
	// returns true if successfull
	BOOL registerMotion( const LLUUID& id, LLMotionConstructor create );

	// creates a motion from the registry
	LLMotion::ptr_t createMotion( const LLUUID &id );

	// unregisters a motion with the controller
	// (actually just forwards call to motion registry)
	// returns true if successfull
	void removeMotion( const LLUUID& id );

	// start motion
	// begins playing the specified motion
	// returns true if successful
	BOOL startMotion( const LLUUID &id, F32 start_offset );

	// stop motion
	// stops a playing motion
	// in reality, it begins the ease out transition phase
	// returns true if successful
	BOOL stopMotionLocally( const LLUUID &id, BOOL stop_immediate );

	// Move motions from loading to loaded
	void updateLoadingMotions();
	
	// update motions
	// invokes the update handlers for each active motion
	// activates sequenced motions
	// deactivates terminated motions`
	void updateMotions(bool force_update = false);

	// minimal update (e.g. while hidden)
	void updateMotionsMinimal();

	// flush motions
	// releases all motion instances
	void flushAllMotions();

	//Flush is a liar.
	void deactivateAllMotions();	

	// pause and continue all motions
	void pauseAllMotions();
	void unpauseAllMotions();
	BOOL isPaused() const { return mPaused; }
    S32 getPausedFrame() const { return mPausedFrame; }

	void setTimeStep(F32 step);
    F32 getTimeStep() const { return mTimeStep; }

	void setTimeFactor(F32 time_factor);
	F32 getTimeFactor() const { return mTimeFactor; }

    F32 getAnimTime() const { return mAnimTime; }
    
	motion_list_t& getActiveMotions() { return mActiveMotions; }

	void incMotionCounts(S32& num_motions, S32& num_loading_motions, S32& num_loaded_motions, S32& num_active_motions, S32& num_deprecated_motions);
	
//protected:
	bool isMotionActive(const LLMotion::ptr_t &motion );
	bool isMotionLoading(const LLMotion::ptr_t &motion );
	LLMotion::ptr_t findMotion( const LLUUID& id ) const;

	void dumpMotions();

	const LLFrameTimer& getFrameTimer() { return mTimer; }

	static F32	getCurrentTimeFactor()				{ return sCurrentTimeFactor;	};
	static void setCurrentTimeFactor(F32 factor)	{ sCurrentTimeFactor = factor;	};

protected:
	// internal operations act on motion instances directly
	// as there can be duplicate motions per id during blending overlap
	void deleteAllMotions();
	BOOL activateMotionInstance(const LLMotion::ptr_t &motion, F32 time);
	BOOL deactivateMotionInstance(LLMotion::ptr_t &motion);
	void deprecateMotionInstance(LLMotion::ptr_t &motion);
	BOOL stopMotionInstance(LLMotion::ptr_t &motion, BOOL stop_imemdiate);
	void removeMotionInstance(LLMotion::ptr_t &motion); // note non const ref
	void updateRegularMotions();
	void updateAdditiveMotions();
	void resetJointSignatures();
	void updateMotionsByType(LLMotion::LLMotionBlendType motion_type);
	void updateIdleMotion(LLMotion::ptr_t &motionp);
	void updateIdleActiveMotions();
	void purgeExcessMotions();
	void deactivateStoppedMotions();

protected:
	F32					mTimeFactor;			// 1.f for normal speed
	static F32			sCurrentTimeFactor;		// Value to use for initialization
	static LLMotionRegistry	sRegistry;
	LLPoseBlender		mPoseBlender;

	LLCharacter			*mCharacter;

//	Life cycle of an animation:
//
//	Animations are instantiated and immediately put in the mAllMotions map for their entire lifetime.
//	If the animations depend on any asset data, the appropriate data is fetched from the data server,
//	and the animation is put on the mLoadingMotions list.
//	Once an animations is loaded, it will be initialized and put on the mLoadedMotions list.
//	Any animation that is currently playing also sits in the mActiveMotions list.

	typedef std::map<LLUUID, LLMotion::ptr_t> motion_map_t;
	motion_map_t	mAllMotions;

	motion_set_t		mLoadingMotions;
	motion_set_t		mLoadedMotions;
	motion_list_t		mActiveMotions;
	motion_set_t		mDeprecatedMotions;
	
	LLFrameTimer		mTimer;
	F32					mPrevTimerElapsed;
	F32					mAnimTime;
	F32					mLastTime;
	BOOL				mHasRunOnce;
	BOOL				mPaused;
	S32					mPausedFrame;
	F32					mTimeStep;
	S32					mTimeStepCount;
	F32					mLastInterp;

	U8					mJointSignature[2][LL_CHARACTER_MAX_ANIMATED_JOINTS];
private:
	U32					mLastCountAfterPurge; //for logging and debugging purposes
};

//-----------------------------------------------------------------------------
// Class declaractions
//-----------------------------------------------------------------------------
#include "llcharacter.h"

#endif // LL_LLMOTIONCONTROLLER_H

