
////////////////////////////////////////////////////////////////////////////////
//
//	Asura_Moves.h
//
//	Created by CJW - 09-March-2007
//
//	Copyright (C) 2007 Rebellion, All Rights Reserved.
//
////////////////////////////////////////////////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
#pragma once
#endif

#ifndef __ASURA_MOVES_H__
#define __ASURA_MOVES_H__

#if defined(ASURA_USE_MOVEMENT_SYSTEM)

// I N C L U D E S /////////////////////////////////////////////////////////////

#include "Core/Asura_HashTree.h"
#include "Core/Asura_Maths.h"
#include "Entity/PhysicalObject/Moves/Asura_MoveData_Animation.h"
#include "Entity/PhysicalObject/Moves/Asura_MoveData_OrientationInterpolation.h"
#include "Entity/PhysicalObject/Moves/Asura_MoveData_PositionInterpolation.h"


// F O R W A R D   D E C L A R A T I O N S /////////////////////////////////////

class Asura_ClientEntity_PhysicalObject;
class Asura_Movement_System;
class Asura_Msg_Move;


// C L A S S E S ///////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
//
//	Class Name	: Asura_Move
//
//	Author		: Richard May
//
//	Description	: Base move class with chaining support. Includes
//				  functions for both anim-based and position-based
//				  moves.
//
////////////////////////////////////////////////////////////////////////////////

class Asura_Move
{
	// Friend classes...
	friend class Asura_Movement_System;

public:
	// Public enumerated types...
	enum OrientationUsage
	{
		ORIENTATION_NOT_USED,
		ORIENTATION_SET_DIRECTLY,

		NUM_ORIENTATION_USAGES
	};

	enum PositionUsage
	{
		POSITION_NOT_USED,
		POSITION_SET_FROM_VELOCITY,
		POSITION_SET_DIRECTLY,

		NUM_POSITION_USAGES
	};

	// Public functions...
    Asura_Move(Asura_Movement_System& xParentSystem, const Asura_Hash_ID uMoveHash);
	virtual ~Asura_Move()	{}

	// Functions to add chains of moves...
	void	 AddNextMove(Asura_Move* pxNext);
	void	 AddNextMove(const Asura_Hash_ID uHash);
	void	 SetNextMoves(u_int uCount, ...);
	void	 SetNextMovesByHash(u_int uCount, ...);
	void	 ResetNextMoves();
	
	// Virtual query functions...
	virtual bool		HasFinished(void) const;

	virtual bool		CanBeAborted(void) const	{ return(true); }
	virtual const char*	GetName(void) const			{ return("<UNKNOWN MOVE>"); }

	// Accessor functions...
	Asura_Movement_System&			GetParentSystem(void)		{ return(m_xParentSystem); }
	const Asura_Movement_System&	GetParentSystem(void) const	{ return(m_xParentSystem); }

	void	SetGravityIgnored(const bool bSet)			{ m_bGravityIgnored = bSet; }
	void	SetAlwaysCheckNextMoves(const bool bSet)	{ m_bAlwaysCheckNextMoves = bSet; }
	void	SetKeepPosing(const bool bSet)				{ m_bKeepPosing = bSet; }
	// Loop at end == looping the anim completely.
	void	SetLoopAtEnd(const bool bSet)				{ m_bLoopAtEnd = bSet; }

	void	SetSuppressStartMessage(const bool bSet)	{ m_bSuppressStartMessage = bSet; }
	void	SetSuppressAbortMessage(const bool bSet)	{ m_bSuppressAbortMessage = bSet; }
	void	SetSuppressFinishMessage(const bool bSet)	{ m_bSuppressFinishMessage = bSet; }
	void	SetSuppressAllMessages(const bool bSet)		{ m_bSuppressStartMessage = m_bSuppressAbortMessage = m_bSuppressFinishMessage = bSet; }

	bool	IsGravityIgnored(void) const				{ return(m_bGravityIgnored); }
	bool 	IsPlayingAnimation(void) const;
	bool	ShouldNextMoveIgnoreMovePos(void) const		{ return GetPositionInterpolationData() == NULL; }
	bool	ShouldNextMoveIgnoreMoveOri(void) const		{ return GetOrientationInterpolationData() == NULL; }

	OrientationUsage	GetOrientationUsage(void) const	{ return(static_cast<OrientationUsage>(m_eOrientationUsage)); }
	PositionUsage		GetPositionUsage(void) const	{ return(static_cast<PositionUsage>(m_ePositionUsage)); }

	Asura_Hash_ID	GetHash(void) const	{ return(m_uHash); }
	u_int			GetAnimFlagsForLastFrame(void) const;

	bool GetSuppressStartMessage() const { return m_bSuppressStartMessage; }
	bool GetSuppressAbortMessage() const { return m_bSuppressAbortMessage; }
	bool GetSuppressFinishMessage() const { return m_bSuppressFinishMessage; }

protected:
	// Protected typedefs...
	typedef Asura_TemplateHashTree<Asura_Move*> NextMoveList;

	// Protected variables...
	Asura_Movement_System&	m_xParentSystem;	// A reference to the movement-system to use.
	Asura_Hash_ID			m_uHash;			// The hash-ID of this move.

	// Protected functions...
	Asura_Move*	GetNextMove();

	// Initialisation functions...
    // Warning: These functions keep a pointer to the data passed in, so this data must not be on the stack.
	void	InitialiseAnimation(Asura_MoveData_Animation& xAnimationData, Asura_AVector3* pxSingularityAxis = NULL);
	void	InitialiseOrientationInterpolation(Asura_MoveData_OrientationInterpolation& xOrientationInterpolationData);
	void	InitialisePositionInterpolation(Asura_MoveData_PositionInterpolation& xPositionInterpolationData);

	// Virtual functions...
	virtual void		Reset();

	virtual bool		ShouldStart() const	{ return(true); }
	virtual bool		Start(const Asura_Msg_Move* pxStartMsg);
	virtual Asura_Move*	Update();
	virtual void		Render() const;
	virtual void		Abort();
	virtual void		Finish();

	virtual void		SendStartMsg();

	// Virtual state functions...
	virtual void	CheckStatesToStart();
	virtual void	UpdateStates(const float fTimeStep);

	// Virtual movement calculation functions...
	virtual bool	CalculatePosition(const float fTimeStep, Asura_AVector3_OutParam xPosition) const;
	virtual bool	CalculateOrientation(const float fTimeStep, Asura_Quat& xOrientation) const;

	virtual bool	CalculateVelocity(const float fTimeStep, Asura_AVector3_OutParam xVelocity) const;

	// Accessor functions...
	void	SetOrientationUsage(const OrientationUsage eMethod)	{ m_eOrientationUsage = eMethod; }
	void	SetPositionUsage(const PositionUsage eMethod)		{ m_ePositionUsage = eMethod; }

	bool	IsReadyToStart(const Asura_MoveData& xMoveData) const;

	Asura_MoveData_PositionInterpolation* GetPositionInterpolationData() { return m_pxPositionInterpolationData; }
	const Asura_MoveData_PositionInterpolation* GetPositionInterpolationData() const { return m_pxPositionInterpolationData; }
	Asura_MoveData_OrientationInterpolation* GetOrientationInterpolationData() { return m_pxOrientationInterpolationData; }
	const Asura_MoveData_OrientationInterpolation* GetOrientationInterpolationData() const { return m_pxOrientationInterpolationData; }

	bool			StartAnimation();
	virtual bool	UpdateAnimation(const float fTimeStep);
	virtual void	FinishAnimation();
	void			ResetAnimation();

	bool			StartOrientationInterpolation();
	bool			UpdateOrientationInterpolation(const float fTimeStep);
	virtual void	FinishOrientationInterpolation();
	void			ResetOrientationInterpolation();

	bool			StartPositionInterpolation();
	bool			UpdatePositionInterpolation(const float fTimeStep);
	virtual void	FinishPositionInterpolation();
	void			ResetPositionInterpolation();

	void			SetParentSystemLSMOFinalOrientation( const Asura_Quat& xLSMOFinalOri );
	void			SetParentSystemLSMOFinalPosition( const Asura_AVector3& xLSMOFinalPos );

	// Function to allow subclasses to reset move flags if they're variable per start.
	void ClearFlags() { m_uFlags = 0; }

	// Static functions...
	static bool	Traverse_GetNextMove(Asura_Hash_ID uMoveHashID, Asura_Move** ppxMove, void* pUserData);


private:
	// Private data...
	NextMoveList		m_xNextMoveTree;	// List of moves which can follow this one.
	const Asura_Move*	m_pxPrevMove;		// Pointer to the move that triggered this (may be NULL)

	// Animation data...
	Asura_MoveData_Animation*	m_pxAnimationData;				// A pointer to the animation-data to use.

	// Interpolation data...
	Asura_MoveData_PositionInterpolation*		m_pxPositionInterpolationData;		// A pointer to the position interpolation data for this move.
	Asura_MoveData_OrientationInterpolation*	m_pxOrientationInterpolationData;	// A pointer to the orientation interpolation data for this move.

	// Stationary data...
	float	m_fStationaryTimer;		// The number of seconds we've been stationary (i.e. in exactly the same position).
	float	m_fStationaryEpsilon;	// The value to use when checking whether we're actually stationary.

	// Flags...
	union
	{
		u_int		m_uFlags;										// Single access point for all of the flags for this move.

		struct
		{
			u_int	m_bGravityIgnored						: 1,	// Gravity should be ignored during this move.
					m_bAlwaysCheckNextMoves					: 1,	// Allow us to check whether we finish the current move because we have a next move to start.
					m_bKeepPosing							: 1,	// Keep our posable-animation during the move.
					m_bLoopAtEnd							: 1,	// Stay in our move once its finished animating/interpolating.
					m_bSnapToPosAndOri						: 1,	// Snap directly to the target position and orientation on finish.
					m_bClampVelocityY						: 1,	// Remove the Y component of the velocity during this move.
					m_bSuppressStartMessage					: 1,	// Prevent start messages from being sent.
					m_bSuppressAbortMessage					: 1,	// Prevent abort messages from being sent.
					m_bSuppressFinishMessage				: 1,	// Prevent finish messages from being sent.

					m_eOrientationUsage						: 4,	// The method used when updating the orientation.
					m_ePositionUsage						: 4,	// The method used when updating the position.

															: 0;	// 13 spare-bits
		};
	};

	// Static data...

};		// End of class "Asura_Move".

#endif	// defined(ASURA_USE_MOVEMENT_SYSTEM)

#endif//__ASURA_MOVES_H__


/// End of file. ///
