/** 
 * @file llfollowcam.h
 * @author Jeffrey Ventrella
 * @brief LLFollowCam class definition
 *
 * $LicenseInfo:firstyear=2005&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$
 */

//--------------------------------------------------------------------
// FollowCam
//
// The FollowCam controls three dynamic variables which determine 
// a camera orientation and position for a "loose" third-person view
// (orientation being derived from a combination of focus and up 
// vector). It is good for fast-moving vehicles that change 
// acceleration a lot, but it can also be general-purpose, like for 
// avatar navigation. It has a handful of parameters allowing it to 
// be tweaked to assume different styles of tracking objects.
//
//--------------------------------------------------------------------
#ifndef LL_FOLLOWCAM_H
#define LL_FOLLOWCAM_H

#include "llcoordframe.h"
#include "indra_constants.h"
#include "llmath.h"
#include "lltimer.h"
#include "llquaternion.h"
#include "llcriticaldamp.h"
#include <map>
#include <vector>

class LLFollowCamParams
{
public:
	LLFollowCamParams();
	virtual ~LLFollowCamParams();
	
	//--------------------------------------
	// setty setty set set
	//--------------------------------------
	virtual void setPositionLag			( F32 );
	virtual void setFocusLag			( F32 );
	virtual void setFocusThreshold		( F32 );
	virtual void setPositionThreshold	( F32 );
	virtual void setDistance			( F32 );
	virtual void setPitch				( F32 );
	virtual void setFocusOffset			( const LLVector3& );
	virtual void setBehindnessAngle		( F32 );
	virtual void setBehindnessLag		( F32 );
	virtual void setPosition			( const LLVector3& );
	virtual void setFocus				( const LLVector3& );
	virtual void setPositionLocked		( bool );
	virtual void setFocusLocked			( bool );


	//--------------------------------------
	// getty getty get get
	//--------------------------------------
	virtual F32			getPositionLag() const;
	virtual F32			getFocusLag() const;
	virtual F32			getPositionThreshold() const;
	virtual F32			getFocusThreshold() const;
	virtual F32			getDistance() const;
	virtual F32			getPitch() const;
	virtual LLVector3	getFocusOffset() const;
	virtual F32			getBehindnessAngle() const;
	virtual F32			getBehindnessLag() const;
	virtual LLVector3	getPosition() const;
	virtual LLVector3	getFocus() const;
	virtual bool		getFocusLocked() const;
	virtual bool		getPositionLocked() const;
	virtual bool		getUseFocus() const { return mUseFocus; }
	virtual bool		getUsePosition() const { return mUsePosition; }

protected:
	F32		mPositionLag;
	F32		mFocusLag;
	F32		mFocusThreshold;
	F32		mPositionThreshold;
	F32		mDistance;	
	F32		mPitch;
	LLVector3	mFocusOffset;
	F32		mBehindnessMaxAngle;
	F32		mBehindnessLag;
	F32		mMaxCameraDistantFromSubject;

	bool			mPositionLocked;
	bool			mFocusLocked;
	bool			mUsePosition; // specific camera point specified by script
	bool			mUseFocus; // specific focus point specified by script
	LLVector3		mPosition;			// where the camera is (in world-space)
	LLVector3		mFocus;				// what the camera is aimed at (in world-space)
};

class LLFollowCam : public LLFollowCamParams
{
public:
	//--------------------
	// Contructor
	//--------------------
	LLFollowCam();

	//--------------------
	// Destructor
	//--------------------
	virtual ~LLFollowCam();

	//---------------------------------------------------------------------------------------
	// The following methods must be called every time step. However, if you know for 
	// sure that your  subject matter (what the camera is looking at) is not moving, 
	// then you can get away with not calling "update" But keep in mind that "update" 
	// may still be needed after the subject matter has stopped moving because the 
	// camera may still need to animate itself catching up to its ideal resting place. 
	//---------------------------------------------------------------------------------------
	void setSubjectPositionAndRotation	( const LLVector3 p, const LLQuaternion r );
	void update();

	// initialize from another instance of llfollowcamparams
	void copyParams(LLFollowCamParams& params);

	//-----------------------------------------------------------------------------------
	// this is how to bang the followCam into a specific configuration. Keep in mind 
	// that it will immediately try to adjust these values according to its attributes. 
	//-----------------------------------------------------------------------------------
	void reset( const LLVector3 position, const LLVector3 focus, const LLVector3 upVector );

	void setMaxCameraDistantFromSubject	( F32 m ); // this should be determined by llAgent
	bool isZoomedToMinimumDistance();
	LLVector3	getUpVector();
	void zoom( S32 );

	// overrides for setters and getters
	virtual void setPitch( F32 );
	virtual void setDistance( F32 );
	virtual void setPosition(const LLVector3& pos);
	virtual void setFocus(const LLVector3& focus);
	virtual void setPositionLocked		( bool );
	virtual void setFocusLocked			( bool );

	LLVector3	getSimulatedPosition() const;
	LLVector3	getSimulatedFocus() const;

	//------------------------------------------
	// protected members of FollowCam
	//------------------------------------------
protected:
	F32		mPitchCos;					// derived from mPitch
	F32		mPitchSin;					// derived from mPitch
	LLGlobalVec		mSimulatedPositionGlobal;		// where the camera is (global coordinates), simulated
	LLGlobalVec		mSimulatedFocusGlobal;		// what the camera is aimed at (global coordinates), simulated
	F32				mSimulatedDistance;

	//---------------------
	// dynamic variables
	//---------------------
	bool			mZoomedToMinimumDistance;
	LLFrameTimer	mTimer;
	LLVector3		mSubjectPosition;	// this is the position of what I'm looking at
	LLQuaternion	mSubjectRotation;	// this is the rotation of what I'm looking at
	LLVector3		mUpVector;			// the camera's up vector in world-space (determines roll)
	LLVector3		mRelativeFocus;
	LLVector3		mRelativePos;

	bool mPitchSineAndCosineNeedToBeUpdated;

	//------------------------------------------
	// protected methods of FollowCam
	//------------------------------------------
protected:
		void	calculatePitchSineAndCosine();
		BOOL	updateBehindnessConstraint(LLVector3 focus, LLVector3& cam_position);

};// end of FollowCam class


class LLFollowCamMgr : public LLSingleton<LLFollowCamMgr>
{
    LLSINGLETON(LLFollowCamMgr);
    ~LLFollowCamMgr();
public:	
	void setPositionLag			( const LLUUID& source, F32 lag);
	void setFocusLag				( const LLUUID& source, F32 lag);
	void setFocusThreshold		( const LLUUID& source, F32 threshold);
	void setPositionThreshold	( const LLUUID& source, F32 threshold);
	void setDistance				( const LLUUID& source, F32 distance);
	void setPitch				( const LLUUID& source, F32 pitch);
	void setFocusOffset			( const LLUUID& source, const LLVector3& offset);
	void setBehindnessAngle		( const LLUUID& source, F32 angle);
	void setBehindnessLag		( const LLUUID& source, F32 lag);
	void setPosition				( const LLUUID& source, const LLVector3 position);
	void setFocus				( const LLUUID& source, const LLVector3 focus);
	void setPositionLocked		( const LLUUID& source, bool locked);
	void setFocusLocked			( const LLUUID& source, bool locked );

	void setCameraActive			( const LLUUID& source, bool active );

	LLFollowCamParams* getActiveFollowCamParams();
	LLFollowCamParams* getParamsForID(const LLUUID& source);
	void removeFollowCamParams(const LLUUID& source);
	bool isScriptedCameraSource(const LLUUID& source);
	void dump();

protected:

	typedef std::map<LLUUID, LLFollowCamParams*> param_map_t;
	param_map_t mParamMap;

	typedef std::vector<LLFollowCamParams*> param_stack_t;
	param_stack_t mParamStack;
};

#endif //LL_FOLLOWCAM_H