//////////////////////////////////////////////////////////////////////////////
//
// AVP_Behaviour_FaceHugger_Wander.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/FaceHugger/AVP_Behaviour_FaceHugger_Wander.h"

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

// avp
#include "Actor/Vertebrates/FaceHugger/AVP_FaceHugger_AIAnimStateMachine.h"
#include "Entity/PhysicalObject/Actor/AVP_ServerEntity_Actor.h" 
#include "Entity/Controller/AVP_ServerEntity_Squad_FaceHugger.h"

// axon
#include "Behaviour/BehaviourTypes/FaceHugger/AVP_Behaviour_FaceHugger_Movement.h"
#include "Behaviour/BehaviourTypes/AVP_Behaviour_Types.h"
#include "Core/AVP_Navigation.h"
#include "Core/BrainTypes/AVP_Brain_Actor_FaceHugger.h"

//////////////////////////////////////////////////////////////////////////////
//
// AVP_Behaviour_FaceHugger_Wander
//
//////////////////////////////////////////////////////////////////////////////

AVP_Behaviour_FaceHugger_Wander::AVP_Behaviour_FaceHugger_Wander( AVP_Brain_Actor_FaceHugger* pxBrain )
: PARENT( pxBrain, AVP_BEHAVIOUR_TYPE_FACEHUGGER_WANDER )
, m_eCurrentState( GETTING_TO_POSITION )
, m_fTimeInState( 0.0f )
, m_usCenterWaypoint(ASURA_INVALID_WAYPOINT_ID)
, m_bIsCenterValid(false)
, m_bAvoidLight(false)
{
	GetFaceHuggerBrain()->SetMovementMode(AVP_Navigation::MM_SLOW);
}

void AVP_Behaviour_FaceHugger_Wander::SetCenterPoint(const Asura_Vector_3& xCenter)
{
	Asura_Assert(m_usCenterWaypoint == ASURA_INVALID_WAYPOINT_ID, "Center was already set");
	AVP_Navigation::NavData& xNavData = GetFaceHuggerBrain()->GetNavData();
	AVP_Navigation::TestParams xTestParams;
	m_usCenterWaypoint = ASURA_INVALID_WAYPOINT_ID;
	xTestParams.m_uPathfinderFlags = xNavData.m_uPathfinderFlags;
	xTestParams.m_usZone           = xNavData.m_usCurrentZone;
	m_bIsCenterValid = AVP_Navigation::GetNearestWaypointID(xCenter, m_usCenterWaypoint, xTestParams );
}

void AVP_Behaviour_FaceHugger_Wander::SetAvoidLight()
{
	m_bAvoidLight = true;
}

void AVP_Behaviour_FaceHugger_Wander::Deactivate()
{
	// Destroy any children.
	Axon_Behaviour* pxChildBehaviour = GetChild();
	while ( pxChildBehaviour )
	{
		UnlinkChild( pxChildBehaviour );
		GetProjectBrain()->DestroyBehaviour( pxChildBehaviour );
		pxChildBehaviour = GetChild();
	}

	PARENT::Deactivate();
}

void AVP_Behaviour_FaceHugger_Wander::Update( const float fTimeslice )
{
	PARENT::PerFrameUpdate( fTimeslice );

	m_fTimeInState += fTimeslice;

	switch ( m_eCurrentState )
	{
	case WAIT:
		if (m_fTimeInState > 1.0f)
		{
			if (m_fTimeInState > 5.0f || Asura_Random::GetInt(0, 10) < 7 )
			{
				SwitchToState(GETTING_TO_POSITION);
			}
		}
		break;
	case GETTING_TO_POSITION:
		{
			AVP_Behaviour_FaceHugger_Movement* pxBehaviour = static_cast< AVP_Behaviour_FaceHugger_Movement* >( GetChildWithType( this, AVP_BEHAVIOUR_TYPE_FACEHUGGER_MOVEMENT ) );
			if ( !pxBehaviour || pxBehaviour->IsInError() || pxBehaviour->IsFinished() )
			{
				if (Asura_Random::GetInt(0, 10) < 3 )
				{
					SwitchToState( WAIT );
				}
				else
				{
					SwitchToState( GETTING_TO_POSITION );
				}
			}
			break;
		}
	}
}

#ifndef ASURA_NO_DEBUG_OUTPUT
static const char* s_aszStateStrings[] =
{
	"GETTING TO POSITION",
	"WAIT"
};

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

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

void AVP_Behaviour_FaceHugger_Wander::SwitchToState( const State eNextState )
{
	// Do any work we need to exit our current state.
	switch ( m_eCurrentState )
	{
	case WAIT:
		break;
	case GETTING_TO_POSITION:
		{
			DestroyChildWithType( AVP_BEHAVIOUR_TYPE_FACEHUGGER_MOVEMENT );
			break;
		}
	}

	// Do any work we need to change into our next state.
	m_eCurrentState = eNextState;
	m_fTimeInState  = 0.0f;
	switch ( m_eCurrentState )
	{
	case WAIT:
		break;
	case GETTING_TO_POSITION:
		{

			AVP_Navigation::NavData& xNavData = GetFaceHuggerBrain()->GetNavData();
			int iTries = 0;
			u_short usCurrentWaypoint = m_usCenterWaypoint;
			Asura_Vector_3 xCenter = Asura_AI_Navigation_System::GetWaypoint(m_usCenterWaypoint)->GetPosition();
			Asura_Vector_3 xDestination = Asura_Maths::ZeroVector3;
			float fRadius = Asura_AI_Navigation_System::GetWaypoint(m_usCenterWaypoint)->GetRadius();
			// Pick a valid random waypoint.
			do
			{
				u_short usNextWaypoint = FindNextRandomWaypoint(usCurrentWaypoint);
				float fDistanceFromCenterSquared = (Asura_AI_Navigation_System::GetWaypoint(usNextWaypoint)->GetPosition() -  xCenter).MagnitudeSquared();
				float fWanderDistanceSquared = GetFaceHuggerBrain()->GetBehaviourData().m_fWanderDistance;
				fWanderDistanceSquared *= fWanderDistanceSquared;
				if (iTries++ > 3 ||
					(fDistanceFromCenterSquared > fWanderDistanceSquared ) )
				{
					u_short usNearestWaypoint = ASURA_INVALID_WAYPOINT_ID;
					AVP_Navigation::TestParams xTestParams;
					xTestParams.m_uPathfinderFlags = xNavData.m_uPathfinderFlags;
					xTestParams.m_usZone           = xNavData.m_usCurrentZone;

					if ( AVP_Navigation::GetNearestWaypointID( Asura_AI_Navigation_System::GetWaypoint( usCurrentWaypoint )->GetPosition(), usNearestWaypoint, xTestParams ) )
					{
						xDestination = Asura_AI_Navigation_System::GetWaypoint( usNearestWaypoint )->GetPosition();
						fRadius = Asura_AI_Navigation_System::GetWaypoint( usNearestWaypoint )->GetRadius();
					}
				}
				usCurrentWaypoint = usNextWaypoint;
			} while ( xDestination == Asura_Maths::ZeroVector3 && iTries < 5);

			if (xDestination == Asura_Maths::ZeroVector3)
			{
				xDestination = xCenter;
			}

			Asura_Vector_3 xRandomVector;
			xRandomVector.RandomDirectionSeeded();
			xDestination += xRandomVector * Asura_Random::GetFloat( fRadius / 2.0f, fRadius );

			AVP_Navigation::TestParams xTestParams;
			xTestParams.m_uPathfinderFlags = xNavData.m_uPathfinderFlags;
			xTestParams.m_usZone           = xNavData.m_usCurrentZone;
			AVP_Navigation::ResolveToValidPosition( xDestination, xTestParams );

			AVP_Behaviour_FaceHugger_Movement * pxBehaviour = static_cast< AVP_Behaviour_FaceHugger_Movement* >( CreateChildWithType( AVP_BEHAVIOUR_TYPE_FACEHUGGER_MOVEMENT ) );
			if ( pxBehaviour && pxBehaviour->GetToPosition( xDestination ) )
			{
				if (m_bAvoidLight)
				{
					pxBehaviour->SetAvoidLight();
				}
				pxBehaviour->SetMovementMode(AVP_Behaviour_FaceHugger_Movement::EMM_SLOW);
			}
			else
			{
				m_bIsInError = true;
			}

			// if the wander want to move us close to where we are just ignore it and move to wait 
			float fDistanceSquared = (GetFaceHuggerBrain()->GetActor()->GetPosition() - xDestination).MagnitudeSquared();
			if ( fDistanceSquared < 1.0f * 1.0f )
			{
				SwitchToState(WAIT);
			}
			break;
		}
	}
}

AVP_Brain_Actor_FaceHugger* AVP_Behaviour_FaceHugger_Wander::GetFaceHuggerBrain() const
{
	return static_cast< AVP_Brain_Actor_FaceHugger* >( GetProjectBrain() );
}


u_short AVP_Behaviour_FaceHugger_Wander::FindNextRandomWaypoint(u_short usCurrentWaypoint) const
{
	AVP_Navigation::NavData& xNavData = GetFaceHuggerBrain()->GetNavData();

	Asura_AI_NavVertex *pxCurrVertex = Asura_AI_Navigation_System::GetWaypoint(usCurrentWaypoint);
    u_int uLinks = pxCurrVertex ->GetNumLinks();
	u_int uValidLinks = 0;
	AVP_Navigation::TestParams xTestParams;
	xTestParams.m_uPathfinderFlags = xNavData.m_uPathfinderFlags;
	xTestParams.m_usZone = xNavData.m_usCurrentZone;

	// count the number of valid links
	for (u_int i = 0; i < uLinks; ++i)
	{
		if(AVP_Navigation::IsValidWaypointID(static_cast<u_short>(pxCurrVertex->GetLink(i).GetIndex()), xTestParams ) )
		{
			++uValidLinks;
		}
	}
	
	// if no vali points found - return the current waypoint
	if (uValidLinks == 0)
	{
		return usCurrentWaypoint;
	}

	u_int uRandomLink = Asura_Random::GetIntU( 0, uValidLinks - 1 );
	// find the uRandomLink'th valid point
	for (u_int i = 0; i < uLinks; ++i)
	{
		u_short nextWaypoint = static_cast<u_short>(pxCurrVertex->GetLink(i).GetIndex());
		if(AVP_Navigation::IsValidWaypointID(nextWaypoint, xTestParams ) && (uRandomLink-- == 0))
		{
			return nextWaypoint;
		}
		
	}

	Asura_Assert(false, "No Valid Waypoint found although some must exist");
	return usCurrentWaypoint;
}

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