//////////////////////////////////////////////////////////////////////////////
//
// AVP_ServerNode_GS2Attractor.cpp
//
// Copyright (C) 2008 Rebellion. All Rights Reserved.
//
//////////////////////////////////////////////////////////////////////////////

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

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

// header
#include "Entity/Locations/OrientedNode/GamesceneNode/AttractorNode/AVP_ServerNode_GS2Attractor.h"

// asura
#include "ChunkSystem/Asura_Chunk_Stream.h"

// axon

// avp
#include "Entity/AVP_Entity_Classification.h"
#include "Entity/PhysicalObject/Actor/AVP_ServerEntity_Actor.h"

//////////////////////////////////////////////////////////////////////////////
//
// AVP_ServerNode_GS2Attractor
//
//////////////////////////////////////////////////////////////////////////////

AVP_ServerNode_GS2Attractor::AVP_ServerNode_GS2Attractor( Asura_Guid uGuid )
: Asura_ServerNode_GS2Node( uGuid )
, AVP_ServerNode_GS2Node( uGuid )
, Asura_ServerNode_GS2Attractor( uGuid )
{
	Classification = AVP_ENTITYCLASS_GS2ATTRACTOR;
}

void AVP_ServerNode_GS2Attractor::ReadFromChunkStream( Asura_Chunk_Stream& xStream )
{
	int iVersion;
	xStream >> iVersion;

	// Parent.
	Asura_ServerNode_GS2Attractor::ReadFromChunkStream( xStream );
	AVP_ServerNode_GS2Node::ReadFromChunkStream( xStream );

	// Us.
	if ( iVersion == CURRENT_CHUNK_VERSION )
	{
	}
	else
	{
		Asura_Assertf( false, ( "AVP_ServerNode_GS2Attractor::ReadFromChunkStream - unhandled chunk version (%d)", iVersion ) );
	}
}

void AVP_ServerNode_GS2Attractor::WriteToChunkStream( Asura_Chunk_Stream& xStream ) const
{
	// Version
	xStream << static_cast< int >( CURRENT_CHUNK_VERSION );

	// Parent
	Asura_ServerNode_GS2Attractor::WriteToChunkStream( xStream );
	AVP_ServerNode_GS2Node::WriteToChunkStream( xStream );

	// Our data
}

bool AVP_ServerNode_GS2Attractor::IsBaseOrDerivativeOf( u_short usClassification ) const
{
	if ( usClassification == AVP_ENTITYCLASS_GS2ATTRACTOR )
	{
		return true;
	}
	return AVP_ServerNode_GS2Node::IsBaseOrDerivativeOf( usClassification ) || Asura_ServerNode_GS2Attractor::IsBaseOrDerivativeOf( usClassification );
}

bool AVP_ServerNode_GS2Attractor::HandleMessage( const Asura_Message* pxMessage )
{
	bool bRet = false;
	bRet |= AVP_ServerNode_GS2Node::HandleMessage( pxMessage );
	bRet |= Asura_ServerNode_GS2Attractor::HandleMessage( pxMessage );
	return bRet;
}

#ifdef ASURA_TOOLS
bool AVP_ServerNode_GS2Attractor::CanBeIncludedInTemplate() const
{
	return ( Classification == AVP_ENTITYCLASS_GS2ATTRACTOR );
}
#endif

void AVP_ServerNode_GS2Attractor::Create( const Asura_Guid uGuid, Asura_Chunk_Stream& xStream )
{
	AVP_ServerNode_GS2Attractor* pxNode = new AVP_ServerNode_GS2Attractor( uGuid );
	pxNode->ReadFromChunkStream( xStream );
}

void AVP_ServerNode_GS2Attractor::SendAttractionPulse()
{
	const float fAttractRangeSq = GetAttractRange() * GetAttractRange();

	if ( GetAttractType() == AXON_GS2_AAT_ANYTHING )
	{
		// Try and attract anything.
		enum { GUID_MASK = 0x00000003 };
		if ( ++m_uGuidMask > GUID_MASK )
		{
			m_uGuidMask = 0;
		}

		for ( Asura_Hashed_List_ConstIt< AVP_ServerEntity_Actor > xActorIter( AVP_ServerEntity_Actor::GetList() ); !xActorIter.Done(); xActorIter.Next() )
		{
			const AVP_ServerEntity_Actor* const pxActor = xActorIter.GetCurrent();

			const u_int uMaskedGuid = pxActor->GetGuid() & GUID_MASK;
			if ( uMaskedGuid != m_uGuidMask )
			{
				continue;
			}

			// Is the entity not active or dead?
			if ( !pxActor->IsEntityActive() || pxActor->IsDead() )
			{
				continue;
			}

			// Is this entity within range?
			if ( GetAttractRange() > 0.0f && ( pxActor->GetPosition() - GetPosition() ).MagnitudeSquared() > fAttractRangeSq )
			{
				continue;
			}

			SendAttractionPulseToEntity( pxActor );
			// If we've got a bite we must stop iterating.
			if ( IsAttractingSomething() )
			{
				return;
			}
		}
	}
	else if ( GetSpecificGuid() != ASURA_GUID_UNREGISTERED )
	{
		// Try and attract our specific guid.
		const AVP_ServerEntity_Actor* const pxActor = AVP_ServerEntity_Actor::FindEntity( GetSpecificGuid() );
		if ( pxActor )
		{
			// Obviously, if the entity is dead, or not active, we don't want to attempt to attract.
			if ( pxActor->IsEntityActive() && !pxActor->IsDead() )
			{
				if ( GetAttractRange() == 0.0f || ( pxActor->GetPosition() - GetPosition() ).MagnitudeSquared() < fAttractRangeSq )
				{
					SendAttractionPulseToEntity( pxActor );
				}
			}
		}
	}
}

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