//////////////////////////////////////////////////////////////////////////////
//
// AVP_Behaviour_Alien_SwarmIn.cpp
//
// Copyright (C) 2008 Rebellion. All Rights Reserved.
//
//////////////////////////////////////////////////////////////////////////////

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

// core
#include "Core/Asura.h"

// header
#include "Behaviour/BehaviourTypes/Alien/AVP_Behaviour_Alien_SwarmIn.h"

// asura
#include "Network/Asura_Message.h"
#ifndef ASURA_NO_DEBUG_OUTPUT
#include "Debugging/Asura_Logfile.h"
#include "Rendering/Asura_Font_System.h"
#endif

// axon
#include "Behaviour/BehaviourTypes/AVP_Behaviour_Types.h"
#include "Core/AVP_Navigation.h"
#include "Core/BrainTypes/AVP_Brain_Actor_Alien.h"

// avp
#include "Entity/AVP_Entity_Classification.h"
#include "Entity/Controller/AVP_ServerEntity_Squad_Alien.h"
#include "Entity/PhysicalObject/Actor/AVP_ClientEntity_Actor.h"
#include "Entity/PhysicalObject/Actor/AVP_ServerEntity_Actor.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Alien/AVP_Messages_Alien.h"
#include "Moves/Vertebrates/Alien/AVP_Alien_MovesJump.h"

//////////////////////////////////////////////////////////////////////////////
//
// AVP_Behaviour_Alien_SwarmIn
//
//////////////////////////////////////////////////////////////////////////////

AVP_Behaviour_Alien_SwarmIn::AVP_Behaviour_Alien_SwarmIn( Axon_Project_Brain* pxBrain )
: PARENT( pxBrain, AVP_BEHAVIOUR_TYPE_ALIEN_SWARMIN )
, m_bIsLeader( false )
, m_uLeaderGuid( ASURA_GUID_UNREGISTERED )
, m_fPrevSwarmRouteAge( 0.0f )
{
}

void AVP_Behaviour_Alien_SwarmIn::Deactivate()
{
	SwitchToState( UNSET );
	PARENT::Deactivate();
}

void AVP_Behaviour_Alien_SwarmIn::PerFrameUpdate( const float fTimeslice )
{
	PARENT::PerFrameUpdate( fTimeslice );

	if ( fTimeslice == 0.0f || IsInError() || IsFinished()) { return; }
	UpdateStateTimer( fTimeslice );

	if (!GetAlienBrain()->CanPlanMove())
	{
		return;
	}

	switch ( GetCurrentState() )
	{
		case SWARMING_IN:
		{
			if ( m_bIsLeader )
			{
				if ( GetAlienBrain()->ShouldRefreshPath((GetAlienBrain()->GetCurrentTargetPosition() - GetAlienBrain()->GetNavData().m_xDestinationPosition).MagnitudeSquared() ) )
				{

					if ( GetAlienBrain()->GetMovement().GetToPosition( GetAlienBrain()->GetCurrentTargetPosition(), AVP_Alien_Movement::GetMaxArrivalRange(), AVP_Navigation::s_xAlienAStarFunctor, AVP_Alien_Movement::EMM_MEDIUM ) )
					{
						GetAlienBrain()->GetAlienSquad()->ResetSwarmRouteAge( m_uLeaderGuid );
					}
					else
					{
						SetInError();
						SwitchToState( UNSET );
					}
				}
			}
			else if ( m_uLeaderGuid == ASURA_GUID_UNREGISTERED )
			{
				if ( GetAlienBrain()->ShouldRefreshPath((GetAlienBrain()->GetCurrentTargetPosition() - GetAlienBrain()->GetNavData().m_xDestinationPosition).MagnitudeSquared() ) )
				{
					if ( !GetAlienBrain()->GetMovement().GetToPosition( GetAlienBrain()->GetCurrentTargetPosition(), AVP_Alien_Movement::GetMaxArrivalRange(), AVP_Navigation::s_xAlienAStarFunctor, AVP_Alien_Movement::EMM_MEDIUM ) )
					{
						SetInError();
						SwitchToState( UNSET );
					}
				}
			}
			else
			{
				// Make sure the leader is still around.
				const Asura_Guid uLeaderGuid = GetAlienBrain()->GetAlienSquad()->GetSwarmLeaderGuid( GetAlienBrain()->GetCurrentTargetGuid() );
				if ( uLeaderGuid != m_uLeaderGuid )
				{
					// The leader has changed/gone. If we switch to this state again we'll establish leadership and sort our route out again.
					SwitchToState( SWARMING_IN );
				}
				else
				{
					// If the leader updates their route we'll need to update ours also.
					const float fSwarmRouteAge = GetAlienBrain()->GetAlienSquad()->GetSwarmRouteAge( m_uLeaderGuid );
					if ( fSwarmRouteAge < m_fPrevSwarmRouteAge )
					{
						GetAlienBrain()->GetMovement().FollowSharedRoute( m_uLeaderGuid, AVP_Navigation::s_xAlienAStarFunctor, AVP_Alien_Movement::EMM_MEDIUM );
					}
					m_fPrevSwarmRouteAge = fSwarmRouteAge;
				}
			}
			break;
		}

		default:
		case UNSET:
		case NUM_STATES:
		{
			SwitchToState( SWARMING_IN );
			break;
		}
	}
}

#ifndef ASURA_NO_DEBUG_OUTPUT
void AVP_Behaviour_Alien_SwarmIn::RenderDebugInfo()
{
	static const char* s_aszStateStrings[ NUM_STATES ] =
	{
		"UNSET",
		"SWARMING_IN"
	};

	PARENT::RenderDebugInfo();

	Asura_Font_System::Print( GetProjectBrain()->GetDebugOutputPos().x, GetProjectBrain()->GetDebugOutputPos().y, 0.5f, 0xffffffff,
		ASURA_TEXTFORMAT_CENTREJUSTIFIED | ASURA_TEXTFORMAT_FORMATSTRING, "Swarm In: %s", s_aszStateStrings[ GetCurrentState() ] );
	GetProjectBrain()->GetDebugOutputPos().y += 10.0f;
}
#endif

void AVP_Behaviour_Alien_SwarmIn::EstablishLeadership()
{
	// Set ourselves up as a leader if we can, otherwise find our leader.
	m_uLeaderGuid = GetAlienBrain()->GetAlienSquad()->GetSwarmLeaderGuid( GetAlienBrain()->GetCurrentTargetGuid() );
	if ( m_uLeaderGuid == ASURA_GUID_UNREGISTERED )
	{
		// We should become the leader.
		GetAlienBrain()->GetAlienSquad()->SetSwarmLeaderGuid( GetBrain()->GetOwnerGuid(), GetAlienBrain()->GetCurrentTargetGuid() );
		m_bIsLeader   = true;
		m_uLeaderGuid = GetBrain()->GetOwnerGuid();
	}
	else
	{
		m_bIsLeader = false;
	}
}

void AVP_Behaviour_Alien_SwarmIn::SwitchToState( const int iNewState )
{
	switch ( GetCurrentState() )
	{
		case SWARMING_IN:
		{
			if ( m_bIsLeader && GetBrain()->IsActive() )
			{
				GetAlienBrain()->GetAlienSquad()->ResetSwarmLeaderGuid( m_uLeaderGuid );
                m_bIsLeader   = false;
				m_uLeaderGuid = ASURA_GUID_UNREGISTERED;
			}
			break;
		}

		default:
		case UNSET:
		case NUM_STATES:
		{
			break;
		}
	}

	SetCurrentState( iNewState );
	ResetStateTimer();
	switch ( GetCurrentState() )
	{
		case SWARMING_IN:
		{
			// Set ourselves up as a leader if we can, otherwise find our leader.
			EstablishLeadership();
			if ( m_bIsLeader )
			{
				// Just a simple pathfind towards our target.
				if ( GetAlienBrain()->GetMovement().GetToPosition( GetAlienBrain()->GetCurrentTargetPosition(), AVP_Alien_Movement::GetMaxArrivalRange(), AVP_Navigation::s_xAlienAStarFunctor, AVP_Alien_Movement::EMM_MEDIUM ) )
				{
					GetAlienBrain()->GetAlienSquad()->ResetSwarmRouteAge( m_uLeaderGuid );
				}
				else
				{
					// If we can't get to our target then we should ignore it for a while to see if we can target someone else.
					GetAlienBrain()->IgnoreTarget( GetAlienBrain()->GetCurrentTargetGuid(), 5.0f );
					SetInError();
					SetFinished();
				}
			}
			else
			{
				// Follow the leader's route if we're not a loner.
				if ( m_uLeaderGuid != ASURA_GUID_UNREGISTERED && GetAlienBrain()->GetMovement().FollowSharedRoute( m_uLeaderGuid, AVP_Navigation::s_xAlienAStarFunctor, AVP_Alien_Movement::EMM_MEDIUM ) )
				{
					m_fPrevSwarmRouteAge = GetAlienBrain()->GetAlienSquad()->GetSwarmRouteAge( m_uLeaderGuid );
				} 
				else
				{
					// Ok try and make it in on our own.
					m_uLeaderGuid = ASURA_GUID_UNREGISTERED;

					if ( !GetAlienBrain()->GetMovement().GetToPosition( GetAlienBrain()->GetCurrentTargetPosition(), AVP_Alien_Movement::GetMaxArrivalRange(), AVP_Navigation::s_xAlienAStarFunctor, AVP_Alien_Movement::EMM_MEDIUM ) )
					{
						// If we can't get to our target then we should ignore it for a while to see if we can target someone else.
						GetAlienBrain()->IgnoreTarget( GetAlienBrain()->GetCurrentTargetGuid(), 5.0f );
						SetInError();
						SetFinished();
					}
				}
			}

			break;
		}

		default:
		case UNSET:
		case NUM_STATES:
		{
			break;
		}
	}
}

AVP_Brain_Actor_Alien* AVP_Behaviour_Alien_SwarmIn::GetAlienBrain() const
{
	return static_cast< AVP_Brain_Actor_Alien* >( GetBrain() );
}

//////////////////////////////////////////////////////////////////////////////
//
// 
//
//////////////////////////////////////////////////////////////////////////////
