//
// Asura_ServerEntity_Template.cpp
//
// Created by Richard Rice - 31/03/2004
// 
// Copyright (C) 2004 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"
#include "Entity/EntityTemplate/Asura_ServerEntity_Template.h"

#include "Entity/Asura_Entity_Classification.h"
#include "Entity/Asura_StaticMessage_System.h"
#include "Network/Asura_Message.h"

#ifdef ASURA_TOOLS
#include "Entity/Tools_Message_Editing.h"
#include "EntityTemplate/Tools_EntityTemplate_System.h"
#include "Load&Save/LevelExport/Tools_Level_Export.h"
#endif


#define ASURA_SERVERENTITY_TEMPLATE_VERSION_NUMBER 11
//Version 4 - Add lights
//Version 8 - Save out guids of children
//Version 9 - Add initially triggered inputs
//Version 10 - Replacement skin ID
//Version 11 - Bumped for change of behavoiur for replacement skin

Asura_Hashed_List<Asura_ServerEntity_Template> Asura_ServerEntity_Template::s_xList(5);


Asura_ServerEntity_Template::Asura_ServerEntity_Template(Asura_Guid uGuid)
#if defined(ASURA_TOOLS)
: Asura_Entity_Server(uGuid == ASURA_GUID_UNREGISTERED ? Asura_Entity_System::GetCreatedByToolsGuid() : uGuid)
#else
: Asura_Entity_Server(uGuid)
#endif	// defined(ASURA_TOOLS)
{
	s_xList.AddToList(this);
	#ifdef ASURA_TOOLS
	m_xPosition.Zero();
	m_xOrient.Identity();
	m_xName = "";
	m_iTemplateVersion = 0;

	m_iNumInputNames = 0;
	m_pxInputNames = 0;
	m_iNumOutputNames = 0;
	m_pxOutputNames = 0;
	m_uReplacementSkinID = 0;
	#endif

	m_bMaster = false;
	m_uFlags = 0;
	Classification = AsuraEntityClass_Template;
	SetNeverUpdateClient();	

	m_iNumInputs = 0;
	m_pxInputs = 0;

	m_iNumOutputs = 0;
	m_pxOutputs = 0;

}

void Asura_ServerEntity_Template::CreateFromChunkStream(Asura_Guid uGuid, Asura_Chunk_Stream& xStream)
{
	Asura_ServerEntity_Template* pxEntity = new Asura_ServerEntity_Template(uGuid);
	pxEntity->ReadFromChunkStream(xStream);
}

void Asura_ServerEntity_Template::ReadFromChunkStream(Asura_Chunk_Stream& xStream)
{

	int iVersion;
	xStream >> iVersion;
	Asura_Assert(iVersion<=ASURA_SERVERENTITY_TEMPLATE_VERSION_NUMBER,"Unknown chunk version for template");


	int iMaster;
	xStream >> iMaster;
	m_bMaster = iMaster ? true : false;


	xStream >> m_iNumInputs;
	m_pxInputs = m_iNumInputs ? new Asura_TemplateEntityMessage[m_iNumInputs] : 0;
	
	for(int i=0;i<m_iNumInputs;i++)
	{
		xStream >> m_pxInputs[i].m_uNameHash;
		xStream >> m_pxInputs[i].m_usMessageBlock;
		if(iVersion < 9) xStream.Skip(2);//older versions will have 2 bytes of padding
	}

	xStream >> m_iNumOutputs;
	m_pxOutputs = m_iNumOutputs ? new Asura_TemplateEntityMessage[m_iNumOutputs] : 0;
	
	for(int i=0;i<m_iNumOutputs;i++)
	{
		xStream >> m_pxOutputs[i].m_uNameHash;
		xStream >> m_pxOutputs[i].m_usMessageBlock;
		if(iVersion < 9) xStream.Skip(2);//older versions will have 2 bytes of padding
	}

	// load in guid list before tools data. The tools data might overwrite it.
	if (iVersion >= 8)
	{
		// TODO: use native chunk stream functions for Asura_HeapArray
		u_int uGuidListSize;
		xStream >> uGuidListSize;
		m_xChildGuids.SetSize(uGuidListSize);
		for (u_int u = 0; u < uGuidListSize; ++u)
		{
			xStream >> m_xChildGuids[u];
		}
	}

	if(iVersion >= 9)
	{
		xStream >> m_uFlags;
	}

	int iToolsDataSize;
	xStream >> iToolsDataSize;

	#ifdef ASURA_TOOLS
	if(iToolsDataSize)
	{
		xStream >> m_iTemplateVersion;
		xStream >> m_xPosition;
		xStream >> m_xOrient;
		xStream >> m_xName;

		m_xChildIDHandler.InitialiseFromChunkStream(xStream,iVersion);

		// ensure non-tools data is in sync.
		UpdateGuidListFromToolsData();

		xStream >> m_iNumInputNames;
		m_pxInputNames = m_iNumInputNames ? new Asura_String[m_iNumInputNames] : 0;

		for(int i=0;i<m_iNumInputNames;i++)
		{
			xStream >> m_pxInputNames[i];
		}

		xStream >> m_iNumOutputNames;
		m_pxOutputNames = m_iNumOutputNames ? new Asura_String[m_iNumOutputNames] : 0;

		for(int i=0;i<m_iNumOutputNames;i++)
		{
			xStream >> m_pxOutputNames[i];
		}

		if(iVersion >= 10)
		{
			xStream >> m_uReplacementSkinID;
			if(m_uReplacementSkinID && iVersion==10)
			{
				//Force earlier templates that are using replacement skins to update again
				FlagForUpdate();
			}
		}

	}
	else
	{
		//in tools , but no tools data.... this may be a problem.
		m_xName = "";
		m_iTemplateVersion = 0;
		m_xPosition.Zero();
		m_xOrient.Identity();

		m_iNumInputNames = 0;
		m_pxInputNames = 0;
		m_iNumOutputNames = 0;
		m_pxOutputNames = 0;
	}
	#else
	//not in tools , so just skip this
	xStream.Skip(iToolsDataSize);
	#endif

	//The contents of the chunk beyond this point is ignored by the Tools_EntityTemplateInfo loader

	if(iVersion >= 9)
	{
		u_int uNumInputsToTrigger;
		xStream >> uNumInputsToTrigger;
		m_xInitialInputsToTrigger.SetSize(uNumInputsToTrigger);
		for(u_int i=0; i<uNumInputsToTrigger; i++)
		{
			xStream >> m_xInitialInputsToTrigger[i];
		}
	}

	//read in parent class data if new enough. Note that this doesn't follow the normal
	//practice of parent class data being before this class' data to avoid changes to the
	//parent file from affecting the code in Tools_EntityTemplateInfo that skips over template data.
	if(iVersion >= 6)
	{
		Asura_Entity_Server::ReadFromChunkStream(xStream);
	}

	#ifndef ASURA_TOOLS
	//Flag as not updating if we aren't going to need to send messages during the first update.
	//Not doing this while in tools to avoid worrying about correcting this if the entity is edited.
	if(m_bSentInitialMessages || !m_xInitialInputsToTrigger.Size())
	{
		SetNeverUpdates(true);
	}
	#endif

}


Asura_ServerEntity_Template::~Asura_ServerEntity_Template()
{
	s_xList.RemoveFromList(this);

	delete [] m_pxInputs;
	delete [] m_pxOutputs;

	#ifdef ASURA_TOOLS
	delete [] m_pxInputNames;
	delete [] m_pxOutputNames;
	#endif // ASURA_TOOLS
}


#ifdef ASURA_TOOLS
int Asura_ServerEntity_Template::GetToolsDataSize() const
{
	int iChunkSize 	= sizeof(Asura_Vector_3)  //position
					+ sizeof(Asura_Matrix_3x3) //orientation
					+ m_xName.GetSizeInInts()*4//template name
					+ m_xChildIDHandler.GetChunkBufferSize()
					+ sizeof(int)*3 //template version number,num input names , num output names
					+ sizeof(Asura_Hash_ID); //replacement skin ID

	for(int i=0;i<m_iNumInputNames;i++)
	{
		iChunkSize += m_pxInputNames[i].GetSizeInInts()*4;
	}
	for(int i=0;i<m_iNumOutputNames;i++)
	{
		iChunkSize += m_pxOutputNames[i].GetSizeInInts()*4;
	}

					
	return iChunkSize;
}
#endif //ASURA_TOOLS


void Asura_ServerEntity_Template::WriteToChunkStream(Asura_Chunk_Stream& xStream) const
{

	/*
	This entity type can be used in template files , so any changes to FillInChunkBuffer may
	require ApplyChangesForTemplateInstance() to be updated as well.
	Please see the comment at the top of Tools/EntityTemplate/Tools_EntityTemplate_System.h before 
	making any changes.
	RAR 29/04/2004
	*/

	int iVersion = ASURA_SERVERENTITY_TEMPLATE_VERSION_NUMBER;
	xStream << iVersion;

	int iMaster = m_bMaster ? 1 : 0;
	xStream << iMaster;

	//inputs/outputs
	xStream << m_iNumInputs;
	for(int i=0;i<m_iNumInputs;i++)
	{
		xStream << m_pxInputs[i].m_uNameHash;
		xStream << m_pxInputs[i].m_usMessageBlock;
	}

	xStream << m_iNumOutputs;
	for(int i=0;i<m_iNumOutputs;i++)
	{
		xStream << m_pxOutputs[i].m_uNameHash;
		xStream << m_pxOutputs[i].m_usMessageBlock;
	}

	// TODO: use native chunk stream functions for Asura_HeapArray
	u_int uGuidListSize = m_xChildGuids.Size();
	xStream << uGuidListSize;
	for (u_int u = 0; u < uGuidListSize; ++u)
	{
		xStream << m_xChildGuids[u];
	}

	Asura_WriteToStream_Bitfield_Wrapper<u_int>	xFlagsBitfield (m_uFlags);
	xFlagsBitfield.Add(m_bSentInitialMessages, 1);
	xFlagsBitfield.Add(m_bMergeWithEnvironment, 1);
	xStream << xFlagsBitfield;

	#ifdef ASURA_TOOLS
	if(!Tools_Level_Export::IsExport())//exported levels won't want the tools data
	{
		//save out data only needed by tools
		int iToolsDataSize = GetToolsDataSize();
		xStream << iToolsDataSize;
			
		#ifdef ASURA_ASSERT
		u_int uToolsDataStartPos = xStream.GetPosition();
		#endif

		//Increment the version number whenever a template file is saved
		//(In practice this will probably mean that the version number goes up several times each
		//time the file gets edited , but this shouldn't really matter)
		if(m_bMaster) m_iTemplateVersion++;
		xStream << m_iTemplateVersion;

		xStream << m_xPosition;
		xStream << m_xOrient;
		xStream << m_xName;

		m_xChildIDHandler.WriteToChunkStream(xStream);

		xStream << m_iNumInputNames;
		for(int i=0;i<m_iNumInputNames;i++)
		{
			xStream << m_pxInputNames[i];
		}

		xStream << m_iNumOutputNames;
		for(int i=0;i<m_iNumOutputNames;i++)
		{
			xStream << m_pxOutputNames[i];
		}

		xStream << m_uReplacementSkinID;

		Asura_Assert(xStream.GetPosition()-uToolsDataStartPos == (u_int)iToolsDataSize, "Calculated size of tools data is incorrect");
	}
	else
	#endif
	{
		//Either not in tools , or doing a level export.
		//In either case , we don't want the tools data.
		int iToolsDataSize = 0;
		xStream << iToolsDataSize;
	}

	xStream << m_xInitialInputsToTrigger.Size();
	for(u_int i=0; i<m_xInitialInputsToTrigger.Size(); i++)
	{
		xStream << m_xInitialInputsToTrigger[i];
	}

	Asura_Entity_Server::WriteToChunkStream(xStream);

}


#ifdef ASURA_TOOLS
void Asura_ServerEntity_Template::UpdateGuidListFromToolsData()
{
	// update non-tools guid list from tools data
	m_xChildGuids.Clear();
	Tools_EntityTemplate_ChildIDArray* pxGuidArray = m_xChildIDHandler.GetArray(Tools_EntityTemplate_ChildType_Entity);
	if (pxGuidArray)
	{
		m_xChildGuids.SetSize(pxGuidArray->m_iNumIDs);
		for (int i = 0; i < pxGuidArray->m_iNumIDs; ++i)
		{
			m_xChildGuids[i] = pxGuidArray->m_puIDs[i];
		}
	}
}

// set each child's version of the template active flag.
void Asura_ServerEntity_Template::UpdateChildrenTemplateActiveFlag()
{
	for (u_int u = 0; u < m_xChildGuids.Size(); ++u)
	{
		Asura_Entity_Server* pxChild = Asura_Entity_Server::FindEntity( m_xChildGuids[u] );
		Asura_Assert(pxChild, "template instance lost a child entity");
		if (pxChild)
		{
			// using IsEntityActive() means we get the recursive effect we want for nested templates.
			pxChild->UpdateTemplateInstanceActive(IsEntityActive());
		}
	}
}
#endif // ASURA_TOOLS



// overrides of OnEntityActivate()/OnEntityDeactivate() - called when active status changes or is initialised. 
// Derived classes should always call parent versions if they override them. 
void Asura_ServerEntity_Template::OnEntityActivate(void)
{
	PARENT::OnEntityActivate();

	// tell each child that template has become active
	for (u_int u = 0; u < m_xChildGuids.Size(); ++u)
	{
		Asura_Entity_Server* pxChild = Asura_Entity_Server::FindEntity( m_xChildGuids[u] );
		// can't assert about this as a child may be destroyed by the template's inner workings.
		//Asura_Assert(pxChild, "template instance lost a child entity");
		if (pxChild)
		{
			pxChild->SetTemplateInstanceActive(true);
		}
	}
}

void Asura_ServerEntity_Template::OnEntityDeactivate(void)
{
	PARENT::OnEntityDeactivate();

	// tell each child that template has become inactive
	for (u_int u = 0; u < m_xChildGuids.Size(); ++u)
	{
		Asura_Entity_Server* pxChild = Asura_Entity_Server::FindEntity( m_xChildGuids[u] );
		// can't assert about this as a child may be destroyed by the template's inner workings.
		//Asura_Assert(pxChild, "template instance lost a child entity");
		if (pxChild)
		{
			pxChild->SetTemplateInstanceActive(false);
		}
	}
}


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

	switch(pxMessage->GetMsgID())
	{
		case ASURA_MSG_TEMPLATE_TRIGGERINPUT :
		{
			const Asura_Message_Template_Trigger* pxMessageData = reinterpret_cast
				<const Asura_Message_Template_Trigger*>(pxMessage->GetData());

			TriggerInput(pxMessageData->m_uNameHash);

			return true;
		}

		case ASURA_MSG_TEMPLATE_TRIGGEROUTPUT :
		{
			const Asura_Message_Template_Trigger* pxMessageData = reinterpret_cast
				<const Asura_Message_Template_Trigger*>(pxMessage->GetData());

			//search for a matching output
			for(int i=0;i<m_iNumOutputs;i++)
			{
				if(m_pxOutputs[i].m_uNameHash == pxMessageData->m_uNameHash)
				{
					//trigger this one
					Asura_StaticMessage_System::SendMessages(m_pxOutputs[i].m_usMessageBlock,Guid);
					break;
				}
			}

			return true;
		}
		
		case ASURA_MSG_DESTRUCT:
		{
			SetAsDestroyed();
			// also destroy all child entities
			for (u_int u = 0; u < m_xChildGuids.Size(); ++u)
			{
				Asura_Network::Forward(pxMessage, m_xChildGuids[u]);
			}
			return true;
		}

		default :
			break;
	}

	return PARENT::HandleMessage(pxMessage);
}


void Asura_ServerEntity_Template::Update()
{
	if(!m_bSentInitialMessages)
	{
		//Trigger the initial inputs
		for(u_int i=0; i<m_xInitialInputsToTrigger.Size(); i++)
		{
			TriggerInput(m_xInitialInputsToTrigger[i]);
		}
		m_bSentInitialMessages = true;
	}
	//Don't need to do any more updates after this
	SetNeverUpdates(true);
}


#ifdef ASURA_TOOLS
void Asura_ServerEntity_Template::ApplyChangesForTemplateInstance(Tools_EntityTemplateInstance* pxInstance)
{

	Asura_Entity_Server::ApplyChangesForTemplateInstance(pxInstance);

	m_xPosition = pxInstance->TransformPosition(m_xPosition);

	//RAR 10/03/2006 If just recentreing the entity ,then we want to ignore the rest of this :
	if(!pxInstance->OnlyUpdatesPosition())
	{
		m_xOrient = pxInstance->TransformMatrix(m_xOrient);
		m_iTemplateVersion = 0;//force immediate update for this template instance

		//Clear guid list , since the children won't be being imported at the same time , instead
		//they wil be created from this template's template file
		m_xChildIDHandler.Clear();



		//inputs will need to be setup when this template instance gets updated from its template
		//so delete any we have loaded already
		//(No need to delete the inputs from the static message system , since they haven't been
		//set up yet)
		m_iNumInputs = 0;
		delete [] m_pxInputs;
		m_pxInputs = 0;

		m_iNumInputNames = 0;
		delete [] m_pxInputNames;
		m_pxInputNames = 0;

		m_iNumOutputNames = 0;
		delete [] m_pxOutputNames;
		m_pxOutputNames = 0;

		//update outputs
		for(int i=0;i<m_iNumOutputs;i++)
		{
			m_pxOutputs[i].m_usMessageBlock = pxInstance->ConvertMessageBlock(m_pxOutputs[i].m_usMessageBlock);
		}
	}
}

void Asura_ServerEntity_Template::UpdateInputsAndOutputs(class Tools_EntityTemplateInstance* pxInstance,const struct Tools_EntityTemplateInfo* pxTemplate)
{
	//delete the old inputs
	for(int i=0;i<m_iNumInputs;i++)
	{
		Asura_StaticMessage_System::DeleteMessageBlock(m_pxInputs[i].m_usMessageBlock);
	}
	m_iNumInputs = 0;
	delete [] m_pxInputs;
	m_pxInputs = 0;

	//delete the old input/output names
	m_iNumInputNames = 0;
	delete [] m_pxInputNames;
	m_pxInputNames = 0;

	m_iNumOutputNames = 0;
	delete [] m_pxOutputNames;
	m_pxOutputNames = 0;



	//copy everything from the template info
	m_iNumInputs = pxTemplate->m_iNumInputs;
	m_pxInputs = m_iNumInputs ? new Asura_TemplateEntityMessage[m_iNumInputs] : 0;

	for(int i=0;i<m_iNumInputs;i++)
	{
		m_pxInputs[i] = pxTemplate->m_pxInputs[i];
		m_pxInputs[i].m_usMessageBlock = pxInstance->ConvertMessageBlock(m_pxInputs[i].m_usMessageBlock);
	}

	m_iNumInputNames = pxTemplate->m_iNumInputNames;
	m_pxInputNames = m_iNumInputNames ? new Asura_String[m_iNumInputNames] : 0;

	for(int i=0;i<m_iNumInputNames;i++)
	{
		m_pxInputNames[i] = pxTemplate->m_pxInputNames[i];
	}

	m_iNumOutputNames = pxTemplate->m_iNumOutputNames;
	m_pxOutputNames = m_iNumOutputNames ? new Asura_String[m_iNumOutputNames] : 0;

	for(int i=0;i<m_iNumOutputNames;i++)
	{
		m_pxOutputNames[i] = pxTemplate->m_pxOutputNames[i];
	}




}


const char* Asura_ServerEntity_Template::GetMessageDescription(const Asura_StaticMessage* pxMessage)
{
	if(pxMessage->m_usMsgID != ASURA_MSG_TEMPLATE_TRIGGERINPUT &&
		pxMessage->m_usMsgID != ASURA_MSG_TEMPLATE_TRIGGEROUTPUT) return 0;

	const Asura_Message_Template_Trigger* pxData = (const Asura_Message_Template_Trigger*) pxMessage->m_pData;


	//Find the entity being triggered
	Asura_ServerEntity_Template* pxEntity = FindEntity(Tools_Message_Editing::GetFinalDestinationGuid(pxMessage));
	if(!pxEntity) return 0;

	static char szText[100];

	if(pxMessage->m_usMsgID == ASURA_MSG_TEMPLATE_TRIGGERINPUT)
	{
		for(int i=0;i<pxEntity->m_iNumInputNames;i++)
		{
			if(pxData->m_uNameHash == Asura_GetHashID(pxEntity->m_pxInputNames[i].GetString()))
			{
				Asura_PrintfToBfr(szText,"Trigger Input (%s)",100,true,pxEntity->m_pxInputNames[i].GetString());
				return szText;
			}
		}
	}
	else
	{
		for(int i=0;i<pxEntity->m_iNumOutputNames;i++)
		{
			if(pxData->m_uNameHash == Asura_GetHashID(pxEntity->m_pxOutputNames[i].GetString()))
			{
				Asura_PrintfToBfr(szText,"Trigger Output (%s)",100,true,pxEntity->m_pxOutputNames[i].GetString());
				return szText;
			}
		}
	}
	return 0;
}


bool Asura_ServerEntity_Template::EntityBelongsToTemplate(Asura_Guid uGuid) const
{
	return m_xChildIDHandler.Contains(Tools_EntityTemplate_ChildType_Entity,uGuid);
}

bool Asura_ServerEntity_Template::AttractorBelongsToTemplate(u_int uID) const
{
	return m_xChildIDHandler.Contains(Tools_EntityTemplate_ChildType_Attractor,uID);
}


#endif // ASURA_TOOLS

#ifdef ASURA_TOOLS
int Asura_ServerEntity_Template::GetNumCustomEvents() const 
{ 
	if(IsMaster())
	{
		return m_iNumInputs;
	}
	else
	{
		return m_iNumOutputs; 
	}
}

unsigned short* Asura_ServerEntity_Template::GetCustomEvent(int iIndex) 
{ 
	if(IsMaster())
	{
		Asura_Assert(iIndex>=0 && iIndex < m_iNumInputs, "");
		return &m_pxInputs[iIndex].m_usMessageBlock;
	}
	else
	{
		Asura_Assert(iIndex>=0 && iIndex < m_iNumOutputs, "");
		return &m_pxOutputs[iIndex].m_usMessageBlock;
	}
}

void Asura_ServerEntity_Template::GetCustomEventName(int iEventIndex, Asura_String& xEventName)
{
	if(IsMaster())
	{
		Asura_Assert(iEventIndex>=0 && iEventIndex < m_iNumInputNames, "");
		xEventName = m_pxInputNames[iEventIndex];
	}
	else
	{
		//xEventName = m_pxOutputNames[iEventIndex];

		Asura_Hash_ID uNameHash = m_pxOutputs[iEventIndex].m_uNameHash;

		for(int i=0;i<m_iNumOutputNames;i++)
		{
			if(uNameHash == Asura_GetHashID(m_pxOutputNames[i].GetString()))
			{
				xEventName = m_pxOutputNames[i].GetString();
				return;
			}
		}

		xEventName = "*Unknown*";
	}
}
#endif //ASURA_TOOLS

void Asura_ServerEntity_Template::TriggerInput(Asura_Hash_ID uInput) const
{
	//search for a matching input
	for(int i=0;i<m_iNumInputs;i++)
	{
		if(m_pxInputs[i].m_uNameHash == uInput)
		{
			//trigger this one
			Asura_StaticMessage_System::SendMessages(m_pxInputs[i].m_usMessageBlock,Guid);
			break;
		}
	}
}


//////////////////////////////////////////////////////////////////////////////////////////
/*--------------------------**
** Tools_EntityTemplateInfo **
**--------------------------*/

#ifdef ASURA_TOOLS
Tools_EntityTemplateInfo::Tools_EntityTemplateInfo(const char* pcChunkBuffer)
{
	m_bValid = false;
	//zero everything
	m_iTemplateVersion = 0;
	m_xPosition.Zero();
	m_xOrient.Identity();


	m_iNumInputs = 0;
	m_pxInputs = 0;
	m_iNumOutputs = 0;
	m_pxOutputs = 0;

	m_iNumInputNames = 0;
	m_pxInputNames = 0;
	m_iNumOutputNames = 0;
	m_pxOutputNames = 0;

	Asura_Chunk_ChunkBuffer_Stream xStream(pcChunkBuffer);
	
	
	//process the chunk buffer
	int iVersion;
	xStream >> iVersion;
	Asura_Assert(iVersion<=ASURA_SERVERENTITY_TEMPLATE_VERSION_NUMBER,"Unknown chunk version for template");
	if(iVersion>ASURA_SERVERENTITY_TEMPLATE_VERSION_NUMBER) return;

	int iMaster;
	xStream >> iMaster;
	//m_bMaster = iMaster ? true : false;


	xStream >> m_iNumInputs;
	m_pxInputs = m_iNumInputs ? new Asura_TemplateEntityMessage[m_iNumInputs] : 0;
	
	for(int i=0;i<m_iNumInputs;i++)
	{
		xStream >> m_pxInputs[i].m_uNameHash;
		xStream >> m_pxInputs[i].m_usMessageBlock;
		if(iVersion < 9) xStream.Skip(2);//older versions will have 2 bytes of padding
	}

	xStream >> m_iNumOutputs;
	m_pxOutputs = m_iNumOutputs ? new Asura_TemplateEntityMessage[m_iNumOutputs] : 0;
	
	for(int i=0;i<m_iNumOutputs;i++)
	{
		xStream >> m_pxOutputs[i].m_uNameHash;
		xStream >> m_pxOutputs[i].m_usMessageBlock;
		if(iVersion < 9) xStream.Skip(2);//older versions will have 2 bytes of padding
	}

	//skip the non-tools guid list.
	if (iVersion >= 8)
	{
		u_int uGuidListSize;
		xStream >> uGuidListSize;
		xStream.Skip(uGuidListSize*sizeof(u_int));
	}

	//skip some flags
	if(iVersion >= 9)
	{
		xStream.Skip(sizeof(u_int));
	}

	int iToolsDataSize;
	xStream >> iToolsDataSize;

	if(iToolsDataSize)
	{
		xStream >> m_iTemplateVersion;		
		xStream >> m_xPosition;
		xStream >> m_xOrient;

		xStream >> m_xName;

		m_xChildIDHandler.InitialiseFromChunkStream(xStream,iVersion);
		

		xStream >> m_iNumInputNames;
		m_pxInputNames = m_iNumInputNames ? new Asura_String[m_iNumInputNames] : 0;

		for(int i=0;i<m_iNumInputNames;i++)
		{
			xStream >> m_pxInputNames[i];
		}

		xStream >> m_iNumOutputNames;
		m_pxOutputNames = m_iNumOutputNames ? new Asura_String[m_iNumOutputNames] : 0;

		for(int i=0;i<m_iNumOutputNames;i++)
		{
			xStream >> m_pxOutputNames[i];
		}

		m_bValid = true;
	}
	//The rest of the chunk is ignored , since it isn't required by this class

}

Tools_EntityTemplateInfo::~Tools_EntityTemplateInfo()
{
	delete [] m_pxInputs;
	delete [] m_pxOutputs;
	delete [] m_pxInputNames;
	delete [] m_pxOutputNames;
}


//////////////////////////////////////////////////////////////////////////////////////////
/*-----------------------------------**
** Tools_EntityTemplate_ChildIDArray **
**-----------------------------------*/

Tools_EntityTemplate_ChildIDArray::Tools_EntityTemplate_ChildIDArray(int iType)
{
	m_iNumIDs = 0;
	m_puIDs = 0;

	m_iType = iType;
}

Tools_EntityTemplate_ChildIDArray::~Tools_EntityTemplate_ChildIDArray()
{
	delete [] m_puIDs;
}

//////////////////////////////////////////////////////////////////////////////////////////
/*-------------------------------------**
** Tools_EntityTemplate_ChildIDHandler **
**-------------------------------------*/

Tools_EntityTemplate_ChildIDHandler::~Tools_EntityTemplate_ChildIDHandler()
{
	Clear();
}

void Tools_EntityTemplate_ChildIDHandler::Clear()
{
	while(m_xTypeList.size())
	{
		delete m_xTypeList.first_entry();
		m_xTypeList.delete_first_entry();
	}
}


const Tools_EntityTemplate_ChildIDArray* Tools_EntityTemplate_ChildIDHandler::GetArray(Tools_EntityTemplate_ChildType eType) const
{
	//search for an existing entry
	for(CLIF<Tools_EntityTemplate_ChildIDArray*> xTypeLIF(&m_xTypeList);!xTypeLIF.done();xTypeLIF.next())
	{
		if(xTypeLIF()->GetType() == eType) return xTypeLIF();
	}
	return 0;
}

Tools_EntityTemplate_ChildIDArray* Tools_EntityTemplate_ChildIDHandler::GetArray(Tools_EntityTemplate_ChildType eType,bool bCreate)
{
	//search for an existing entry
	for(LIF<Tools_EntityTemplate_ChildIDArray*> xTypeLIF(&m_xTypeList);!xTypeLIF.done();xTypeLIF.next())
	{
		if(xTypeLIF()->GetType() == eType) return xTypeLIF();
	}
	
	//create a new one if needed
	if(!bCreate) return 0;

	Tools_EntityTemplate_ChildIDArray* pxArray = new Tools_EntityTemplate_ChildIDArray(eType);
	m_xTypeList.add_entry(pxArray);

	return pxArray;
}

void Tools_EntityTemplate_ChildIDHandler::InitialiseFromChunkStream(Asura_Chunk_Stream& xStream,int iVersion)
{
	if(iVersion >= 7)
	{
		int iNumTypes;
		xStream >> iNumTypes;

		//read in each type
		for(int i=0;i<iNumTypes;i++)
		{
			int iType;
			xStream >> iType;

			Tools_EntityTemplate_ChildIDArray* pxArray = new Tools_EntityTemplate_ChildIDArray(iType);
			m_xTypeList.add_entry(pxArray);

			//read in the array
			xStream >> pxArray->m_iNumIDs;
			if(pxArray->m_iNumIDs)
			{
				pxArray->m_puIDs = new u_int[pxArray->m_iNumIDs];
				for(int i=0;i<pxArray->m_iNumIDs;i++)
				{
					xStream >> pxArray->m_puIDs[i];
				}
			}
		}
	}
	else
	{
		//old version with separate entity/sound/light and corona arrays

		//determine number of types saved according to version
		int iNumTypes = 1;
		if(iVersion>2) iNumTypes++;
		if(iVersion>3) iNumTypes++;
		if(iVersion>4) iNumTypes++;

		//conveniently the enumeration of types matches the order that they will appear in old files
		for(int iType = 0;iType<iNumTypes;iType++)
		{
			Tools_EntityTemplate_ChildIDArray* pxArray = new Tools_EntityTemplate_ChildIDArray(iType);
			m_xTypeList.add_entry(pxArray);

			//read in the array
			xStream >> pxArray->m_iNumIDs;
			if(pxArray->m_iNumIDs)
			{
				pxArray->m_puIDs = new u_int[pxArray->m_iNumIDs];
				for(int i=0;i<pxArray->m_iNumIDs;i++)
				{
					xStream >> pxArray->m_puIDs[i];
				}
			}
			
		}
	}
}

void Tools_EntityTemplate_ChildIDHandler::WriteToChunkStream(Asura_Chunk_Stream& xStream) const
{
	//determine the number of types to save (can skip the empty lists)
	int iNumTypes = 0;
	for(CLIF<Tools_EntityTemplate_ChildIDArray*> xTypeLIF(&m_xTypeList);!xTypeLIF.done();xTypeLIF.next())
	{
		if(xTypeLIF()->m_iNumIDs) iNumTypes++;
	}

	xStream << iNumTypes;

	//save out each of the type arrays
	for(CLIF<Tools_EntityTemplate_ChildIDArray*> xTypeLIF(&m_xTypeList);!xTypeLIF.done();xTypeLIF.next())
	{
		if(!xTypeLIF()->m_iNumIDs) continue;
		const Tools_EntityTemplate_ChildIDArray* pxArray = xTypeLIF();

		int iType = pxArray->GetType();
		xStream << iType;
		xStream << pxArray->m_iNumIDs;
		for(int i=0;i<pxArray->m_iNumIDs;i++)
		{
			xStream << pxArray->m_puIDs[i];
		}

	}
}


int Tools_EntityTemplate_ChildIDHandler::GetChunkBufferSize() const
{
	int iChunkBufferSize = sizeof(int);//for num types

	for(CLIF<Tools_EntityTemplate_ChildIDArray*> xTypeLIF(&m_xTypeList);!xTypeLIF.done();xTypeLIF.next())
	{
		if(!xTypeLIF()->m_iNumIDs) continue;
		const Tools_EntityTemplate_ChildIDArray* pxArray = xTypeLIF();

		iChunkBufferSize += sizeof(int)*2 + sizeof(u_int)*pxArray->m_iNumIDs;
	}

	return iChunkBufferSize;
}

bool Tools_EntityTemplate_ChildIDHandler::Contains(Tools_EntityTemplate_ChildType eType,u_int uID) const
{
	const Tools_EntityTemplate_ChildIDArray* pxArray = GetArray(eType);
	if(!pxArray) return false;

	for(int i=0;i<pxArray->m_iNumIDs;i++)
	{
		if(pxArray->m_puIDs[i] == uID) return true;
	}

	return false;
}

void Tools_EntityTemplate_ChildIDHandler::SetIDList(Tools_EntityTemplate_ChildType eType,const List<u_int>& xIDList)
{
	if(xIDList.size())
	{
		Tools_EntityTemplate_ChildIDArray* pxArray = GetArray(eType,true);

		if(xIDList.size() != pxArray->m_iNumIDs)
		{
			//allocate new array
			delete [] pxArray->m_puIDs;
			pxArray->m_iNumIDs = xIDList.size();
			pxArray->m_puIDs = new u_int[pxArray->m_iNumIDs];
		}
		
		//copy from the list
		int iIndex = 0;
		for(CLIF<u_int> xIDLIF(&xIDList);!xIDLIF.done();xIDLIF.next(),iIndex++)
		{
			pxArray->m_puIDs[iIndex] = xIDLIF();
		}
				
	}
	else
	{
		//delete existing entry if it exists
		Tools_EntityTemplate_ChildIDArray* pxArray = GetArray(eType);
		if(pxArray)
		{
			delete pxArray;
			m_xTypeList.delete_entry(pxArray);
		}
	}
}



#endif // ASURA_TOOLS
