//
// Tools_Entity_TimelineTrigger_Object.cpp
//
// Created by Richard Rice - 03/05/2005
// 
// Copyright (C) 2005 Rebellion, All Rights Reserved.
//

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

#include "Debugging/Asura_Assert.h"
#include "Entity/Asura_Entity_Classification.h"
#include "Entity/Asura_StaticMessage_System.h"
#include "Entity/Triggers/Asura_ServerEntity_TimelineTrigger.h"
#include "Interface/Asura_Dialog.h"
#include "Interface/Asura_Overlay.h"
#include "Mesh/Asura_Mesh_System.h"
#include "Rendering/Asura_Text_Output.h"

#include "Entity/Tools_Entity_Data.h"
#include "Entity/Tools_Message_Editing.h"
#include "Entity/Tools_Entity_Simulation.h"
#include "Objects/Tools_Object_System.h"
#include "Load&Save/Tools_SaveAsura.h"

#ifdef ASURA_TOOLS_USE_NEW_MESSAGE_EDITOR
#include "Entity/Tools_MessageEditor.h"
#endif


Tools_Entity_TimelineTrigger_Object::Tools_Entity_TimelineTrigger_Object(Asura_Entity* pxEntity)
:Tools_Entity_Activatable_Object(pxEntity)
{
	Asura_Assert(pxEntity->GetClassification()==AsuraEntityClass_TimelineTrigger,"");
	Type = TOT_Entity_TimelineTrigger;
	SetMesh("ToolsShape_TimelineTrigger");

	Asura_ServerEntity_TimelineTrigger* pxAsuraEntity = (Asura_ServerEntity_TimelineTrigger*) Entity;
	m_pbActive = new bool[pxAsuraEntity->m_uNumberOfMessageBlocks];
	m_pfTime = new float[pxAsuraEntity->m_uNumberOfMessageBlocks];
	for(unsigned i=0;i<pxAsuraEntity->m_uNumberOfMessageBlocks;i++)
	{
		m_pbActive[i] = pxAsuraEntity->m_pxMessageBlocks[i].m_bActive ? true : false;
		m_pfTime[i] = pxAsuraEntity->m_pxMessageBlocks[i].m_fTime;
	}
	m_bTimeChanged = false;

}

Tools_Entity_TimelineTrigger_Object::~Tools_Entity_TimelineTrigger_Object()
{
	delete [] m_pbActive;
	delete [] m_pfTime;
}

void Tools_Entity_TimelineTrigger_Object::AddOverlayControls(class Asura_Overlay* pxOverlay,class Asura_Vector_2& xOffset)
{
	Tools_Entity_Activatable_Object::AddOverlayControls(pxOverlay,xOffset);
	Asura_Dialog* pxDialog;

	Asura_ServerEntity_TimelineTrigger* pxAsuraEntity = (Asura_ServerEntity_TimelineTrigger*) Entity;

	pxDialog = new Asura_FlagTickbox_Dialog("Looped",xOffset,&pxAsuraEntity->m_uTimelineTriggerFlags,ASURA_TIMELINETRIGGERFLAG_LOOP,&Changed);
	pxDialog->SetHelpText("If looped then the timer will loop when the highest trigger time is reached.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	char szText[100];

	//add/delete messages
	pxDialog = new Asura_Function_Dialog("Add Message",xOffset,AddMessage,(int)this);
 	pxDialog->SetHelpText("Add a new message block to this trigger.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.x += 14.0f;

	pxDialog = new Asura_Function_Dialog("Delete Message",xOffset,DeleteMessage,(int)this);
 	pxDialog->SetHelpText("Deletes a message block from this trigger.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.x -= 14.0f;
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Rename Message",xOffset,RenameMessage,(int)this);
 	pxDialog->SetHelpText("Renames a message block on this trigger. This will also update any existing messages that are targeting the message block.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::LargeSpacing();



#if 1
	m_bTimeChanged = false;

	if(pxAsuraEntity->m_uNumberOfMessageBlocks)
	{
		//do some headings
		xOffset.x += 0.5f;
		pxDialog = new Asura_Dialog("Sel.",xOffset);
		pxOverlay->AddDialog(pxDialog);
		xOffset.x += 8.0f;

		pxDialog = new Asura_Dialog("Timer",xOffset);
		pxOverlay->AddDialog(pxDialog);
		xOffset.x += 7.5f;

		pxDialog = new Asura_Dialog("Enabled",xOffset);
		pxOverlay->AddDialog(pxDialog);

		xOffset.x = 1.0f;
		xOffset.y += Asura_Dialog::DefaultSpacing();
	}


	Asura_TimelineTrigger_MessageBlock* pxMessageBlock = pxAsuraEntity->m_pxMessageBlocks;
	for(unsigned i=0;i<pxAsuraEntity->m_uNumberOfMessageBlocks;i++,pxMessageBlock++)
	{
		pxDialog = new Asura_Tickbox_Dialog("",xOffset,&pxMessageBlock->m_bSelected);
	 	pxDialog->SetHelpText("Indicates whether this timer is selected.\nWhenever a timer is adjusted (in tools) , all selected timers will be altered by the same amount.\nThis has no affect outside of tools.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.x = pxDialog->m_xMaxCoord.x;

		pxDialog = new Asura_Numerical_Dialog("",xOffset,&m_pfTime[i],0.0f,9999.0f,1.0f,"%.3f",&m_bTimeChanged);
		pxDialog->SetHelpText("The time at which these messages will be sent.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.x = pxDialog->m_xMaxCoord.x;

		pxDialog = new Asura_Tickbox_Dialog("",xOffset,&m_pbActive[i],&Changed);
	 	pxDialog->SetHelpText("Indicates whether this message block is initially enabled.\nIf disabled at the point the trigger time is reached , then the messages won't be sent.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.x = pxDialog->m_xMaxCoord.x;

 		Asura_PrintfToBfr(szText,"%s (%d)",100,true,pxMessageBlock->m_xName.GetString(),Asura_StaticMessage_System::GetNumberOfMessagesInBlock(pxMessageBlock->m_usMessageBlock));
		pxDialog = new Asura_Function_Dialog(szText,xOffset,EditMessages,(int)i);
		pxDialog->SetHelpText("Edit the messages to be sent for this time.");
		pxOverlay->AddDialog(pxDialog);

		xOffset.x = 1.0f;
		xOffset.y += Asura_Dialog::LargeSpacing();

	}

	pxDialog = new Asura_Function_Dialog("Deselect All",xOffset,DeselectAll,(int)this);
	pxDialog->SetHelpText("Deselects all the timers");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

#endif
	
}


void Tools_Entity_TimelineTrigger_Object::AddOverlayControls_EnableMessage(struct Asura_StaticMessage* pxMessage,class Asura_Overlay* pxOverlay,class Asura_Vector_2& xOffset)
{
	Asura_Assert(pxMessage->m_pData,"");
	Asura_Message_TimelineTrigger* pxData = (Asura_Message_TimelineTrigger*) pxMessage->m_pData;

	Asura_Dialog* pxDialog = 0;

	//find the entity
	Asura_ServerEntity_TimelineTrigger* pxEntity = Asura_ServerEntity_TimelineTrigger::FindEntity(Tools_Message_Editing::GetFinalDestinationGuid(pxMessage));
	if(!pxEntity)
	{
		pxDialog = new Asura_Dialog("*Unknown Target*",xOffset);	
		pxOverlay->AddDialog(pxDialog);
		xOffset.y += Asura_Dialog::DefaultSpacing();
		return;
	}


	Asura_Assert(pxEntity->ToolsData && pxEntity->ToolsData->ToolsObject,"");
	Tools_Entity_TimelineTrigger_Object* pxObject = (Tools_Entity_TimelineTrigger_Object*) pxEntity->ToolsData->ToolsObject;

	//get the name
	const char* pszName;
	char szText[100];
	if(pxData->m_uMessageHash)
	{
		pszName = pxObject->GetMessageBlockNameFromHash(pxData->m_uMessageHash);
	}
	else
	{
		pszName = "*Not Set*";
	}

	Asura_PrintfToBfr(szText,"Message Block : %s",100,true,pszName);
	pxDialog = new Asura_Function_Dialog(szText,xOffset,EditEnableMessage,(int)pxMessage);
	pxDialog->SetHelpText("Select message block to enable/disable");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();
}

void Tools_Entity_TimelineTrigger_Object::AddOverlayControls_SetTriggerTime(struct Asura_StaticMessage* pxMessage,class Asura_Overlay* pxOverlay,class Asura_Vector_2& xOffset)
{
	Asura_Assert(pxMessage->m_pData,"");
	Asura_Message_TimelineTrigger_SetTriggerTime* pxData = (Asura_Message_TimelineTrigger_SetTriggerTime*) pxMessage->m_pData;

	Asura_Dialog* pxDialog = 0;

	//find the entity
	Asura_ServerEntity_TimelineTrigger* pxEntity = Asura_ServerEntity_TimelineTrigger::FindEntity(Tools_Message_Editing::GetFinalDestinationGuid(pxMessage));
	if(!pxEntity)
	{
		pxDialog = new Asura_Dialog("*Unknown Target*",xOffset);	
		pxOverlay->AddDialog(pxDialog);
		xOffset.y += Asura_Dialog::DefaultSpacing();
		return;
	}


	Asura_Assert(pxEntity->ToolsData && pxEntity->ToolsData->ToolsObject,"");
	Tools_Entity_TimelineTrigger_Object* pxObject = (Tools_Entity_TimelineTrigger_Object*) pxEntity->ToolsData->ToolsObject;

	//get the name
	const char* pszName;
	char szText[100];
	if(pxData->m_uMessageHash)
	{
		pszName = pxObject->GetMessageBlockNameFromHash(pxData->m_uMessageHash);
	}
	else
	{
		pszName = "*Not Set*";
	}

	Asura_PrintfToBfr(szText,"Message Block : %s",100,true,pszName);
	pxDialog = new Asura_Function_Dialog(szText,xOffset,EditEnableMessage,(int)pxMessage);
	pxDialog->SetHelpText("Select message block to change");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Numerical_Dialog("Trigger Time",xOffset,&pxData->m_fTriggerTime,0.0f,9999.0f,1.0f,"%.3f");
	pxDialog->SetHelpText("The new trigger time");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();
}




void Tools_Entity_TimelineTrigger_Object::Create()
{
	Tools_Object* pxObject = Create(false);
	Tools_Object_System::SelectObject(pxObject);
}

Tools_Entity_TimelineTrigger_Object* Tools_Entity_TimelineTrigger_Object::Create(bool bGenerateName)
{
	//get a name , and the position data
	Asura_Guid uGuid = Asura_Entity_System::GetNextToolsCreatedGuid();
	Tools_Entity_Data* pxToolsData = 0;
	if(bGenerateName)
	{
		pxToolsData = Tools_Entity_Object::CreateToolsDataWithDefaultName("Timeline Trigger","Timeline Trigger",uGuid,GetNamePrefix());
	}
	else
	{
		pxToolsData = Tools_Entity_Object::CreateToolsData("Timeline Trigger",0,GetNamePrefix());
	}
	if(!pxToolsData) return 0 ;

	//create the asura data
	Asura_ServerEntity_TimelineTrigger* pxAsuraEntity = new Asura_ServerEntity_TimelineTrigger();
	pxAsuraEntity->ToolsData = pxToolsData;

	//create the tools pxObject
	Tools_Entity_TimelineTrigger_Object* pxObject = new Tools_Entity_TimelineTrigger_Object(pxAsuraEntity);

	Tools_SaveAsura::SetEntityModified();

	return pxObject;
}



void Tools_Entity_TimelineTrigger_Object::Destroy()
{
	Asura_ServerEntity_TimelineTrigger* pxAsuraEntity = (Asura_ServerEntity_TimelineTrigger*) Entity;
	//clear out any message blocks associated with this

	for(u_int i=0;i<pxAsuraEntity->m_uNumberOfMessageBlocks;i++)
	{
		Asura_StaticMessage_System::DeleteMessageBlock(pxAsuraEntity->m_pxMessageBlocks[i].m_usMessageBlock);
		pxAsuraEntity->m_pxMessageBlocks[i].m_usMessageBlock = ASURA_STATICMESSAGE_EMPTYBLOCK;
	}

	Tools_Entity_Activatable_Object::Destroy();
}

void Tools_Entity_TimelineTrigger_Object::UpdateWhileSelected()
{
	Asura_ServerEntity_TimelineTrigger* pxAsuraEntity = (Asura_ServerEntity_TimelineTrigger*) Entity;
		
	if(Changed)
	{
		//update the asura data
		for(unsigned i=0;i<pxAsuraEntity->m_uNumberOfMessageBlocks;i++)
		{
	   		pxAsuraEntity->m_pxMessageBlocks[i].m_bActive = m_pbActive[i];
		}
	}

	if(m_bTimeChanged)
	{
		//determine which time changed
		u_int uChangedIndex = 0xffffffff;
		float fDelta = 0;
		for(u_int i=0;i<pxAsuraEntity->m_uNumberOfMessageBlocks;i++)
		{
			if(m_pfTime[i] != pxAsuraEntity->m_pxMessageBlocks[i].m_fTime)
			{
				uChangedIndex = i;
				fDelta = m_pfTime[i] - pxAsuraEntity->m_pxMessageBlocks[i].m_fTime;
				break;
			}
		}

		//update the times as needed
		if(uChangedIndex != 0xffffffff)
		{
			if(fDelta<0)
			{
				//clamp delta so that no selected timer goes negative
				for(u_int i=0;i<pxAsuraEntity->m_uNumberOfMessageBlocks;i++)
				{
					if(pxAsuraEntity->m_pxMessageBlocks[i].m_bSelected)
					{
						//apply the time change
						if(-fDelta>pxAsuraEntity->m_pxMessageBlocks[i].m_fTime)
						{
							fDelta = -pxAsuraEntity->m_pxMessageBlocks[i].m_fTime;
						}
					}

				}

			}
			
			for(u_int i=0;i<pxAsuraEntity->m_uNumberOfMessageBlocks;i++)
			{
				if(pxAsuraEntity->m_pxMessageBlocks[i].m_bSelected || i==uChangedIndex)
				{
					pxAsuraEntity->m_pxMessageBlocks[i].m_fTime += fDelta;
					m_pfTime[i] = pxAsuraEntity->m_pxMessageBlocks[i].m_fTime;
				}

			}
		}
		pxAsuraEntity->InvalidateMaxTriggerTime();
		
		Changed = true;
	}

	Tools_Entity_Activatable_Object::UpdateWhileSelected();
}

bool Tools_Entity_TimelineTrigger_Object::HandlesThisMessage(int iMessageID)
{
	if(iMessageID == ASURA_MSG_TIMELINETRIGGER_ENABLEMESSAGE ||
	   iMessageID == ASURA_MSG_TIMELINETRIGGER_DISABLEMESSAGE ||
	   iMessageID == ASURA_MSG_ACTIVATE ||
	   iMessageID == ASURA_MSG_DEACTIVATE ||
	   iMessageID == ASURA_MSG_DESTRUCT ||
	   iMessageID == ASURA_MSG_RESET ||
	   iMessageID == ASURA_MSG_PAUSETIMER ||
	   iMessageID == ASURA_MSG_UNPAUSETIMER ||
	   iMessageID == ASURA_MSG_SETTIMER ||
	   iMessageID == ASURA_MSG_TIMELINETRIGGER_SETTRIGGERTIME)
	{
		return true;
	}
	return false; 
}

u_short* Tools_Entity_TimelineTrigger_Object::AddTimelineTriggerMessageBlock(const char* szName /* = NULL */)
{
	Tools_Entity_TimelineTrigger_Object* pxToolsEntity = (Tools_Entity_TimelineTrigger_Object*) this;
	Asura_ServerEntity_TimelineTrigger* pxAsuraEntity = (Asura_ServerEntity_TimelineTrigger*) Entity;

	Asura_String xName;

	if(szName == NULL)
	{
		//get a name for the new message block
		GetString_Overlay xOverlay("Enter name for the new message block","");
		if(!xOverlay.DoModal()) return NULL;

		xName = xOverlay.GetString();
	}
	else
	{
		xName = szName;
	}

	if(xName.IsBlank()) return NULL;

	//make sure this name is okay
	Asura_Hash_ID uHash = xName.GetHash();
	for(u_int i=0;i<pxAsuraEntity->m_uNumberOfMessageBlocks;i++)
	{
		if(pxAsuraEntity->m_pxMessageBlocks[i].m_uHashID == uHash)
		{
			ShowMessageOverlay("A message block with that name already exists.");
			return NULL;
		}
	}
	
	
	//allocate an enlarged array of message blocks , and copy over the old values
	Asura_TimelineTrigger_MessageBlock* pxMessageBlocks = new Asura_TimelineTrigger_MessageBlock[pxAsuraEntity->m_uNumberOfMessageBlocks+1];
	for(unsigned i=0;i<pxAsuraEntity->m_uNumberOfMessageBlocks;i++)
	{
		pxMessageBlocks[i] = pxAsuraEntity->m_pxMessageBlocks[i];
	}

	//add the new input
	int iIndex = pxAsuraEntity->m_uNumberOfMessageBlocks;
	pxMessageBlocks[iIndex].SetDefaultValues();
	pxMessageBlocks[iIndex].m_xName = xName;
	pxMessageBlocks[iIndex].m_uHashID = uHash;

	delete [] pxAsuraEntity->m_pxMessageBlocks;
	pxAsuraEntity->m_pxMessageBlocks = pxMessageBlocks;
	pxAsuraEntity->m_uNumberOfMessageBlocks++;


	//update active input array
	delete [] pxToolsEntity->m_pbActive;
	delete [] pxToolsEntity->m_pfTime;
	pxToolsEntity->m_pbActive = new bool[pxAsuraEntity->m_uNumberOfMessageBlocks];
	pxToolsEntity->m_pfTime = new float[pxAsuraEntity->m_uNumberOfMessageBlocks];

	for(unsigned i=0;i<pxAsuraEntity->m_uNumberOfMessageBlocks;i++)
	{
		pxToolsEntity->m_pbActive[i] = pxAsuraEntity->m_pxMessageBlocks[i].m_bActive ? true : false;
		pxToolsEntity->m_pfTime[i] = pxAsuraEntity->m_pxMessageBlocks[i].m_fTime;
	}

	return &pxMessageBlocks[iIndex].m_usMessageBlock;
}

void Tools_Entity_TimelineTrigger_Object::AddMessage()
{
	Asura_Assert(ThisEntity,"");

	AddTimelineTriggerMessageBlock();

	#ifdef ASURA_TOOLS_USE_NEW_MESSAGE_EDITOR
	Tools_MessageEditor::UpdateMessageEditorTable();
	#endif
	
	//update the overlay
	ThisEntity->DeleteOverlay();
	ThisEntity->CreateOverlay();
}

void Tools_Entity_TimelineTrigger_Object::DeleteMessage()
{
	DeleteMessage((int) this, -1);
}

void Tools_Entity_TimelineTrigger_Object::DeleteMessage(int iThis, int iIndex)
{
	Asura_ServerEntity_TimelineTrigger* pxAsuraEntity = (Asura_ServerEntity_TimelineTrigger*) Entity;

	if(iIndex == -1)
	{
		//ask which input to delete
		iIndex = SelectMessageIndex("Delete which message block?");
		if(iIndex<0) return;
	}

	//delete the message block from static messages system
	Asura_StaticMessage_System::DeleteMessageBlock(pxAsuraEntity->m_pxMessageBlocks[iIndex].m_usMessageBlock);

	
	//shuffle down the remaining inputs
	for(u_int i=iIndex;i<pxAsuraEntity->m_uNumberOfMessageBlocks-1;i++)
	{
		pxAsuraEntity->m_pxMessageBlocks[i] = pxAsuraEntity->m_pxMessageBlocks[i+1];
	}

	pxAsuraEntity->m_uNumberOfMessageBlocks--;

	#ifdef ASURA_TOOLS_USE_NEW_MESSAGE_EDITOR
	Tools_MessageEditor::UpdateMessageEditorTable();
	#endif

	//overlay needs updating
	DeleteOverlay();
	CreateOverlay();

	SetEntityModified();
}

void Tools_Entity_TimelineTrigger_Object::RenameMessage()
{
	RenameMessage((int) this, -1);
}

void Tools_Entity_TimelineTrigger_Object::RenameMessage(int iThis, int iIndex)
{
	Asura_ServerEntity_TimelineTrigger* pxAsuraEntity = (Asura_ServerEntity_TimelineTrigger*) Entity;

	if(iIndex == -1)
	{
		//ask which input to rename
		iIndex = SelectMessageIndex("Rename which message block?");
		if(iIndex<0) return;
	}
	
	Asura_TimelineTrigger_MessageBlock* pxMessageBlock = &pxAsuraEntity->m_pxMessageBlocks[iIndex];
	Asura_Hash_ID uOldHash = pxMessageBlock->m_uHashID;

	//ask for the new input name
	GetString_Overlay xOverlay("Enter new name for message blockt",pxMessageBlock->m_xName.GetString());
	if(!xOverlay.DoModal()) return;

	const char* szNewName = xOverlay.GetString();
	if(!szNewName) return;

	//make sure the name doesn't clash with any other message blocks
	Asura_Hash_ID uNewHash = Asura_GetHashID(szNewName);

	for(u_int i=0;i<pxAsuraEntity->m_uNumberOfMessageBlocks;i++)
	{
		if(i==(u_int)iIndex) continue;
		
		if(pxAsuraEntity->m_pxMessageBlocks[i].m_uHashID == uNewHash)
		{
			ShowMessageOverlay("A message block with that name already exists.");
			return;
		}
	}
	
	//update the message block with the new name
	pxMessageBlock->m_xName = szNewName;
	pxMessageBlock->m_uHashID = uNewHash;



	//update any static messages that were targeting the message block
	for(Tools_StaticMessageIterator xMessageIt;!xMessageIt.Done();xMessageIt.Next())
	{
		Asura_StaticMessage* pxMessage = xMessageIt.GetCurrent();
		//message targets this?
		if(pxMessage->m_uSendTo != pxAsuraEntity->GetGuid()) continue;
		//is it a timeline trigger messageblock  affecting message?
		if(pxMessage->m_usMsgID == ASURA_MSG_TIMELINETRIGGER_ENABLEMESSAGE ||
			pxMessage->m_usMsgID == ASURA_MSG_TIMELINETRIGGER_DISABLEMESSAGE)
		{
			Asura_Message_TimelineTrigger* pxData = (Asura_Message_TimelineTrigger*) pxMessage->m_pData;

			//update the target , and set as modified
			if(pxData->m_uMessageHash == uOldHash)
			{
				pxData->m_uMessageHash = uNewHash;
				xMessageIt.SetModified();
			}
		}
		else if(pxMessage->m_usMsgID == ASURA_MSG_TIMELINETRIGGER_SETTRIGGERTIME)
		{
			Asura_Message_TimelineTrigger_SetTriggerTime* pxData = (Asura_Message_TimelineTrigger_SetTriggerTime*) pxMessage->m_pData;

			//update the target , and set as modified
			if(pxData->m_uMessageHash == uOldHash)
			{
				pxData->m_uMessageHash = uNewHash;
				xMessageIt.SetModified();
			}
		}
	}

	//overlay needs updating
	DeleteOverlay();
	CreateOverlay();

	#ifdef ASURA_TOOLS_USE_NEW_MESSAGE_EDITOR
	Tools_MessageEditor::UpdateMessageEditorTable();
	#endif

	SetEntityModified();
}



bool Tools_Entity_TimelineTrigger_Object::OwnsThisMessageBlock(unsigned short usBlockID)
{
	Asura_ServerEntity_TimelineTrigger* pxAsuraEntity = (Asura_ServerEntity_TimelineTrigger*) Entity;

	for(u_int i=0;i<pxAsuraEntity->m_uNumberOfMessageBlocks;i++)
	{
		if(pxAsuraEntity->m_pxMessageBlocks[i].m_usMessageBlock == usBlockID) return true;
	}
	
	return Tools_Entity_Activatable_Object::OwnsThisMessageBlock(usBlockID);
}

bool Tools_Entity_TimelineTrigger_Object::GetEventDescForMessageBlock(unsigned short usBlockID,Asura_String& xOutputDesc)
{
	Asura_ServerEntity_TimelineTrigger* pxAsuraEntity = (Asura_ServerEntity_TimelineTrigger*) Entity;

	for(u_int i=0;i<pxAsuraEntity->m_uNumberOfMessageBlocks;i++)
	{
		if(pxAsuraEntity->m_pxMessageBlocks[i].m_usMessageBlock == usBlockID)
		{
			char szTemp[100];
			Asura_PrintfToBfr(szTemp,"Timer '%s'",sizeof(szTemp),true,pxAsuraEntity->m_pxMessageBlocks[i].m_xName.GetString());
			xOutputDesc = szTemp;
			return true;
		}
	}
	
	return false;
}


Tools_Object* Tools_Entity_TimelineTrigger_Object::CreateObjectOfThisType() const
{
	return Create(true);	
}

void Tools_Entity_TimelineTrigger_Object::Copy(const Tools_Object* pxObject)
{
	Tools_Entity_Activatable_Object::Copy(pxObject);

	const Tools_Entity_TimelineTrigger_Object* pxFromObject = (const Tools_Entity_TimelineTrigger_Object*) pxObject;

	Asura_ServerEntity_TimelineTrigger* pxFromEntity = (Asura_ServerEntity_TimelineTrigger*) pxFromObject->Entity;
	Asura_ServerEntity_TimelineTrigger* pxToEntity = (Asura_ServerEntity_TimelineTrigger*) Entity;

	//destroy the old messages
	for(u_int i=0;i<pxToEntity->m_uNumberOfMessageBlocks;i++)
	{
		Asura_StaticMessage_System::DeleteMessageBlock(pxToEntity->m_pxMessageBlocks[i].m_usMessageBlock);
	}
	
	delete [] pxToEntity->m_pxMessageBlocks;
	pxToEntity->m_pxMessageBlocks = 0;
	delete [] m_pbActive;
	m_pbActive = 0;
	delete [] m_pfTime;
	m_pfTime = 0;
	
	
	//copy between the entities
	pxToEntity->m_uNumberOfMessageBlocks = pxFromEntity->m_uNumberOfMessageBlocks;


	if(pxToEntity->m_uNumberOfMessageBlocks)
	{
		pxToEntity->m_pxMessageBlocks = new Asura_TimelineTrigger_MessageBlock[pxToEntity->m_uNumberOfMessageBlocks];
		for(u_int i=0;i<pxToEntity->m_uNumberOfMessageBlocks;i++)
		{
			pxToEntity->m_pxMessageBlocks[i] = pxFromEntity->m_pxMessageBlocks[i];
			pxToEntity->m_pxMessageBlocks[i].m_usMessageBlock = Asura_StaticMessage_System::CopyMessageBlock(pxToEntity->m_pxMessageBlocks[i].m_usMessageBlock,pxFromObject->GetGuid(),GetGuid());
		}
	}
	pxToEntity->InvalidateMaxTriggerTime();

	//copy between the tools objects
	m_pbActive = new bool[pxToEntity->m_uNumberOfMessageBlocks];
	m_pfTime = new float[pxToEntity->m_uNumberOfMessageBlocks];

	for(u_int i=0;i<pxToEntity->m_uNumberOfMessageBlocks;i++)
	{
		m_pbActive[i] = pxFromObject->m_pbActive[i];
		m_pfTime[i] = pxFromObject->m_pfTime[i];
	}

}


int Tools_Entity_TimelineTrigger_Object::SelectMessageIndex(const char* pszMessage)
{
	Asura_ServerEntity_TimelineTrigger* pxAsuraEntity = (Asura_ServerEntity_TimelineTrigger*) Entity;

	if(!pxAsuraEntity->m_uNumberOfMessageBlocks)
	{
		ShowMessageOverlay("This trigger doesn't have any message blocks");
		return -1;
	}

	ListSelection_Overlay xOverlay(pszMessage);
	//add the names of all the messages
	for(u_int i=0;i<pxAsuraEntity->m_uNumberOfMessageBlocks;i++)
	{
		xOverlay.AddItem(pxAsuraEntity->m_pxMessageBlocks[i].m_xName.GetString(),i);
	}

	if(!xOverlay.DoModal()) return -1;

	return xOverlay.GetSelectionData();
	
}

Asura_Hash_ID Tools_Entity_TimelineTrigger_Object::SelectMessageHash(const char* pszMessage)
{
	Asura_ServerEntity_TimelineTrigger* pxAsuraEntity = (Asura_ServerEntity_TimelineTrigger*) Entity;

	int iIndex = SelectMessageIndex(pszMessage);
	if(iIndex<0) return ASURA_HASH_ID_UNSET;

	return pxAsuraEntity->m_pxMessageBlocks[iIndex].m_uHashID;
}



const char* Tools_Entity_TimelineTrigger_Object::GetMessageBlockNameFromHash(Asura_Hash_ID uNameHash) const
{
	const Asura_ServerEntity_TimelineTrigger* pxAsuraEntity = (const Asura_ServerEntity_TimelineTrigger*) Entity;

	for(u_int i=0;i<pxAsuraEntity->m_uNumberOfMessageBlocks;i++)
	{
		if(uNameHash == pxAsuraEntity->m_pxMessageBlocks[i].m_uHashID)
		{
			return pxAsuraEntity->m_pxMessageBlocks[i].m_xName.GetString();
		}
	}

	return "*Unknown*";
}



void Tools_Entity_TimelineTrigger_Object::PrintInfo(float& x,float& y)
{
	Tools_Entity_Activatable_Object::PrintInfo(x,y);
	
	if(Tools_Entity_Simulation::IsActive())
	{
		Asura_ServerEntity_TimelineTrigger* pxAsuraEntity = (Asura_ServerEntity_TimelineTrigger*) Entity;
	
		for(unsigned i=0;i<pxAsuraEntity->m_uNumberOfMessageBlocks;i++)
		{
			Asura_Text_Output::Print(x,y++,0xffffff00,0,"%s : %s",pxAsuraEntity->m_pxMessageBlocks[i].m_xName.GetString(),pxAsuraEntity->m_pxMessageBlocks[i].m_bActive ? "Enabled" : "Disabled");
		}
		Asura_Text_Output::Print(x,y++,0xffffff00,0,"Timer : %.3f",pxAsuraEntity->m_fTimer);
	}

}


void Tools_Entity_TimelineTrigger_Object::EditMessages(int iIndex)
{
	Asura_Assert(ThisEntity,"");
	Tools_Entity_TimelineTrigger_Object* pxObject = (Tools_Entity_TimelineTrigger_Object*)ThisEntity;
	Asura_ServerEntity_TimelineTrigger* pxAsuraEntity = (Asura_ServerEntity_TimelineTrigger*) ThisEntity->Entity;
	Asura_Assert(iIndex>=0 && iIndex<(int)pxAsuraEntity->m_uNumberOfMessageBlocks,"");

	//sort the messages now , since they will be sorted when the object gets deselected
	Asura_Hash_ID uHash = pxAsuraEntity->m_pxMessageBlocks[iIndex].m_uHashID;
	pxObject->SortMessages();

	//find the index again
	for(u_int i=0;i<pxAsuraEntity->m_uNumberOfMessageBlocks;i++)
	{
		if(uHash == pxAsuraEntity->m_pxMessageBlocks[i].m_uHashID)
		{
			iIndex = i;
			break;
		}
	}

	#ifdef ASURA_TOOLS_USE_NEW_MESSAGE_EDITOR
	Tools_MessageEditor::StartMessageEditingMode(ThisEntity->m_pxServerEntity->GetCustomEvent(iIndex));
	#else
	Tools_Object_System::EditMessageBlock(pxAsuraEntity->m_pxMessageBlocks[iIndex].m_xName.GetString(),&pxAsuraEntity->m_pxMessageBlocks[iIndex].m_usMessageBlock);
	#endif
	
}

static int TimelineTriggerSort(const void* p1,const void* p2)
{
	const Asura_TimelineTrigger_MessageBlock* pxBlock1 = (const Asura_TimelineTrigger_MessageBlock*) p1;
	const Asura_TimelineTrigger_MessageBlock* pxBlock2 = (const Asura_TimelineTrigger_MessageBlock*) p2;

	if(pxBlock1->m_fTime == pxBlock2->m_fTime)
	{
		//sort alphabetically if time is the same
		return strcmp(pxBlock1->m_xName.GetString(),pxBlock2->m_xName.GetString());
	}
	return (pxBlock1->m_fTime < pxBlock2->m_fTime) ? -1 : 1;
}

void Tools_Entity_TimelineTrigger_Object::SortMessages()
{
	Asura_ServerEntity_TimelineTrigger* pxAsuraEntity = (Asura_ServerEntity_TimelineTrigger*) Entity;
	if(pxAsuraEntity->m_uNumberOfMessageBlocks<2) return;

	qsort(pxAsuraEntity->m_pxMessageBlocks,pxAsuraEntity->m_uNumberOfMessageBlocks,sizeof(Asura_TimelineTrigger_MessageBlock),TimelineTriggerSort);

	//update the active array
	for(u_int i=0;i<pxAsuraEntity->m_uNumberOfMessageBlocks;i++)
	{
		m_pbActive[i] = pxAsuraEntity->m_pxMessageBlocks[i].m_bActive;
		m_pfTime[i] = pxAsuraEntity->m_pxMessageBlocks[i].m_fTime;
	}

	#ifdef ASURA_TOOLS_USE_NEW_MESSAGE_EDITOR
	Tools_MessageEditor::UpdateMessageEditorTable();
	#endif
}


void Tools_Entity_TimelineTrigger_Object::OnDeselection()
{
	SortMessages();
	Tools_Entity_Activatable_Object::OnDeselection();
}

void Tools_Entity_TimelineTrigger_Object::EditEnableMessage(int iMessage)
{
	EditEnableMessage((Asura_StaticMessage*)iMessage);
	Tools_Message_Editing::UpdateMessageOverlay();
}

void Tools_Entity_TimelineTrigger_Object::EditEnableMessage(Asura_StaticMessage* pxMessage)
{
	Asura_Assert(pxMessage->m_pData,"");
	Asura_Message_TimelineTrigger* pxData = (Asura_Message_TimelineTrigger*) pxMessage->m_pData;

	//find the entity and tools object for this
	Asura_ServerEntity_TimelineTrigger* pxEntity = Asura_ServerEntity_TimelineTrigger::FindEntity(Tools_Message_Editing::GetFinalDestinationGuid(pxMessage));
	if(!pxEntity) return;
	if(!pxEntity->ToolsData) return;
	if(!pxEntity->ToolsData->ToolsObject) return;

	Tools_Entity_TimelineTrigger_Object* pxObject = (Tools_Entity_TimelineTrigger_Object*) pxEntity->ToolsData->ToolsObject;

	const char* szTitle = 0;
	switch(pxMessage->m_usMsgID)
	{
		case ASURA_MSG_TIMELINETRIGGER_ENABLEMESSAGE :
			szTitle = "Select message block to enable.";
			break;
		case ASURA_MSG_TIMELINETRIGGER_DISABLEMESSAGE :
			szTitle = "Select message block to disable.";
			break;
		case ASURA_MSG_TIMELINETRIGGER_SETTRIGGERTIME :
			szTitle = "Select message block to alter.";
			break;
	}

	//ask for the message to enable
	Asura_Hash_ID uHash = pxObject->SelectMessageHash(szTitle);
	if(uHash)
	{
		pxData->m_uMessageHash = uHash;
	}
}

void Tools_Entity_TimelineTrigger_Object::DeselectAll()
{
	Asura_ServerEntity_TimelineTrigger* pxAsuraEntity = (Asura_ServerEntity_TimelineTrigger*) Entity;	

	for(u_int i=0;i<pxAsuraEntity->m_uNumberOfMessageBlocks;i++)
	{
		pxAsuraEntity->m_pxMessageBlocks[i].m_bSelected = false;
	}
}
