//////////////////////////////////////////////////////////////////////////////
//
// AVP_Behaviour_Predator_Stalking.cpp
//
// Copyright (C) 2007 Rebellion. All Rights Reserved.
//
//////////////////////////////////////////////////////////////////////////////

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

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

// header
#include "Behaviour/BehaviourTypes/Predator/AVP_Behaviour_Predator_Stalking.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/Predator/AVP_Behaviour_Predator_AtVentagePoint.h"
#include "Behaviour/BehaviourTypes/Predator/AVP_Behaviour_Predator_Sneaking.h"
#include "Core/BrainTypes/AVP_Brain_Actor_Predator.h"
#include "Core/AVP_Waypoint_Manager.h"
#include "Knowledge/AVP_Knowledge_System_Predator.h"
#include "Knowledge/Axon_Knowledge.h"

// avp
#include "Core/AVP_LOS.h"
#include "Entity/Locations/VantagePoint/AVP_ServerNode_VantagePoint_Predator.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Predator/AVP_ServerActor_Predator.h"

//////////////////////////////////////////////////////////////////////////////
//
// AVP_Behaviour_Predator_Stalking
//
//////////////////////////////////////////////////////////////////////////////

DEBUGVAR int dbg_iMaxReselectVPCount = 4;

AVP_Behaviour_Predator_Stalking::AVP_Behaviour_Predator_Stalking( AVP_Brain_Actor_Predator* pxBrain )
: PARENT( pxBrain, AVP_BEHAVIOUR_TYPE_PREDATOR_STALKING )
, m_eCurrentState( UNSET )
, m_fTimeInState( 0.0f )
, m_pxDestinationVP(NULL)
, m_pxCurrentVP(NULL)
, m_fTotalEnemyHealth(0.0f)
, m_bLastVPSearchWasFast(false)
, m_iReselectVPCount(0)
, m_bAbort(false)
{
}


bool AVP_Behaviour_Predator_Stalking::Activate()
{
#ifndef ASURA_NO_DEBUG_OUTPUT
	GetPredatorBrain()->SetRenderRoute( true );
#endif

	return PARENT::Activate();
}

void AVP_Behaviour_Predator_Stalking::Deactivate()
{
	Axon_Behaviour* pxChild = GetChild();
	while ( pxChild )
	{
		UnlinkChild( pxChild );
		GetProjectBrain()->DestroyBehaviour( pxChild );
		pxChild = GetChild();
	}

	PARENT::Deactivate();
}

DEBUGVAR float dbg_fIgnoreVPDuration = 5.0f;

void AVP_Behaviour_Predator_Stalking::Update( const float fTimeslice )
{
	PARENT::Update( fTimeslice );

	if ( IsFinished() || IsInError() )
	{
		return;
	}

	m_fTimeInState += fTimeslice;

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

	switch ( m_eCurrentState )
	{
	case UNSET:
		SwitchToState(SELECT_VENTAGE_POINT);
		break;
	case SELECT_VENTAGE_POINT:
		if (m_bAbort)
		{
			SwitchToState(FINISHED);
			return;
		}

		GetPredatorBrain()->Cloak(); // ask to cloak now while we looking for place to go... will save time later
		if (GetPredatorBrain()->IsVPTestFinished() )
		{ 
			m_pxDestinationVP = GetPredatorBrain()->GetVPTestResult(); // fast selection doesnt count for reselect			
			if (m_pxDestinationVP == NULL) 
			{
				SetInError(true);
				SwitchToState(FINISHED); 
			}
			else
			{
				SwitchToState(MOVE_TO_VENTAGE_POINT);
			}
		}

		break;
	case MOVE_TO_VENTAGE_POINT:
		if (m_bAbort)
		{
			SwitchToState(FINISHED);
			return;
		}

		{
			AVP_Behaviour_Predator_Sneaking* pxBehaviour = static_cast< AVP_Behaviour_Predator_Sneaking* >( GetChildWithType( this, AVP_BEHAVIOUR_TYPE_PREDATOR_SNEAKING ) );
			if (!pxBehaviour || pxBehaviour->IsInError())
			{
				GetPredatorBrain()->CancelVPTest();
				GetPredatorBrain()->IgnoreVentagePoint(m_pxDestinationVP->GetGuid(), dbg_fIgnoreVPDuration);
				SwitchToState(SELECT_VENTAGE_POINT);
			}
			else if (pxBehaviour->IsFinished())
			{
				SwitchToState(AT_VENTAGE_POINT);
			}
			else if (m_iReselectVPCount < dbg_iMaxReselectVPCount && GetPredatorBrain()->IsVPTestFinished())
			{ 
				const AVP_ServerNode_VantagePoint_Predator* pxNewVP = GetPredatorBrain()->GetVPTestResult();

				if (pxNewVP && pxNewVP->GetGuid() != m_pxDestinationVP->GetGuid())
				{
					m_pxDestinationVP = pxNewVP;
					++m_iReselectVPCount;
					SwitchToState(MOVE_TO_VENTAGE_POINT);
				}

				if (m_bLastVPSearchWasFast)
				{
					GetPredatorBrain()->StartVPTest(ASURA_GUID_UNREGISTERED, false);		
				}
				else
				{
					GetPredatorBrain()->StartVPTest(m_pxDestinationVP->GetGuid(), false );		
				}
				m_bLastVPSearchWasFast = false;
				const Axon_Knowledge& xKnowledge = GetPredatorBrain()->GetKnowledgeSystem()->GetBestThreatKnowledge();
				if (xKnowledge.GetGuid() != ASURA_GUID_UNREGISTERED)
				{
					pxBehaviour->AimAt(xKnowledge.m_xPosition);
				}
				else
				{
					pxBehaviour->AimWhereYouMove();
				}
			}
		}
		break;
	case AT_VENTAGE_POINT:
		{
			AVP_Behaviour_Predator_AtVentagePoint* pxBehaviour = static_cast< AVP_Behaviour_Predator_AtVentagePoint* >( GetChildWithType( this, AVP_BEHAVIOUR_TYPE_PREDATOR_AT_VENTAGE_POINT ) );
			if (m_bAbort && pxBehaviour && !pxBehaviour->IsFinished())
			{
				pxBehaviour->Abort();
				return;
			}
			
			if (pxBehaviour && pxBehaviour->IsInError())
			{
				GetPredatorBrain()->IgnoreVentagePoint(m_pxCurrentVP->GetGuid(), dbg_fIgnoreVPDuration);
				SwitchToState(SELECT_VENTAGE_POINT);
			}
			else if (!pxBehaviour || pxBehaviour->IsFinished())
			{
				float fNewEnemyHealth = CalculateThreatsTotalHealth();
				if (fNewEnemyHealth < m_fTotalEnemyHealth)
				{
					SwitchToState(AT_VENTAGE_POINT);
				}
				else if (m_pxDestinationVP->GetGuid() == m_pxCurrentVP->GetGuid())
				{
					GetPredatorBrain()->IgnoreVentagePoint(m_pxCurrentVP->GetGuid(), dbg_fIgnoreVPDuration);
					SwitchToState(SELECT_VENTAGE_POINT);
				}
				else
				{

					SwitchToState(MOVE_TO_VENTAGE_POINT);
				}
			}
			else if (GetPredatorBrain()->IsVPTestFinished() )
			{ 
				const AVP_ServerNode_VantagePoint_Predator* pxNewVP = GetPredatorBrain()->GetVPTestResult();
				if (pxNewVP && pxNewVP->GetGuid() != m_pxDestinationVP->GetGuid())
				{
					m_pxDestinationVP = pxNewVP;
				}
				GetPredatorBrain()->StartVPTest(ASURA_GUID_UNREGISTERED, false);
				m_bLastVPSearchWasFast = false;
			}
		}
		break;
	case FINISHED:
		break;
	case NUM_STATES:
		break;
	default:
		Asura_Assert(false, "unexpected state");
	}
}

#ifndef ASURA_NO_DEBUG_OUTPUT
static const char* s_aszStateStrings[] =
{
	"UNSET",
	"SELECT_VENTAGE_POINT",
	"MOVE_TO_VENTAGE_POINT",
	"AT_VENTAGE_POINT",
	"FINISHED",
	"NUM_STATES"
};

void AVP_Behaviour_Predator_Stalking::RenderDebugInfo()
{
	PARENT::RenderDebugInfo();

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

AVP_Brain_Actor_Predator* AVP_Behaviour_Predator_Stalking::GetPredatorBrain() const
{
	return static_cast< AVP_Brain_Actor_Predator* >( GetProjectBrain() );
}

void AVP_Behaviour_Predator_Stalking::SwitchToState( const State eNextState )
{

	switch ( m_eCurrentState )
	{
	case UNSET:
	case SELECT_VENTAGE_POINT:
		break;
	case MOVE_TO_VENTAGE_POINT:
		DestroyChildWithType( AVP_BEHAVIOUR_TYPE_PREDATOR_SNEAKING );
		break;
	case AT_VENTAGE_POINT:
		DestroyChildWithType( AVP_BEHAVIOUR_TYPE_PREDATOR_AT_VENTAGE_POINT );
		break;
	case FINISHED:
	default:
		Asura_Assert(false, "unexpected state");
	}

	m_eCurrentState = eNextState;
	m_fTimeInState  = 0.0f;
	switch ( m_eCurrentState )
	{
	case SELECT_VENTAGE_POINT:
		GetPredatorBrain()->StartVPTest(ASURA_GUID_UNREGISTERED, true);
		m_bLastVPSearchWasFast = true;
		break;
	case MOVE_TO_VENTAGE_POINT:
		{			
			Asura_Assert(m_pxDestinationVP, "Ventage Point Not found");
			if (m_pxDestinationVP)
			{
				if (m_pxCurrentVP && m_pxCurrentVP->GetGuid() == m_pxDestinationVP->GetGuid())
				{
					SwitchToState(AT_VENTAGE_POINT);
				}
				else
				{
					if (m_bLastVPSearchWasFast)
					{
						GetPredatorBrain()->StartVPTest(ASURA_GUID_UNREGISTERED, false);
					}
					else
					{
						GetPredatorBrain()->StartVPTest(m_pxDestinationVP->GetGuid(), false);
					}
					m_bLastVPSearchWasFast = false;
					Asura_Vector_3 xDestination = m_pxDestinationVP->GetPosition();
					AVP_Behaviour_Predator_Sneaking* pxNewBehaviour = static_cast< AVP_Behaviour_Predator_Sneaking* >( CreateChildWithType( AVP_BEHAVIOUR_TYPE_PREDATOR_SNEAKING ) );
					if ( !pxNewBehaviour || pxNewBehaviour->IsInError() )
					{
						GetPredatorBrain()->CancelVPTest();
						GetPredatorBrain()->IgnoreVentagePoint(m_pxDestinationVP->GetGuid(), dbg_fIgnoreVPDuration);
						SwitchToState(SELECT_VENTAGE_POINT);
					}
					else 
					{
						pxNewBehaviour->SetDestination(xDestination, true);
					}
				}
			}
			else
			{
				SetInError(true);
				SwitchToState(FINISHED);
			}
		}
		break;
	case AT_VENTAGE_POINT:
		{
			m_iReselectVPCount = 0;
			m_pxCurrentVP = m_pxDestinationVP;
			AVP_Behaviour_Predator_AtVentagePoint* pxNewBehaviour = static_cast< AVP_Behaviour_Predator_AtVentagePoint* >( CreateChildWithType( AVP_BEHAVIOUR_TYPE_PREDATOR_AT_VENTAGE_POINT ) );
			if ( !pxNewBehaviour || pxNewBehaviour->IsInError() )
			{
				SetInError(true);
				SwitchToState( FINISHED );
			}
			else
			{
				m_fTotalEnemyHealth  = CalculateThreatsTotalHealth();
			}
		}

		break;
	case FINISHED:
		GetPredatorBrain()->CancelVPTest();
		SetFinished(true);
		break;
	case UNSET:
	default:
		Asura_Assert(false, "unexpected state");
		break;
	}
}


float AVP_Behaviour_Predator_Stalking::CalculateThreatsTotalHealth() const
{
	float fTotalHealth = 0.0f;
	const AVP_Knowledge_System_Predator* pxKnowlegeSystem = GetPredatorBrain()->GetKnowledgeSystem();

	Asura_Guid uThreat = pxKnowlegeSystem->GetBestThreatKnowledge().m_uSourceGuid;

	if (uThreat != ASURA_GUID_UNREGISTERED && !pxKnowlegeSystem->IsBestThreatAShootAtNode())
	{
		const AVP_ServerEntity_Actor* pxThreat = AVP_ServerEntity_Actor::FindEntity(uThreat);
		if (pxThreat)
		{
			fTotalHealth += pxThreat->GetHealth();
		}
	}

	for (u_int u =0 ; u < pxKnowlegeSystem->GetNumSecondaryThreats(); ++u)
	{
		Asura_Guid uThreat = pxKnowlegeSystem->GetSecondaryThreatKnowledge(u).m_uSourceGuid;
		if (uThreat != ASURA_GUID_UNREGISTERED)
		{
			const AVP_ServerEntity_Actor* pxThreat = AVP_ServerEntity_Actor::FindEntity(uThreat);
			if (pxThreat)
			{
				fTotalHealth += pxThreat->GetHealth();
			}
		}
	}

	return fTotalHealth;
}

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