//
// Asura_ServerEntity_ProxyTrigger.cpp
//
// Created by Richard Rice - 21/09/2006
// 
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"
#include "Entity/Triggers/Asura_ServerEntity_ProxyTrigger.h"

#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Network/Asura_Message.h"
#ifdef ASURA_USE_STREAMING
#include "Streaming/Asura_Streaming_System.h"
#endif

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

static const int iASURA_SERVERENTITY_PROXYTRIGGER_VERSION_NUMBER = 2;


#ifdef ASURA_TOOLS
Asura_Hashed_List<Asura_ServerEntity_ProxyTrigger> Asura_ServerEntity_ProxyTrigger::s_xList(5);
#endif


#ifdef ASURA_TOOLS
Asura_ServerEntity_ProxyTrigger::Asura_ServerEntity_ProxyTrigger()
:Asura_Entity_Server(Asura_Entity_System::GetCreatedByToolsGuid())
{
	#ifdef ASURA_TOOLS
	s_xList.AddToList(this);
	#endif
	Initialise();
}
#endif // ASURA_TOOLS

Asura_ServerEntity_ProxyTrigger::Asura_ServerEntity_ProxyTrigger(Asura_Guid uGuid)
:Asura_Entity_Server(uGuid)
{
	#ifdef ASURA_TOOLS
	s_xList.AddToList(this);
	#endif
	Initialise();
}

Asura_ServerEntity_ProxyTrigger::~Asura_ServerEntity_ProxyTrigger()
{
	#ifdef ASURA_TOOLS
	s_xList.RemoveFromList(this);
	#endif
}

void Asura_ServerEntity_ProxyTrigger::Create(Asura_Guid uGuid, Asura_Chunk_Stream& xStream)
{
	Asura_ServerEntity_ProxyTrigger* pxTrigger = new Asura_ServerEntity_ProxyTrigger(uGuid);

	if( pxTrigger )
	{
		pxTrigger->ReadFromChunkStream(xStream);
	}
}

void Asura_ServerEntity_ProxyTrigger::Initialise()
{
	Classification = AsuraEntityClass_ProxyTrigger;

	m_iTargetClass = AsuraEntityClass_NotSet;
	m_uProxyFlags = 0;

	SetNeverUpdateClient();
	SetNeverUpdates(true);
}


void Asura_ServerEntity_ProxyTrigger::ReadFromChunkStream(Asura_Chunk_Stream& xStream)
{
	int iVersion;
	xStream >> iVersion;
	Asura_Assertf(iVersion <= iASURA_SERVERENTITY_PROXYTRIGGER_VERSION_NUMBER,("Unknown chunk version %d for proxy trigger",iVersion));

	//read in parent class data if new enough
	if(iVersion >= 1)
	{
		Asura_Entity_Server::ReadFromChunkStream(xStream);
	}

	xStream >> m_uProxyFlags;
	xStream >> m_iTargetClass;

	//read in the target list
	u_int uNumTargets;
	xStream >> uNumTargets;
	for(u_int i=0;i<uNumTargets;i++)
	{
		Asura_Guid uGuid;
		xStream >> uGuid;
		#if defined( ASURA_USE_STREAMING) && !defined(ASURA_TOOLS)
		uGuid = Asura_Streaming_System::MakeCurrentMasterZoneGuid(uGuid);
		#endif

		m_xTargetList.Push_Back(uGuid);
	}


	if(iVersion < 2)
	{
		//Old versions stored the active flag in the proxy trigger flags , so copy to the base class
		//(m_uProxyFlags only contained the one flag for that version , so no need to check the bit explicitly)
		SetEntityActiveFromLoad(m_uProxyFlags != 0);
		m_uProxyFlags = 0;
	}
}

void Asura_ServerEntity_ProxyTrigger::WriteToChunkStream(Asura_Chunk_Stream& xStream) const
{
	xStream << iASURA_SERVERENTITY_PROXYTRIGGER_VERSION_NUMBER;

	Asura_Entity_Server::WriteToChunkStream(xStream);


	xStream << m_uProxyFlags;
	xStream << m_iTargetClass;
	
	xStream << m_xTargetList.Size();
	for(const Asura_ListElt<Asura_Guid>* pxIt = m_xTargetList.Front(); pxIt; pxIt = pxIt->Next())
	{
		xStream << pxIt->Data();
	}
}

bool Asura_ServerEntity_ProxyTrigger::HandleMessage(const Asura_Message* pxMessage)
{
    if( IsDestroyed() ) { return true; } // ignore all further messages?

	if (pxMessage)
	{
		int iMessageType = pxMessage->GetMsgID();
		switch (iMessageType)
		{
			case ASURA_MSG_PROXY_ACTIVATE :
				SetEntityActive(true);
				return true;

			case ASURA_MSG_PROXY_DEACTIVATE :
				SetEntityActive(false);
				return false;

			case ASURA_MSG_PROXY_ADDTARGET :
			case ASURA_MSG_PROXY_REMOVETARGET :
			case ASURA_MSG_PROXY_SETTARGET :
			{	
				const Asura_Message_ProxyTrigger_Target* pxMessageData = reinterpret_cast
					<const Asura_Message_ProxyTrigger_Target*>(pxMessage->GetData());

				Asura_Guid uGuid = pxMessageData->m_uTargetGuid;
				
				if(iMessageType == ASURA_MSG_PROXY_REMOVETARGET)
				{
					Asura_ListElt<Asura_Guid>* pxElem = m_xTargetList.Find(uGuid);
					if(pxElem) m_xTargetList.Remove(pxElem);
				}
				else
				{
					if(iMessageType == ASURA_MSG_PROXY_SETTARGET)
					{
						//if using set target then we just want the new target in the list
						m_xTargetList.Clear();
					}
					//ASURA_GUID_UNREGISTERED shouldn't be added , but can be used by SetTarget to clear the list
					if(uGuid != ASURA_GUID_UNREGISTERED)
					{
						if(!m_xTargetList.Find(uGuid))
						{
							//make sure the proposed target exists
							Asura_Entity_Server* pxEntity = Asura_Entity_Server::FindEntity(uGuid);
							if(pxEntity)
							{
								//and is the right type
								if(pxEntity->GetClassification() == m_iTargetClass)
								{
									m_xTargetList.Push_Back(uGuid);
								}
								else
								{
									Asura_Assertf(0,("Target guid '%d' that is being added to proxy '%d' is of the wrong type",uGuid,GetGuid()));
								}
							}
						}
					}
				}

				return true;
			}

			default :
			{
				if(IsEntityActive())
				{
					//Any messages that aren't specific to proxy triggers get forwarded to the targets
					for(const Asura_ListElt<Asura_Guid>* pxIt = m_xTargetList.Front(); pxIt; pxIt = pxIt->Next())
					{
						ForwardMessage(pxMessage,pxIt->Data());
					}
				}
				return true;
			}
		}
	}
	return false;
}

void Asura_ServerEntity_ProxyTrigger::ForwardMessage(const Asura_Message* pxMessage,Asura_Guid uToGuid)
{

	#ifdef ASURA_TOOLS
	//log the message
	Tools_Entity_Simulation::IncreaseMessageLogIndent();
	Tools_Entity_Simulation::LogInGameMessage(pxMessage,uToGuid,GetGuid());
	#endif
	//and forward it
	Asura_Network::Forward(pxMessage,uToGuid,GetGuid());
	#ifdef ASURA_TOOLS
	Tools_Entity_Simulation::DecreaseMessageLogIndent();
	#endif

}

#ifdef ASURA_TOOLS
void Asura_ServerEntity_ProxyTrigger::ApplyChangesForTemplateInstance(Tools_EntityTemplateInstance* pxInstance)
{
	Asura_Entity_Server::ApplyChangesForTemplateInstance(pxInstance);

	//update the target guids
	for(Asura_ListElt<Asura_Guid>* pxIt = m_xTargetList.Front(); pxIt; pxIt = pxIt->Next())
	{
		Asura_Guid uGuid = pxInstance->ConvertGuid(pxIt->Data());
		pxIt->SetData(uGuid);
	}

}
#endif // ASURA_TOOLS


#ifdef ASURA_TOOLS
Asura_Guid Asura_ServerEntity_ProxyTrigger::GetGuidOfTargetClass() const
{
	//search through current targets for an existing entity
	for(const Asura_ListElt<Asura_Guid>* pxIt = m_xTargetList.Front(); pxIt; pxIt = pxIt->Next())
	{
		if(Asura_Entity_Server::FindEntity(pxIt->Data()))
		{
			return pxIt->Data();
		}
	}

	//Try for any entity of the correct class
	for(Asura_Classified_List_It<Asura_Entity_Server> xIt(Asura_Entity_Server::GetList());!xIt.Done();xIt.Next())
	{
		if(xIt.GetCurrent()->GetClassification() == m_iTargetClass)
		{
			return xIt.GetCurrent()->GetGuid();
		}
	}
    return ASURA_GUID_UNREGISTERED;
}

bool Asura_ServerEntity_ProxyTrigger::IsEntityTargeted( Asura_Guid uGuid ) const
{
	//search through current targets for an existing entity
	for(const Asura_ListElt<Asura_Guid>* pxIt = m_xTargetList.Front(); pxIt; pxIt = pxIt->Next())
	{
		if (pxIt->Data() == uGuid)
		{
			return true;
		}
	}
	return false;
}
#endif //ASURA_TOOLS

