
///////////////////////////////////////////////////////////////////////////////
//
// File   : AVP_Actor_AIAnimStateMachine.h
// Author : Andy Tate
// Date   : 14-Dec-2006
//
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
	#pragma once
#endif   // ifdef ASURA_USE_PRAGMA_ONCE

#ifndef __AVP_ACTOR_AIANIMSTATEMACHINE_H__
#define __AVP_ACTOR_AIANIMSTATEMACHINE_H__

////////////////////////////////////////////////////////////////////////////////
// Include files...
////////////////////////////////////////////////////////////////////////////////

#include "Core/Asura_Maths.h"

#include "Actor/AVP_Actor_AnimStateData.h"


////////////////////////////////////////////////////////////////////////////////
// Forward class declarations...
////////////////////////////////////////////////////////////////////////////////

class Asura_Message;

class AVP_Actor_AIAnimStateConnectionData;
class AVP_Collision_Descriptor;
class AVP_ServerEntity_Actor;

////////////////////////////////////////////////////////////////////////////////
// Class declaration...
////////////////////////////////////////////////////////////////////////////////

#define AVP_ACTOR_AIANIMSTATEMACHINE_MAX_STATES				64
#define AVP_ACTOR_AIANIMSTATEMACHINE_MAX_STATES_IN_QUEUE	10

////////////////////////////////////////////////////////////////////////////////
// Class        : AVP_Actor_AIAnimStateMachine
// Author       : Andy Tate
// Date         : 14-Dec-2006
//
// Description  : Class to handle state changing for actors.
////////////////////////////////////////////////////////////////////////////////

class AVP_Actor_AIAnimStateMachine
{
public:
	enum ActorAnimState
	{
		ACTOR_STATE_BEGIN = 0,	
		ACTOR_STATE_DEAD = ACTOR_STATE_BEGIN,	// 0
		ACTOR_STATE_IN_MOVE,					// 1
		ACTOR_STATE_IN_GAMESCENE,				// 2
		ACTOR_STATE_IDLE,						// 3
		ACTOR_STATE_END							// 4
	};

	AVP_Actor_AIAnimStateMachine( AVP_ServerEntity_Actor& xServerActor );
	virtual ~AVP_Actor_AIAnimStateMachine();

	virtual void Initialise() { BeginState( ACTOR_STATE_IDLE ); }

	void			SetNeedToTween( float fTweenTime ) { m_fCurrentTweenTime = Asura_Maths::Max( fTweenTime, m_fCurrentTweenTime ); }
	void			SetPlayNextAnimFromStart() { m_bPlayNextAnimFromStart = true; }

	// State Accessors
	u_int			GetCurrentState() const { return m_uCurrentState; }
	u_int			GetTargetState() const;
	bool			HasStateInQueue() const { return m_uStateQueueStart != m_uStateQueueEnd; }
	float			GetTimeInState() const { return m_fTimeInState; }

	void			Update();
	
	virtual bool	HandleMessage( const Asura_Message* pxMessage );

	void			RequestIdle()								{ RequestState( ACTOR_STATE_IDLE ); }
	bool			IsIdle()							const	{ return IsStateIdle( m_uCurrentState ); }
	bool			IsStateIdle( const u_int uState )	const	{ return ACTOR_STATE_IDLE == uState; }

	void			RequestDead()								{ if ( m_uCurrentState == ACTOR_STATE_IN_MOVE || m_uCurrentState == ACTOR_STATE_IN_GAMESCENE ) { ForceState( ACTOR_STATE_DEAD ); } else { RequestState( ACTOR_STATE_DEAD ); } }
	bool			IsDead()							const	{ return IsStateDead( m_uCurrentState ); }
	bool			IsStateDead( const u_int uState )	const	{ return ACTOR_STATE_DEAD == uState; }

	void			RequestInGamescene()						{ RequestState( ACTOR_STATE_IN_GAMESCENE ); m_uStateAfterMove = ACTOR_STATE_IDLE; }
	void			RequestEndGamescene()						{ m_bGamesceneFinished = true; RequestState( ACTOR_STATE_IDLE ); }

	void			RequestInMove( Asura_Message* pxMoveStartMessage, const u_int uStateAfterMove = ACTOR_STATE_IDLE );
	void			RequestInMove( const u_int uStateAfterMove = ACTOR_STATE_IDLE ); // variation that does not send the move
	bool			IsInMove()							const	{ return IsStateInMove( m_uCurrentState ); }
	bool			IsStateInMove( const u_int uState )	const	{ return ACTOR_STATE_IN_MOVE == uState; }

	float			GetDistanceRequired( const u_int uStateFrom, const u_int uStateTo );

	float			GetCurrentTweenTime() const { return m_fCurrentTweenTime; }
	bool			IsNextAnimPlayFromStart() const { return m_bPlayNextAnimFromStart; }

	u_int			GetStateAfterMove() const { return m_uStateAfterMove; }

	virtual const AVP_Actor_AIAnimStateConnectionData* GetConnectionData( const u_int uFromState, const u_int uToState ) const = 0;

	// we only want to call this on the rare occasions when we want to override the normal operation of the state machine e.g. teleporting actors around
	void			ForceState( const u_int uNewState );
	
	virtual const char* GetStateDescription( const u_int uState ) const { return GetStateDescriptionStatic( uState ); }
	static const char* GetStateDescriptionStatic( const u_int uState );

	virtual bool IsLoopedState( const u_int uState ) const { return IsLoopedStateStatic( uState ); }
	static bool IsLoopedStateStatic( const u_int uState );

protected:
	void			RequestState( const u_int uNewState );

	u_int			GetNextState() const { return m_auStateQueue[ m_uStateQueueStart ]; }

	virtual void	BeginState( const u_int uState );
	virtual void	UpdateState( const u_int uState );
	virtual bool	EndState( const u_int uState );

	virtual bool	IsCurrentStateFinished() const;
	virtual bool	ShouldStateUpdateMoveDirAngle( const u_int uState ) const { return true; }
	
	virtual u_int GetNumStates() const = 0;
	virtual const AVP_Actor_AnimStateData*	GetStateAnimData( const u_int uState ) const = 0;

#ifndef ASURA_NO_DEBUG_OUTPUT
	virtual void	RenderDebugInfo();
#endif

	AVP_ServerEntity_Actor& m_xServerActor;

private:
	struct AStarNode
	{
		AStarNode*		m_pxParent;
		float			m_fCostToHere;
		union
		{
			u_int		m_uFlags;			// Single access point for the flags.
			struct
			{
				u_int	m_bOpen		: 1,
						m_bClosed	: 1,

									: 0;	// 32 - 2 = 30 spare-bits.
			};
		};
	};

	bool AStarSearch( u_int* puRouteArray, u_int& uRouteLength, const u_int uFromState, const u_int uToState );
	AStarNode* GetOpenAStarNode();

	static AStarNode s_axAStarNodeScratchPad[ AVP_ACTOR_AIANIMSTATEMACHINE_MAX_STATES ];
	
	u_int	m_uStateAfterMove;

	u_int	m_auStateQueue[ AVP_ACTOR_AIANIMSTATEMACHINE_MAX_STATES_IN_QUEUE ];
	u_int	m_uStateQueueStart;
	u_int	m_uStateQueueEnd;

	u_int	m_uCurrentState;

	union
	{
		u_int m_uFlags;
		struct
		{
			u_int	m_bGamesceneFinished		: 1,
					m_bInMove					: 1,
					m_bPlayNextAnimFromStart	: 1,
					m_bPlayingNextAnim			: 1,
												: 0;
		};
	};


	float	m_fTimeInState;	// The number of seconds we've been in this state.
	float	m_fCurrentTweenTime;

	float	m_fOSMoveDirAngle;

#if defined(ASURA_SUPPORT_COMMAND_CONSOLE)
	static int s_iConsoleVarsCount;
#endif	// defined(ASURA_SUPPORT_COMMAND_CONSOLE)

	AVP_Actor_AIAnimStateMachine(const AVP_Actor_AIAnimStateMachine& xOther);
	AVP_Actor_AIAnimStateMachine& operator=(const AVP_Actor_AIAnimStateMachine& xOther);
};

#endif   // ifndef __AVP_ACTOR_AIANIMSTATEMACHINE_H__

/// End of file. ///
