//////////////////////////////////////////////////////////////////////////////
//
// AVP_Behaviour_Alien_MoveBetweenCover.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_MoveBetweenCover.h"

// asura
#ifndef ASURA_NO_DEBUG_OUTPUT
#include "Rendering/Asura_Font_System.h"
#endif

// axon
#include "Behaviour/BehaviourTypes/AVP_Behaviour_Types.h"
#include "Behaviour/BehaviourTypes/Alien/AVP_Behaviour_Alien_Movement.h"
#include "Core/AVP_Waypoint_Manager.h"
#include "Core/BrainTypes/AVP_Brain_Actor_Alien.h"

// avp
#include "Entity/PhysicalObject/Actor/AVP_ServerEntity_Actor.h"
#include "Knowledge/AVP_Knowledge_System_AlienTeam.h"

//////////////////////////////////////////////////////////////////////////////
//
// AVP_Behaviour_Alien_MoveBetweenCover
//
//////////////////////////////////////////////////////////////////////////////

static const float fCOVER_REFRESH_PERIOD = 2.0f;
static const float fSTATIONARY_PERIOD_MAX = 5.0f;

AVP_Behaviour_Alien_MoveBetweenCover::AVP_Behaviour_Alien_MoveBetweenCover( Axon_Project_Brain* pxBrain )
: PARENT( pxBrain, AVP_BEHAVIOUR_TYPE_ALIEN_MOVEBETWEENCOVER )
, m_fCoverRefreshTimer( 0.0f )
, m_xWanderBasePosition( Asura_Maths::ZeroVector3 )
{
	SetCurrentState( UNSET );
}

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

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

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

	m_fCoverRefreshTimer -= fTimeslice;

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

	switch ( GetCurrentState() )
	{
		case UNSET:
		{
			const AVP_AlienTeam_Knowledge* pxBestTarget = GetAlienBrain()->ChooseNewTarget();
			if ( pxBestTarget )
			{
				GetAlienBrain()->StartedTargeting( pxBestTarget->GetGuid() );
				SwitchToState( MOVING_TO_NEAREST_COVER );
			}

			break;
		}

		case MOVING_TO_NEAREST_COVER:
		case MOVING_TO_COVER:
		{
			if ( !GetAlienBrain()->CurrentTargetValid() )
			{
				SwitchToState( UNSET );
			}
			else if ( GetAlienBrain()->GetMovement().IsInError() )
			{
				SwitchToState( WAITING );
			}
			else if ( GetAlienBrain()->GetMovement().IsFinished() )
			{
				SwitchToState( IN_COVER );
			}
			else if ( m_fCoverRefreshTimer <= 0.0f &&
				!AVP_Waypoint_Manager::HasCoverFrom( GetAlienBrain()->GetNavData().m_usDestinationWPID, GetAlienBrain()->GetCurrentTargetWPID() ) )
			{
				m_fCoverRefreshTimer = fCOVER_REFRESH_PERIOD;
				SwitchToState( MOVING_TO_NEAREST_COVER );
			}
			break;
		}

		case IN_COVER:
		{
			if ( !GetAlienBrain()->CurrentTargetValid() )
			{
				SwitchToState( UNSET );
			}
			// If our cover's blown or we've been here too long then move.
			else if ( AVP_Waypoint_Manager::HasCoverFrom( GetAlienBrain()->GetNavData().m_usCurrentWPID, GetAlienBrain()->GetCurrentTargetWPID() ) )
			{
				if ( GetStateTimer() > fSTATIONARY_PERIOD_MAX )
				{
					SwitchToState( MOVING_TO_COVER );
				}
			}
			else
			{
				SwitchToState( MOVING_TO_NEAREST_COVER );
			}

			break;
		}

		case MOVING_AWAY_FROM_THREAT:
		{
			break;
		}

		case WAITING:
		{
			if ( !GetAlienBrain()->CurrentTargetValid() )
			{
				SwitchToState( UNSET );
			}
			else if ( GetStateTimer() > 2.5f )
			{
				SwitchToState( MOVING_TO_NEAREST_COVER );
			}
			break;
		}

		default:
		{
			break;
		}
	}
}

#ifndef ASURA_NO_DEBUG_OUTPUT
void AVP_Behaviour_Alien_MoveBetweenCover::RenderDebugInfo()
{
	static const char* s_aszStateStrings[ NUM_STATES ] =
	{
		"UNSET",
		"MOVING_TO_NEAREST_COVER",
		"MOVING_TO_COVER",
		"IN_COVER",
		"MOVING_AWAY_FROM_THREAT",
		"WANDERING_LOCALLY",
		"WAITING"
	};

	PARENT::RenderDebugInfo();

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

void AVP_Behaviour_Alien_MoveBetweenCover::SwitchToState( const int iNewState )
{
	switch ( GetCurrentState() )
	{
		case MOVING_TO_NEAREST_COVER:
		case MOVING_TO_COVER:
			break;
		case UNSET:
		case IN_COVER:
		case WAITING:
		default:
			break;
	}

	SetCurrentState( iNewState );
	ResetStateTimer();
	switch ( GetCurrentState() )
	{
		case MOVING_TO_NEAREST_COVER:
		{
			const u_short usNearestCoverWPID = FindNearestCoverWPID();
			if ( usNearestCoverWPID == ASURA_INVALID_WAYPOINT_ID )
			{
				//Asura_OutputToDebugger( ( "AVP_Behaviour_Alien_MoveBetweenCover - could not find nearest cover\n" ) );
				SwitchToState( WAITING );
			}
			else
			{
				const Asura_AI_NavVertex* pxWP = Asura_AI_Navigation_System::GetWaypoint( usNearestCoverWPID );
				if ( !GetAlienBrain()->GetMovement().GetToPosition( pxWP->GetPosition(), 0.0f, AVP_Navigation::s_xAlienAStarFunctor, AVP_Alien_Movement::EMM_MEDIUM ) )
				{
					SwitchToState( WAITING );
				}
			}

			break;
		}

		case MOVING_TO_COVER:
		{
			// Find cover in a random direction. If this isn't possible find nearest cover.
			u_short usCoverWPID = FindRandomCoverWPID();
			if ( usCoverWPID == ASURA_INVALID_WAYPOINT_ID )
			{
				//Asura_OutputToDebugger( ( "AVP_Behaviour_Alien_MoveBetweenCover - could not find cover in direction\n" ) );
				usCoverWPID = FindNearestCoverWPID();
			}

			if ( usCoverWPID == ASURA_INVALID_WAYPOINT_ID )
			{
				//Asura_OutputToDebugger( ( "                                     - could not find nearest cover either\n" ) );
				SwitchToState( WAITING );
			}
			else
			{
				const Asura_AI_NavVertex* pxWP = Asura_AI_Navigation_System::GetWaypoint( usCoverWPID );
				if ( !GetAlienBrain()->GetMovement().GetToPosition( pxWP->GetPosition(), 0.0f, AVP_Navigation::s_xAlienAStarFunctor, AVP_Alien_Movement::EMM_MEDIUM ) )
				{
					SwitchToState( WAITING );
				}
			}

			break;
		}

		case UNSET:
		{
			GetAlienBrain()->StoppedTargetingCurrentTarget();
			break;
		}

		case IN_COVER:
		case WAITING:
			GetAlienBrain()->GetMovement().Stop();
			break;
		default:
			break;
	}
}

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

u_short AVP_Behaviour_Alien_MoveBetweenCover::FindNearestCoverWPID() const
{
	AVP_Navigation::AlienFindCoverInRangeFunctor xFCIRF( GetAlienBrain()->GetActor()->GetCurrentWPID(), GetAlienBrain()->GetCurrentTargetWPID(), 0.0f, 40.0f );
	AVP_Navigation::SearchWaypoints( GetAlienBrain()->GetNavData().m_uPathfinderFlags, &xFCIRF );
	return static_cast< u_short >( xFCIRF.GetCoverWPID() );
}

u_short AVP_Behaviour_Alien_MoveBetweenCover::FindRandomCoverWPID() const
{
	Asura_Vector_3 xRandomDir;
	xRandomDir.RandomDirection();

	AVP_Navigation::AlienFindCoverInDirFunctor xFCID(
		static_cast< u_short >( GetAlienBrain()->GetActor()->GetCurrentWPID() ),
		static_cast< u_short >( GetAlienBrain()->GetCurrentTargetWPID() ),
		40.0f,
		xRandomDir,
		10.0f );
	AVP_Navigation::SearchWaypoints( GetAlienBrain()->GetNavData().m_uPathfinderFlags, &xFCID );
	return xFCID.GetNearestCoverWPID();
}

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