//---------------------------------------------------------------------------
// File			: AVP_Behaviour_Civilian_Wander.cpp
// Created		: 04 / 03 / 2008
// Created By	: Gareth Thomas
// Copyright (C) 2007 Rebellion. All Rights Reserved.
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// Includes
//---------------------------------------------------------------------------
// Core
#include "Core/Asura.h"

// This Files Header
#include "Behaviour/BehaviourTypes/Human/Civilian/AVP_Behaviour_Civilian_Wander.h"

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

// AVP
#include "Entity/GamePlayVolume/AVP_ServerEntity_GamePlayVolume.h"
#include "Entity/Locations/GuardZone/AVP_ServerNode_GuardZone.h"
#include "Entity/Locations/OrientedNode/GamesceneNode/AVP_ServerNode_GS2Node.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Human/Civilian/AVP_ServerActor_Civilian.h"

// AVP Axon
#include "Behaviour/BehaviourTypes/AVP_Behaviour_Types.h"
#include "Behaviour/BehaviourTypes/Human/AVP_Behaviour_Human_Movement.h"
#include "Core/BrainTypes/AVP_Brain_Actor_Civilian.h"
#include "Knowledge/AVP_Knowledge_System_Actor.h"

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
AVP_Behaviour_Civilian_Wander::AVP_Behaviour_Civilian_Wander(AVP_Brain_Actor_Civilian* pxBrain)
: PARENT(pxBrain, AVP_BEHAVIOUR_TYPE_CIVILIAN_WANDER)
, m_eUpdateState( FIND_ACTION )
, m_pxCivilianBrain( pxBrain )
, m_uLastActionGuid( ASURA_GUID_UNREGISTERED )
, m_fFindActionTimer( 0.0f )
{
	
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
bool AVP_Behaviour_Civilian_Wander::Activate(void)
{
	const bool bParentOk = PARENT::Activate();

	return bParentOk;
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void AVP_Behaviour_Civilian_Wander::Deactivate(void)
{
	if ( m_uCurrentNode != ASURA_GUID_UNREGISTERED )
	{
		AVP_ServerNode_GS2Node* pxNode = AVP_ServerNode_GS2Node::FindEntity( m_uCurrentNode );

		if ( pxNode )
		{
			pxNode->SetIsOccupied( false );
		}
	}

	DestroyAllChildren();
	PARENT::Deactivate();
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void AVP_Behaviour_Civilian_Wander::PerFrameUpdate(const float fTimeslice)
{
	bool bHaveThreat	= (GetCivilianBrain()->GetKnowledgeSystem()->GetBestThreatKnowledge().GetGuid() != ASURA_GUID_UNREGISTERED) ? true : false;
	bool bHaveAlert		= (GetCivilianBrain()->GetKnowledgeSystem()->GetBestAlertKnowledge().GetGuid() != ASURA_GUID_UNREGISTERED) ? true : false;

	if ((bHaveAlert) || (bHaveThreat))
	{
		SetFinished();
		return;
	}

	switch (GetUpdateState())
	{
		//--------------------------------------
		case FIND_ACTION:
		{
			if ( m_fFindActionTimer <= 0.0f )
			{
				if ( FindNextGS2Action() )
				{
					SwitchToState( MOVE_TO_ACTION );
				}
				else
				{
					m_fFindActionTimer = Asura_Random::GetFloat( 2.0f, 5.0f );
				}
			}

			m_fFindActionTimer -= fTimeslice;
			
		}break;
		//--------------------------------------
		case MOVE_TO_ACTION:
		{
			Axon_Behaviour* pxMoveBehaviour = GetChildWithType( this, AVP_BEHAVIOUR_TYPE_HUMAN_MOVEMENT );
			
			if  ( pxMoveBehaviour && pxMoveBehaviour->IsInError() )
			{
				SwitchToState( FIND_ACTION );

			}
			else if  ( pxMoveBehaviour && pxMoveBehaviour->IsFinished() )
			{
				GetCivilianBrain()->GetCivilianActor()->SetInitialGamesceneGuid( m_uCurrentNode );
				SwitchToState( WAIT_FOR_COMPLETION );
			}

		}break;
		//--------------------------------------
		case WAIT_FOR_COMPLETION:
		{
			SwitchToState( FIND_ACTION );

		}break;
		//--------------------------------------
		default:
		{

		}break;
	}
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void AVP_Behaviour_Civilian_Wander::SwitchToState(WanderUpdateState eState)
{
	switch ( GetUpdateState() )
	{
		//-----------------------------------------------------
		case FIND_ACTION:
		{

		}break;
		//-----------------------------------------------------
		case MOVE_TO_ACTION:
		{
			if ( GetChildWithType( this, AVP_BEHAVIOUR_TYPE_HUMAN_MOVEMENT ) )
			{
				DestroyChildWithType( AVP_BEHAVIOUR_TYPE_HUMAN_MOVEMENT );
			}
		}
		//-----------------------------------------------------
		case WAIT_FOR_COMPLETION:
		{

		}break;
		//-----------------------------------------------------
		default:
		{

		}break;
	}

	SetUpdateState( eState );

	switch ( GetUpdateState() )
	{
		//-----------------------------------------------------
		case FIND_ACTION:
		{
			m_fFindActionTimer = 0.0f;

		}break;
		//-----------------------------------------------------
		case MOVE_TO_ACTION:
		{
			AVP_ServerNode_GS2Node* pxNode = AVP_ServerNode_GS2Node::FindEntity( m_uCurrentNode );

			if ( pxNode )
			{
				CreateMovementBehaviour( pxNode->GetPosition() );
				m_uLastActionGuid = m_uCurrentNode;
				pxNode->SetIsOccupied( true );
			}

		}break;
		//-----------------------------------------------------
		case WAIT_FOR_COMPLETION:
		{

		}break;
		//-----------------------------------------------------
		default:
		{

		}break;
	}
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void AVP_Behaviour_Civilian_Wander::CreateMovementBehaviour(const Asura_Vector_3& xGoToPoint)
{
	AVP_Behaviour_Human_Movement* pxHumanBehaviour = (AVP_Behaviour_Human_Movement*)CreateChildWithType(AVP_BEHAVIOUR_TYPE_HUMAN_MOVEMENT);

	if (pxHumanBehaviour)
	{
		pxHumanBehaviour->GetToPosition(xGoToPoint, AVP_Navigation::MM_SLOW, true);
		pxHumanBehaviour->SetDestinationGS2NodeGuid( m_uCurrentNode );
	}
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
bool AVP_Behaviour_Civilian_Wander::FindNextGS2Action()
{
	// Validated by the brain every frame.
	AVP_ServerActor_Civilian* pxOurActor = GetCivilianBrain()->GetCivilianActor();

	// Set our old node un-occupied
	AVP_ServerNode_GS2Node* pxOldNode = AVP_ServerNode_GS2Node::FindEntity( m_uLastActionGuid ); if ( pxOldNode ) { pxOldNode->SetIsOccupied( false ); }

	// Clear our current node guid
	m_uCurrentNode = ASURA_GUID_UNREGISTERED;
	
	// Find how many nodes we can use
	u_int uNumNodes = 0;

	// Iterate through all the GS2 nodes
	for (Asura_Hashed_List_It<AVP_ServerNode_GS2Node> it(AVP_ServerNode_GS2Node::GetList()); !it.Done(); it.Next())
	{
		AVP_ServerNode_GS2Node* pxNode = it.GetCurrent();

		if (pxNode && pxNode->IsEntityActive() && !pxNode->IsOccupied() && pxNode->GetStructureType() == AVP_GS_ST_CIVILIAN_ACTION && pxNode->GetGuid() != m_uLastActionGuid)
		{
			float fDistanceSq = ( pxNode->GetPosition() - pxOurActor->GetPosition() ).MagnitudeSquared();



			if (fDistanceSq < 400.0f)
			{
				++uNumNodes;
			}
		}
	}

	if (uNumNodes <= 0)
	{
		return false;
	}

	// Claim an array that we can store our guids in.
	Asura_Guid* uAvailableGuids = new Asura_Guid[uNumNodes];

	u_int uNumProcessed = 0;

	// Iterate through all the GS2 nodes again!
	for (Asura_Hashed_List_It<AVP_ServerNode_GS2Node> it(AVP_ServerNode_GS2Node::GetList()); !it.Done(); it.Next())
	{
		AVP_ServerNode_GS2Node* pxNode = it.GetCurrent();

		if (pxNode && pxNode->IsEntityActive() && !pxNode->IsOccupied() && pxNode->GetStructureType() == AVP_GS_ST_CIVILIAN_ACTION && pxNode->GetGuid() != m_uLastActionGuid)
		{
			float fDistanceSq = ( pxNode->GetPosition() - pxOurActor->GetPosition() ).MagnitudeSquared();

			const AVP_Navigation::NavData& xNavData = GetCivilianBrain()->GetNavData();
			AVP_Navigation::TestParams xTestParams;

			xTestParams.m_usZone				= xNavData.m_usCurrentZone;
			xTestParams.m_uPathfinderFlags		=  AVP_Navigation::AVP_AI_PATHFINDER_FLAG_HUMANOID;
			xTestParams.m_xNormal				= -GetCivilianBrain()->GetCivilianActor()->GetCurrentGravityDirection();
			xTestParams.m_bRejectNonalignedWPs	= true;

			u_short uWPID = ASURA_INVALID_WAYPOINT_ID;
			Asura_Vector_3 xTestPos( pxNode->GetPosition() );
			AVP_Navigation::GetNearestWaypointID( xTestPos, uWPID, xTestParams );

			if ( uWPID != ASURA_INVALID_WAYPOINT_ID )
			{
				if ( Asura_AI_Navigation_System::IsPositionInWaypoint( xTestPos, uWPID ) && ( fDistanceSq < 400.0f ) )
				{
					uAvailableGuids[ uNumProcessed ] = pxNode->GetGuid();
					++uNumProcessed;
				}
			}
		}
	}

	if ( uNumProcessed > 0 )
	{
		if ( uNumProcessed == 1 )
		{
			m_uCurrentNode = uAvailableGuids[0];
		}
		else
		{
			while ( m_uCurrentNode == m_uLastActionGuid || m_uCurrentNode == ASURA_GUID_UNREGISTERED )
			{
				// Get the Guid we want
				m_uCurrentNode = uAvailableGuids[Asura_Random::GetIntU(0, uNumProcessed - 1)];
			}
		}
	}

	// Clean up after ourselves;
	delete [] uAvailableGuids;

	if ( m_uCurrentNode != ASURA_GUID_UNREGISTERED )
	{
		return true;
	}
	else
	{
		return false;
	}
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#ifndef ASURA_NO_DEBUG_OUTPUT
static const char* s_aszStateStrings[AVP_Behaviour_Civilian_Wander::MAX_STATES] =
{
	"WANDER - UNSET",
	"FIND_ACTION",
	"MOVE_TO_ACTION",
	"WAIT_FOR_COMPLETION",
};

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

	Asura_Font_System::Print
	(
		GetProjectBrain()->GetDebugOutputPos().x,
		GetProjectBrain()->GetDebugOutputPos().y,
		0.5f, 
		0xff00ff00,
		ASURA_TEXTFORMAT_CENTREJUSTIFIED, 
		s_aszStateStrings[GetUpdateState()]
	);

	// Offset the debug output position for other behaviours to render there own text
	GetProjectBrain()->GetDebugOutputPos().y += 10.0f;
}
#endif
