//*******************************************************************
//
// File: Asura_Affector_System.cpp                  
//
// Created: 9 Nov 2006
//
// Description: 
//
//*******************************************************************

// system libraries
#include "Core/Asura.h"

#if defined( ASURA_USE_AFFECTORS) && 1 == ASURA_USE_AFFECTORS

// main header
#include "Affectors/Asura_Affector_System.h"

// other libraries
#include "Affectors/Asura_Affector.h"
#include "Affectors/Asura_Affector_Global.h"
#include "Core/Asura_Mempool.h"
#include "Core/Asura_Timers.h"
#include "Debugging/Asura_Assert.h"

//*******************************************************************
// Constants
//*******************************************************************

//*******************************************************************
// Local Classes
//*******************************************************************


//*******************************************************************
// Local Data
//*******************************************************************

Asura_TrackingHandlePool<Asura_Affector>*			Asura_Affector_System::s_pxDynamicAffectorList;
Asura_TrackingHandlePool<Asura_Affector_Global>*	Asura_Affector_System::s_pxDynamicGlobalAffectorList;

float												Asura_Affector_System::s_fGlobalAffectorStrength	= 0.0f;
float												Asura_Affector_System::s_fGlobalAffectorTime		= 0.0f;

u_int												Asura_Affector_System::s_uNumActiveAffectors		= 0;
u_int												Asura_Affector_System::s_uNumActiveGlobalAffectors	= 0;
u_int												Asura_Affector_System::s_uNumActiveLocalAffectors	= 0;


//*******************************************************************
// Function Definitions
//*******************************************************************

void Asura_Affector_System::Initialise(void)
{
	s_pxDynamicAffectorList			= new Asura_TrackingHandlePool<Asura_Affector>(16);
	s_pxDynamicGlobalAffectorList	= new Asura_TrackingHandlePool<Asura_Affector_Global>(16);

	s_fGlobalAffectorStrength	= 0.0f;
	s_fGlobalAffectorTime		= 0.0f;

	s_uNumActiveAffectors		= 0;
	s_uNumActiveLocalAffectors	= 0;
	s_uNumActiveGlobalAffectors	= 0;
}

void Asura_Affector_System::Reset(void)
{
	Asura_Assert(s_pxDynamicAffectorList, "Reset called when not Initialised - crash imminent.");
	s_pxDynamicAffectorList->Reset();
	s_pxDynamicGlobalAffectorList->Reset();

	s_fGlobalAffectorStrength	= 0.0f;
	s_fGlobalAffectorTime		= 0.0f;

	s_uNumActiveAffectors		= 0;
	s_uNumActiveGlobalAffectors	= 0;
	s_uNumActiveLocalAffectors	= 0;
}

void Asura_Affector_System::Update(void)
{
	Asura_TrackingHandlePool<Asura_Affector>::Iterator			xIterator			= s_pxDynamicAffectorList->GetIterator();
	Asura_TrackingHandlePool<Asura_Affector_Global>::Iterator	xGlobalIterator		= s_pxDynamicGlobalAffectorList->GetIterator();
	const float													fTimeStep			= Asura_Timers::GetGameTimeSlice();
	float														fTotalResponseScale	= 0.0f;

	// Reset the active flag...
	s_uNumActiveAffectors		= 0;
	s_uNumActiveGlobalAffectors	= 0;
	s_uNumActiveLocalAffectors	= 0;

	// Check all of the affectors...
	for (xIterator.Begin(); !xIterator.End(); xIterator.Next())
	{
		Asura_Affector*	pxAffector	= xIterator.GetCurrentPointer();

		if(pxAffector->IsActive())
		{
			s_uNumActiveAffectors++;
			s_uNumActiveLocalAffectors++;
		}
	}

	// Check all of the global-affectors...
	s_fGlobalAffectorStrength	= 0.0f;

	for(xGlobalIterator.Begin(); !xGlobalIterator.End(); xGlobalIterator.Next())
	{
		Asura_Affector_Global*	pxAffector	= xGlobalIterator.GetCurrentPointer();

		if(pxAffector->IsActive())
		{
			s_fGlobalAffectorStrength	+= pxAffector->GetStrength();
			fTotalResponseScale			+= pxAffector->GetResponseScale();

			s_uNumActiveAffectors++;
			s_uNumActiveGlobalAffectors++;
		}
	}

	// Update the time used by the global-affectors...
	s_fGlobalAffectorTime	+= (s_uNumActiveGlobalAffectors > 0)
							 ? (fTimeStep * (fTotalResponseScale / s_uNumActiveGlobalAffectors))
							 : fTimeStep;
}

void Asura_Affector_System::Shutdown(void)
{
	delete(s_pxDynamicAffectorList);
	delete(s_pxDynamicGlobalAffectorList);

	s_pxDynamicAffectorList			= NULL;
	s_pxDynamicGlobalAffectorList	= NULL;

	s_uNumActiveAffectors		= 0;
	s_uNumActiveGlobalAffectors	= 0;
	s_uNumActiveLocalAffectors	= 0;
}


Asura_Handle Asura_Affector_System::CreateAffector(void)
{
	const Asura_Handle	iHandle	= s_pxDynamicAffectorList->Allocate();

	if (iHandle == ASURA_INVALID_HANDLE)
	{
		return ASURA_INVALID_HANDLE;
	}
	else
	{
		Asura_Affector* pxAffector = s_pxDynamicAffectorList->GetPointer(iHandle);

		pxAffector->Reset();
		return(iHandle);
	}
}

void Asura_Affector_System::DestroyAffector(const Asura_Handle iHandle)
{
	// The delete operator, etc., tolerates NULL ptrs so it kind of makes
	// sense that we should here as well.
	if(iHandle != ASURA_INVALID_HANDLE)
	{
		s_pxDynamicAffectorList->Deallocate(iHandle);
	}
}

Asura_Affector* Asura_Affector_System::GetAffectorPointer(const Asura_Handle iHandle)
{
	Asura_Assert( iHandle!=ASURA_INVALID_HANDLE, "Invalid handle!" );

	return s_pxDynamicAffectorList->GetPointer(iHandle);
}


Asura_Handle Asura_Affector_System::CreateGlobalAffector(void)
{
	const Asura_Handle	iHandle	= s_pxDynamicGlobalAffectorList->Allocate();

	if (iHandle == ASURA_INVALID_HANDLE)
	{
		return NULL;
	}
	else
	{
		Asura_Affector_Global* pxAffector = s_pxDynamicGlobalAffectorList->GetPointer(iHandle);

		pxAffector->Reset();
		return(iHandle);
	}
}

void Asura_Affector_System::DestroyGlobalAffector(const Asura_Handle iHandle)
{
	// The delete operator, etc., tolerates NULL ptrs so it kind of makes
	// sense that we should here as well.
	if(iHandle != ASURA_INVALID_HANDLE)
	{
		s_pxDynamicGlobalAffectorList->Deallocate(iHandle);
	}
}

Asura_Affector_Global* Asura_Affector_System::GetGlobalAffectorPointer(const Asura_Handle iHandle)
{
	Asura_Assert(iHandle!=ASURA_INVALID_HANDLE, "Invalid handle!");
	return(s_pxDynamicGlobalAffectorList->GetPointer(iHandle));
}

void Asura_Affector_System::MakeAffectorListForBoundingBox( const Asura_Bounding_Box& xBoundingBox, const u_int uAffectorArraySizeMax, Asura_Affector** papxAffectorsFound, u_int& uAffectorArraySize)
{
#if defined( ASURA_ASSERT)
	u_int uAffectorArraySizeWanted = 0;
#endif
	uAffectorArraySize = 0;

	Asura_TrackingHandlePool< Asura_Affector>::Iterator xIteratorAffector = s_pxDynamicAffectorList->GetIterator();
	for ( xIteratorAffector.Begin(); !xIteratorAffector.End(); xIteratorAffector.Next())
	{
		Asura_Affector* const pxAffector = xIteratorAffector.GetCurrentPointer();

		if ( false == pxAffector->IsActive() )
		{
			continue;
		}

		const bool bIntersects = xBoundingBox.IntersectsSphere( pxAffector->GetPosition(), pxAffector->GetRange());
		if( false == bIntersects)
		{
			continue;
		}

		Asura_Assertf( uAffectorArraySize < uAffectorArraySizeMax, ("Supplied Array (%d) not large enough to store all of the found Affectors", uAffectorArraySizeMax));
		if( uAffectorArraySizeMax <= uAffectorArraySize)
		{
#if defined( ASURA_ASSERT)
			uAffectorArraySizeWanted += 1;
			continue;
#else
			break;
#endif
		}

		papxAffectorsFound[ uAffectorArraySize] = pxAffector;
		uAffectorArraySize += 1;

#if defined( ASURA_ASSERT)
		uAffectorArraySizeWanted += 1;
#endif
	}

	Asura_Assertf( uAffectorArraySize <= uAffectorArraySizeMax, ("Supplied Array (%d) not large enough to store all of the found Affectors. %d found Total.", uAffectorArraySizeMax, uAffectorArraySizeWanted));
}

#endif //defined( ASURA_USE_AFFECTORS) && 1 == ASURA_USE_AFFECTORS

//*******************************************************************
//
// (c) 2006 Rebellion
//
// Warning: This computer program is protected by copyright law and 
// international treaties. Unauthorized reproduction or distribution
// of this program, or any portion of it, may result in severe civil and
// criminal penalties, and will be prosecuted to the maximum extent 
// possible under the law.
//
//*******************************************************************
