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

// asura
#ifndef ASURA_NO_DEBUG_OUTPUT
#include "Debugging/Asura_Logfile.h"
#endif

// axon

// avp

// avp axon
#include "Behaviour/BehaviourTypes/AVP_Behaviour_Types.h"
#include "Core/BrainTypes/Axon_Project_Brain.h"

//////////////////////////////////////////////////////////////////////////////
//
// AVP_BehaviourTree_Element
//
//////////////////////////////////////////////////////////////////////////////

AVP_BehaviourTree_Element::AVP_BehaviourTree_Element( Axon_Project_Brain* pxBrain, u_int uType )
: PARENT( pxBrain, uType )
, m_bRunningConditionsMet( false )
, m_bEvaluateChildren( true )
, m_bIsAtomic( true )
, m_pxActiveChild( NULL )
, m_bChildBehavioursCreated( false )
, m_uPriority( 0 )
, m_fProbability( 1.0f )
{
}

AVP_BehaviourTree_Element::~AVP_BehaviourTree_Element()
{
}

void AVP_BehaviourTree_Element::Deactivate()
{
	DestroyAllChildren( false );

	m_pxActiveChild = NULL;
	m_bChildBehavioursCreated = false;

	PARENT::Deactivate();
}

void AVP_BehaviourTree_Element::SetActiveChild( AVP_BehaviourTree_Element* pxActiveChild )
{
	if ( m_pxActiveChild )
	{
		m_pxActiveChild->DoDeactivate();
		m_pxActiveChild = NULL;
	}

	m_pxActiveChild = pxActiveChild;
	Asura_Assert( m_pxActiveChild, "Something went wrong when choosing the new AI behaviour!" );
	m_pxActiveChild->DoActivate();
}

void AVP_BehaviourTree_Element::PerFrameUpdate( const float fTimeslice )
{
	if( IsFinished() || IsInError() )
	{
		return;
	}

	if( !m_bChildBehavioursCreated )
	{
		CreateChildBehaviours();
		m_bChildBehavioursCreated = true;
	}

	if( !m_bEvaluateChildren )
	{
		// This behaviour doesn't have children that use the behaviour tree evaluation, just return
		return;
	}

	// Update all of the behaviours' running conditions.
	AVP_BehaviourTree_Element* pxChildBehaviour = static_cast<AVP_BehaviourTree_Element*>( GetChild() );
	while ( pxChildBehaviour && ( pxChildBehaviour->GetType() >= AXON_BEHAVIOUR_TYPE_FIRST_PROJECT_SPECIFIC ) )
	{
		pxChildBehaviour->UpdateRunningConditions( fTimeslice );
		pxChildBehaviour = static_cast<AVP_BehaviourTree_Element*>( pxChildBehaviour->GetSibling() );
	}
}

void AVP_BehaviourTree_Element::Update( const float fTimeslice )
{
	if( IsFinished() || IsInError() )
	{
		return;
	}

	if( !m_bChildBehavioursCreated )
	{
		CreateChildBehaviours();
		m_bChildBehavioursCreated = true;
	}

	if( !m_bEvaluateChildren )
	{
		// This behaviour doesn't have children that use the behaviour tree evaluation, just return
		return;
	}

	// Work out if we should switch child behaviour.
	AVP_BehaviourTree_Element* pxChildBehaviour = static_cast<AVP_BehaviourTree_Element*>( GetChild() );
	if ( pxChildBehaviour )
	{
		// Evaluate active child behaviour
		// TODO: This will occasionally evaluate running conditions for active child behaviour twice. Store first result and re-use!
		if( m_pxActiveChild )
		{
			bool bDeactivate = ( m_pxActiveChild->IsFinished() || m_pxActiveChild->IsInError() );

			if( !bDeactivate )
			{
				m_pxActiveChild->EvaluateRunningConditions();
				bDeactivate = !m_pxActiveChild->RunningConditionsMet();
			}

			if( bDeactivate )
			{
				m_pxActiveChild->DoDeactivate();
				m_pxActiveChild = NULL;
			}
		}

		// Store priorities
		const int iCurrentChildPriority = m_pxActiveChild ? static_cast<int>( m_pxActiveChild->GetPriority() ) : -1;
		int iTopPriority = iCurrentChildPriority;

		// List of top-priority child behaviours
		Asura_List<AVP_BehaviourTree_Element> xTopPriorityChildList;
		float fTotalProbability = 0.0f;

		// Iterate through children
		while( pxChildBehaviour && ( pxChildBehaviour->GetType() >= AXON_BEHAVIOUR_TYPE_FIRST_PROJECT_SPECIFIC ) )
		{
			const int iChildPriority = static_cast<int>( pxChildBehaviour->GetPriority() );

			// Don't interrupt current behaviour, if priority is not higher, or the current behaviour is not atomic
			// Note: no need to check m_pxActiveChild here, the priorities take care of that implicitly
			if( ( iChildPriority >= iTopPriority ) &&
				( ( iChildPriority > iCurrentChildPriority ) || ( ( iChildPriority == iCurrentChildPriority ) && ( !m_pxActiveChild->m_bIsAtomic ) ) ) )
			{
				// Check running conditions (See TODO comment a few lines up about duplicate evaluation!)
				if ( pxChildBehaviour != m_pxActiveChild )
				{
					pxChildBehaviour->EvaluateRunningConditions();
				}
				if( pxChildBehaviour->RunningConditionsMet() )
				{
					// Higher priority child found
					if( iChildPriority > iTopPriority )
					{
						xTopPriorityChildList.ClearList();
						iTopPriority = iChildPriority;
						fTotalProbability = 0.0f;
					}

					// Add to top-priority list
					xTopPriorityChildList.AddToList( pxChildBehaviour );
					fTotalProbability += pxChildBehaviour->GetProbability();
				}
			}

			// Take next child...
			pxChildBehaviour = static_cast<AVP_BehaviourTree_Element*>( pxChildBehaviour->GetSibling() );
		}

		if( xTopPriorityChildList.GetNumEntries() > 0 )
		{
			// More than one valid top-priority behaviour found...
			if( xTopPriorityChildList.GetNumEntries() > 1 )
			{
				// ..choose next behaviour randomly
				float fRandomFloat = Asura_Random::GetFloat( 0.0f, fTotalProbability );

				while( xTopPriorityChildList.GetHeadObject() && ( xTopPriorityChildList.GetHeadObject()->GetProbability() < fRandomFloat ) )
				{
					fRandomFloat -= xTopPriorityChildList.GetHeadObject()->GetProbability();
					xTopPriorityChildList.PopHeadObject();
				}
			}

			if ( xTopPriorityChildList.GetHeadObject() != m_pxActiveChild )
			{
				if( m_pxActiveChild )
				{
					m_pxActiveChild->DoDeactivate();
					m_pxActiveChild = NULL;
				}

				m_pxActiveChild = xTopPriorityChildList.GetHeadObject();
				Asura_Assert( m_pxActiveChild, "Something went wrong when choosing the new AI behaviour!" );
				m_pxActiveChild->DoActivate();
			}
		}
	}
}

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