//////////////////////////////////////////////////////////////////////
// Asura_ServerEntity_VolumeTrigger.cpp								//
// SJL 6/11/07														//
// Copyright (C) 2007 Rebellion, All Rights Reserved.			    //
//																	//
// A volume trigger with trigger and untrigger functionality that   //
// will check entities contained in project-specific lists.			//	
//////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header.
#include "Entity/Triggers/Asura_ServerEntity_VolumeTrigger.h"

// Engine independent.
#include "Core/Memory/Asura_Mem.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Entity/Asura_Entity_Classification.h"
#include "Entity/PhysicalObject/Asura_ServerEntity_PhysicalObject.h"
#include "Network/Asura_Network.h"
#include "Network/Asura_Message.h"
#include "Volume/Asura_Volume.h"
#include "Debugging/Asura_DebugLineRender.h"
#include "Interface/Asura_CommandConsole.h"

#ifdef ASURA_SUPPORT_ENTITYWATCH
#include "Entity/Asura_EntityWatch_System.h"
#endif

#ifdef ASURA_TOOLS
#include "EntityTemplate/Tools_EntityTemplate_System.h"
#endif

#ifdef ASURA_VOLUME_TRIGGER_DEBUG_RENDERING
bool Asura_ServerEntity_VolumeTrigger::s_bRenderVolumeTriggers = false;
unsigned int Asura_ServerEntity_VolumeTrigger::s_uConsoleVarReferenceCount = 0;
#endif

Asura_Hashed_List<Asura_ServerEntity_VolumeTrigger> Asura_ServerEntity_VolumeTrigger::s_xList(3);

Asura_ServerEntity_VolumeTrigger::Asura_ServerEntity_VolumeTrigger(Asura_Guid uGuid)
:Asura_ServerEntity_Trigger(uGuid)
{
	Classification			= AsuraEntityClass_VolumeTrigger;
	m_uFlags 				= 0;
	m_bTriggerOnce			= true;
	m_uSpecificGuid			= ASURA_GUID_UNREGISTERED;
	m_pxVolumeGroup = new Asura_VolumeGroup();
	m_uParentObjectGuid		= ASURA_GUID_UNREGISTERED;

	#ifdef ASURA_VOLUME_TRIGGER_DEBUG_RENDERING
	if (s_uConsoleVarReferenceCount == 0)
	{
		Asura_CommandConsole::AddVar (L"Debug.Entity.DisplayVolumeTriggers", &s_bRenderVolumeTriggers, Asura_CommandConsole::REMOVE_AT_SHUTDOWN);
	}

	s_uConsoleVarReferenceCount ++;
	#endif

	s_xList.AddToList(this);
}



Asura_ServerEntity_VolumeTrigger::~Asura_ServerEntity_VolumeTrigger()
{
	delete m_pxVolumeGroup;

	/*
	#ifdef ASURA_VOLUME_TRIGGER_DEBUG_RENDERING
	s_uConsoleVarReferenceCount --;

	if (s_uConsoleVarReferenceCount == 0)
	{
		Asura_CommandConsole::RemoveVar (L"Debug.Entity.DisplayVolumeTriggers");
	}
	#endif
	*/

	s_xList.RemoveFromList(this);
}

bool Asura_ServerEntity_VolumeTrigger::IsTriggered(void)
{
	if (m_bHasAlreadyTriggered) 
	{
		return false;
	}
	if (IsVolumeOccupied() != (m_bTriggerOnNotOccupied ? true : false))
	{
		m_bHasAlreadyTriggered = true;
		return true;
	}
	return false;
}

bool Asura_ServerEntity_VolumeTrigger::IsUnTriggered(void)
{
	if (!m_bHasAlreadyTriggered)
	{
		return false;
	}
	if (IsVolumeOccupied() == (m_bTriggerOnNotOccupied ? true : false))
	{
		m_bHasAlreadyTriggered = false;
		return true;
	}
	return false;
}

bool Asura_ServerEntity_VolumeTrigger::IsVolumeOccupied() const
{

	// This function needs over-riding by a project-class for any functionality.
	return false;
}

void Asura_ServerEntity_VolumeTrigger::OnUnTrigger()
{
	Asura_ServerEntity_Trigger::OnUnTrigger();
	if(m_bTriggerOnce)
	{
		//deactivate this trigger so it doesn't get triggered again
		OnDeactivation();
	}
}


#ifdef ASURA_TOOLS
void Asura_ServerEntity_VolumeTrigger::ApplyChangesForTemplateInstance(Tools_EntityTemplateInstance* pxInstance)
{
	Asura_ServerEntity_Trigger::ApplyChangesForTemplateInstance(pxInstance);

	m_uSpecificGuid = pxInstance->ConvertGuid(m_uSpecificGuid);

	m_uParentObjectGuid = pxInstance->ConvertGuid (m_uParentObjectGuid);

	if (m_pxVolumeGroup)
	{
		m_pxVolumeGroup->ApplyChangesForTemplateInstance(pxInstance);
	}
}
#endif // ASURA_TOOLS


#ifdef ASURA_SUPPORT_ENTITYWATCH
void Asura_ServerEntity_VolumeTrigger::DisplayEntityWatchText()
{
	Asura_ServerEntity_Trigger::DisplayEntityWatchText();
	Asura_EntityWatch_System::PrintText(18,"Triggered : %s",m_bHasAlreadyTriggered ? "Yes" : "No");
}
#endif


void Asura_ServerEntity_VolumeTrigger::ReadFromChunkStream(Asura_Chunk_Stream& xStream)
{
	int iVersion;
	Asura_ReadFromStream_Bitfield_Wrapper<u_int>	xBitfield(m_uFlags);

	xStream >> iVersion;
	Asura_Assert(iVersion <= iASURA_VOLUME_TRIGGER_CHUNK_VERSION_SERVER, "Unexpected volume trigger chunk version");

	PARENT::ReadFromChunkStream(xStream);

	xStream >> xBitfield;
	xStream >> m_uSpecificGuid;

	if (iVersion >= 1)
	{
		xStream >> m_uParentObjectGuid;
	}

	bool bActive;
	xStream >> bActive; // backup, not needed for server entities, dealt with upclass.

	m_pxVolumeGroup->ReadFromChunkStream(xStream);


}

void Asura_ServerEntity_VolumeTrigger::WriteToChunkStream (Asura_Chunk_Stream& xStream) const
{
	const int iVersion = iASURA_VOLUME_TRIGGER_CHUNK_VERSION_SERVER;

	Asura_WriteToStream_Bitfield_Wrapper<u_int>	xFlagsBitfield (m_uFlags);

	// Create the bitfield...
	xFlagsBitfield.Add	(m_bHasAlreadyTriggered,	1);
	xFlagsBitfield.Add	(m_bTriggerOnce,			1);
	xFlagsBitfield.Add	(m_bTriggerOnNotOccupied,	1);
	xFlagsBitfield.Add  (m_bTriggersOnClient,		1);

	// Best practice is version then parent.
	xStream << iVersion;

	// fill in base class
	PARENT::WriteToChunkStream(xStream);

	xStream << xFlagsBitfield;
	xStream << m_uSpecificGuid;
	xStream << m_uParentObjectGuid;

	bool bActive = IsActive();
	xStream << bActive;

	m_pxVolumeGroup->WriteToChunkStream(xStream);

}



void Asura_ServerEntity_VolumeTrigger::Update()
{
	if (m_bTriggersOnClient) 
	{
		return;
	}

	// Update all the volume positions to match a parent object's position (if a parent object has been set)

	if (m_uParentObjectGuid != ASURA_GUID_UNREGISTERED)
	{
		Asura_ServerEntity_PhysicalObject* pxParentObject = static_cast <Asura_ServerEntity_PhysicalObject *> (Asura_ServerEntity_PhysicalObject::FindEntity (m_uParentObjectGuid));
		Asura_Assert (pxParentObject, "Could not find parent object from guid when updating volume trigger position");

		if (pxParentObject)
		{
			const Asura_Vector_3 xParentPosition = pxParentObject->GetPosition ();

			for (unsigned int n = 0; n < GetNumberOfVolumes (); n ++)
			{
				GetVolume (n)->SetPosition (xParentPosition);
			}
		}
	}

	#ifdef ASURA_VOLUME_TRIGGER_DEBUG_RENDERING
	// Render debug lines for volumes (if enabled in the command console)

	if (s_bRenderVolumeTriggers)
	{
		static Asura_DebugLineRender_LineSet* pxLineSet = NULL;

		if (!pxLineSet)
		{
			pxLineSet = Asura_DebugLineRender::CreateOneShotLineSet (128);
		}

		m_pxVolumeGroup->AddToLineSet (0xffff0000, *pxLineSet);
	}
	#endif

	Asura_ServerEntity_Trigger::Update();
}

void Asura_ServerEntity_VolumeTrigger::OnActivation()
{
	if (m_bTriggersOnClient)
	{
		//tell the client to activate
		Asura_Message* pxMessage = Asura_Network::CreateMessage(ASURA_MSG_ACTIVATE,0);
		pxMessage->To = ASURA_GUID_MY_CLIENT_ENTITIES;
		pxMessage->From = Guid;
		Asura_Network::Send(pxMessage);
	}
	PARENT::OnActivation();
}

void Asura_ServerEntity_VolumeTrigger::OnDeactivation()
{
	if (m_bTriggersOnClient)
	{
		//tell the client to deactivate
		Asura_Message* pxMessage = Asura_Network::CreateMessage(ASURA_MSG_DEACTIVATE,0);
		pxMessage->To = ASURA_GUID_MY_CLIENT_ENTITIES;
		pxMessage->From = Guid;
		Asura_Network::Send(pxMessage);
	}
	PARENT::OnDeactivation();
}


void Asura_ServerEntity_VolumeTrigger::OnDestruction()
{
	if (m_bTriggersOnClient)
	{
		// Destroy the client
		Asura_Message* pxMessage = Asura_Network::CreateMessage(ASURA_MSG_DESTRUCT,0);
		pxMessage->To = ASURA_GUID_MY_CLIENT_ENTITIES;
		pxMessage->From = Guid;
		Asura_Network::Send(pxMessage);
	}
	// And destroy ourselves.
	PARENT::OnDestruction();
}

