//
// Tools_SoundEvent_Overlay.cpp
//
// Created by Richard Rice - 05/07/2006
// 
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"
#include "Sound/Tools_SoundEvent_Overlay.h"

#include "Camera/Asura_Camera.h"
#include "Core/Asura_NameTree.h"
#include "Interface/Asura_Overlay_System.h"
#include "Interface/Asura_Dialog.h"
#include "Rendering/Asura_Viewport.h"
#include "Rendering/Asura_Text_Output.h"
#include "Sound/Asura_Sound_Event_Storage.h"
#include "Sound/Asura_Sound_Event.h"
#include "Sound/Asura_Sound_Event_System.h"

#include "Tools_Overlays.h"
#include "Load&Save/Tools_ModeDataFile.h"
#include "Sound/Tools_PickSoundEvent_Overlay.h"
#include "Sound/Tools_PickSoundSample_Overlay.h"
#include "Sound/Tools_SoundConversionPreview.h"
#include "Sound/Tools_SoundEventTab_Event.h"
#include "Sound/Tools_SoundEventTab_Sample.h"
#include "Sound/Tools_SoundEvent_System.h"



//////////////////////////////////////////////////////////////////////////////////////////////////////
/*--------------------------**
** Tools_SoundEvent_Overlay **
**--------------------------*/

#define CALLBACK_VOID(__Function) Asura_Callback_MemberFunction<void,Tools_SoundEvent_Overlay>(this, &Tools_SoundEvent_Overlay::__Function) 
#define CALLBACK_BOOL(__Function) Asura_Callback_MemberFunction<bool,Tools_SoundEvent_Overlay>(this, &Tools_SoundEvent_Overlay::__Function) 


Asura_Vector_2 Tools_SoundEvent_Overlay::s_axOverlayPosition[2] =
{
	Asura_Vector_2(10.0f,10.0f),
	Asura_Vector_2(10.0f,10.0f)
};
Tools_SoundEvent_Overlay* Tools_SoundEvent_Overlay::s_apxThis[2];
bool Tools_SoundEvent_Overlay::s_bCombineEventAndSampleOverlay = true;

Asura_Handle Tools_SoundEvent_Overlay::s_iEventSoundHandle = ASURA_INVALID_HANDLE;
Tools_SoundSamplePreview Tools_SoundEvent_Overlay::s_xSoundSamplePreview;


Tools_SoundEvent_Overlay::Tools_SoundEvent_Overlay(int iIndex)
:Asura_Overlay("Sound Event Editing",false,&s_axOverlayPosition[iIndex])
{
	Asura_Assert(!s_apxThis[iIndex],"Should only be one instance of the sound event overlay");
	m_iOverlayIndex = iIndex;
	s_apxThis[iIndex] = this;
	m_pxTabsDialog = 0;

}

Tools_SoundEvent_Overlay::~Tools_SoundEvent_Overlay()
{
	Asura_Assert(s_apxThis[m_iOverlayIndex] == this,"");
	s_apxThis[m_iOverlayIndex] = 0;


	while(m_xTabList.size())
	{
		delete m_xTabList.first_entry();
		m_xTabList.delete_first_entry();
	}

	if(!s_apxThis[0] && !s_apxThis[1])
	{
		//stop sound if all overlays are closed
		StopSound();
	}
}

void Tools_SoundEvent_Overlay::TransferModeData(int mode)
{
	//open the file
	Tools_ModeDataFile file("SoundEvent.dat",mode);

	//tranfer the data
	//(none of these should be removed , and new data should go on the end, otherwise
	//the files will get screwed up)
	if( false == file.GetError())
	{
		file.Transfer(s_axOverlayPosition[0]);
		file.Transfer(s_axOverlayPosition[1]);
		file.Transfer(s_bCombineEventAndSampleOverlay);
	}
}


bool Tools_SoundEvent_Overlay::EditEvent(Asura_Hash_ID uEventHash)
{
	//make sure the event exists
	if(!Asura_Sound_Event_Storage::GetEventByHash(uEventHash))
	{
		return false;
	}

	GetEventOverlay(true)->EditEventInternal(uEventHash);	

	return true;
}

void Tools_SoundEvent_Overlay::EditEventInternal(Asura_Hash_ID uEventHash)
{
	//is there already a tab for this event?
	for(int i=0;i<m_xTabList.size();i++)
	{
		if(m_xTabList[i]->IsEvent() && m_xTabList[i]->GetHashID() == uEventHash)
		{
			//select the existing tab
			m_pxTabsDialog->SelectTab(i);
			Asura_Overlay_System::BringOverlayToTop(m_uID);
			return;
		}
	}

	u_int uInitialTab = m_pxTabsDialog->GetSelected();

	//Create a new tab for the event. It will be selected automatically
	Tools_SoundEventTab_Event* pxTab = new Tools_SoundEventTab_Event(m_pxTabsDialog,uEventHash);
	m_xTabList.add_entry(pxTab);

	CloseTabsIfTooLarge(uInitialTab,m_pxTabsDialog->GetSelected());


	ResizeOverlay();
	Asura_Overlay_System::BringOverlayToTop(m_uID);


}

bool Tools_SoundEvent_Overlay::EditSample(Asura_Hash_ID uSampleHash)
{
	//make sure the event exists
	if(!Asura_Sound_Event_Storage::GetSampleByHash(uSampleHash))
	{
		return false;
	}

	GetSampleOverlay(true)->EditSampleInternal(uSampleHash);	

	return true;
}

void Tools_SoundEvent_Overlay::EditSampleInternal(Asura_Hash_ID uSampleHash)
{
	//is there already a tab for this event?
	for(int i=0;i<m_xTabList.size();i++)
	{
		if(m_xTabList[i]->IsSample() && m_xTabList[i]->GetHashID() == uSampleHash)
		{
			//select the existing tab
			m_pxTabsDialog->SelectTab(i);
			Asura_Overlay_System::BringOverlayToTop(m_uID);
			return;
		}
	}

	u_int uInitialTab = m_pxTabsDialog->GetSelected();

	//Create a new tab for the sample. It will be selected automatically
	Tools_SoundEventTab_Sample* pxTab = new Tools_SoundEventTab_Sample(m_pxTabsDialog,uSampleHash);
	m_xTabList.add_entry(pxTab);

	CloseTabsIfTooLarge(uInitialTab,m_pxTabsDialog->GetSelected());

	ResizeOverlay();
	Asura_Overlay_System::BringOverlayToTop(m_uID);


}

bool Tools_SoundEvent_Overlay::CloseEventTab(Asura_Hash_ID uEventHash)
{
	Tools_SoundEvent_Overlay* pxOverlay = GetEventOverlay(false);
	if(!pxOverlay) return true;

	
	//search for a tab for this event
	for(int i=0;i<pxOverlay->m_xTabList.size();i++)
	{
		Tools_SoundEventTab* pxTab = pxOverlay->m_xTabList[i];

		if(pxTab->IsEvent() && pxTab->GetHashID() == uEventHash)
		{
			if(pxTab->CanBeClosed())
			{
				pxOverlay->CloseTabMain(i);
				return true;
			}
			else
			{
				return false;
			}
		}
	}
	return true;
}

bool Tools_SoundEvent_Overlay::CloseSampleTab(Asura_Hash_ID uSampleHash)
{
	Tools_SoundEvent_Overlay* pxOverlay = GetSampleOverlay(false);
	if(!pxOverlay) return true;

	
	//search for a tab for this sample
	for(int i=0;i<pxOverlay->m_xTabList.size();i++)
	{
		Tools_SoundEventTab* pxTab = pxOverlay->m_xTabList[i];

		if(pxTab->IsSample() && pxTab->GetHashID() == uSampleHash)
		{
			if(pxTab->CanBeClosed())
			{
				pxOverlay->CloseTabMain(i);
				return true;
			}
			else
			{
				return false;
			}
		}
	}
	return true;
}

void Tools_SoundEvent_Overlay::HandleReload()
{
	//get each tab on each overlay to update itself
	for(int i=0 ;i<2 ; i++)
	{
		Tools_SoundEvent_Overlay* pxOverlay = s_apxThis[i];
		if(!pxOverlay) continue;

		int iNumTabs = pxOverlay->m_xTabList.size();
		for(int j=0; j<iNumTabs; j++)
		{
			Tools_SoundEventTab* pxTab = pxOverlay->m_xTabList[j];
			if(!pxTab->HandleReload())
			{
				//delete the tab
				pxOverlay->CloseTabMain(j);
				j--;
				iNumTabs--;

				if(!s_apxThis[i]) break;//in case the overlay was deleted
			}
		}
	}
}


void Tools_SoundEvent_Overlay::ShowOverlay()
{
	int iMaxTabWidth = Asura_Maths::FloatToInt((Asura_Viewport::GetWidth()/ASURA_TEXT_OUTPUT_FONT_WIDTH)-15.0f);

	Asura_Vector_2 xOffset(1.0f,3.0f);
	m_pxTabsDialog = new Asura_Tabs_Dialog(xOffset,0,0,iMaxTabWidth);
	AddDialog(m_pxTabsDialog);

	//put close tab to the top right
	Asura_Dialog* pxDialog = new Asura_Function_Dialog("Close Tab",xOffset,CloseTab,(int)this);
	pxDialog->SetHelpText("Finish editing the current event or sample");
	AddDialog(pxDialog);
	pxDialog->SetOverlayRelativePosition(Asura_Vector_2(pxDialog->m_xOffset.x - pxDialog->m_xMaxCoord.x - 2.0f,xOffset.y - Asura_Dialog::DefaultSpacing()),false,true);

	xOffset.y += Asura_Dialog::DefaultSpacing()*2;

	pxDialog = new Asura_Function_Dialog("Delete",xOffset,Delete,(int)this);
	pxDialog->SetHelpText("Delete this sample/event");
	AddDialog(pxDialog);
	pxDialog->SetRelativePosition(m_pxTabsDialog,true,false);

	xOffset.x = pxDialog->m_xMaxCoord.x + 1.0f;
	pxDialog = new Asura_Function_Dialog("Copy",xOffset,Copy,(int)this);
	pxDialog->SetHelpText("Copy this sample/event to the paste buffer");
	AddDialog(pxDialog);
	pxDialog->SetRelativePosition(m_pxTabsDialog,true,false);

	xOffset.x = pxDialog->m_xMaxCoord.x + 1.0f;
	pxDialog = new Asura_Function_Dialog("Paste",xOffset,Paste,(int)this);
	pxDialog->SetHelpText("Paste the copied sample/event over this one");
	AddDialog(pxDialog);
	pxDialog->SetRelativePosition(m_pxTabsDialog,true,false);

	xOffset.x = pxDialog->m_xMaxCoord.x + 1.0f;
	pxDialog = new Asura_Function_Dialog("Paste Multiple",xOffset,PasteMultiple,(int)this);
	pxDialog->SetHelpText("Paste some or all of the properties from this to one or more other events/samples");
	AddDialog(pxDialog);
	pxDialog->SetRelativePosition(m_pxTabsDialog,true,false);


	xOffset.x = pxDialog->m_xMaxCoord.x + 1.0f;
	pxDialog = new Asura_Function_Dialog("Duplicate",xOffset,Duplicate,(int)this);
	pxDialog->SetIsEnabledFunction(CALLBACK_BOOL(IsSoundEventTab));
	pxDialog->SetHelpText("Creates a copy of this event.");
	AddDialog(pxDialog);
	pxDialog->SetRelativePosition(m_pxTabsDialog,true,false);

	xOffset.x = pxDialog->m_xMaxCoord.x + 1.0f;
	pxDialog = new Asura_Function_Dialog("Rename",xOffset,Rename,(int)this);
	pxDialog->SetHelpText("Renames this sample/event.");
	AddDialog(pxDialog);
	pxDialog->SetRelativePosition(m_pxTabsDialog,true,false);

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


	//xOffset.x = pxDialog->MaxCoord.x + 1.0f;
	pxDialog = new Asura_Function_Dialog("New Code Event",xOffset,CreateCodeEvent,(int)this);
	pxDialog->SetHelpText("Create a new sound event picking a name from the list of events that have been declared in code.");
	AddDialog(pxDialog);
	pxDialog->SetRelativePosition(m_pxTabsDialog,true,false);

	xOffset.x = pxDialog->m_xMaxCoord.x + 1.0f;
	pxDialog = new Asura_Function_Dialog("New Event",xOffset,CreateEvent,(int)this);
	pxDialog->SetHelpText("Create a new sound event");
	AddDialog(pxDialog);
	pxDialog->SetRelativePosition(m_pxTabsDialog,true,false);


	xOffset.x = pxDialog->m_xMaxCoord.x + 1.0f;
	pxDialog = new Asura_Function_Dialog("New Sample",xOffset,CreateSample,(int)this);
	pxDialog->SetHelpText("Create a new sound sample");
	AddDialog(pxDialog);
	pxDialog->SetRelativePosition(m_pxTabsDialog,true,false);


	m_uID = TOOLS_OVERLAY_SOUNDEVENT1 + m_iOverlayIndex;
	Asura_Overlay_System::SubmitNewOverlay(this);
}

void Tools_SoundEvent_Overlay::CloseOverlay()
{
	Asura_Overlay_System::CloseOverlays(TOOLS_OVERLAY_SOUNDEVENT1);
	Asura_Overlay_System::CloseOverlays(TOOLS_OVERLAY_SOUNDEVENT2);
}

void Tools_SoundEvent_Overlay::CloseTab()
{
	CloseTabMain(m_pxTabsDialog->GetSelected());
}

void Tools_SoundEvent_Overlay::CloseTabMain(u_int uTab)
{
	Asura_Assert(uTab<(u_int)m_xTabList.size(),"");

	//remove tab from the overlay
	m_pxTabsDialog->DeleteTab(uTab);

	//and delete entry from the list
	delete m_xTabList[uTab];
	m_xTabList.delete_entry(m_xTabList[uTab]);

	//If there are no tabs left , then close the overlay
	if(!m_xTabList.size())
	{
		Asura_Overlay_System::CloseOverlays(m_uID);
	}
}

void Tools_SoundEvent_Overlay::Delete()
{
	u_int uTab = m_pxTabsDialog->GetSelected();
	m_xTabList[uTab]->Delete();
}

void Tools_SoundEvent_Overlay::Copy()
{
	u_int uTab = m_pxTabsDialog->GetSelected();
	m_xTabList[uTab]->Copy();
}

void Tools_SoundEvent_Overlay::Paste()
{
	u_int uTab = m_pxTabsDialog->GetSelected();
	m_xTabList[uTab]->Paste();
}

void Tools_SoundEvent_Overlay::PasteMultiple()
{
	u_int uTab = m_pxTabsDialog->GetSelected();
	m_xTabList[uTab]->PasteMultiple();
}


void Tools_SoundEvent_Overlay::Duplicate()
{
	u_int uTab = m_pxTabsDialog->GetSelected();
	m_xTabList[uTab]->Duplicate();
}

void Tools_SoundEvent_Overlay::Rename()
{
	u_int uTab = m_pxTabsDialog->GetSelected();
	m_xTabList[uTab]->Rename();
	//update the tab name after renaming
	m_pxTabsDialog->RenameTab(uTab,m_xTabList[uTab]->GetTabName());
}


void Tools_SoundEvent_Overlay::CloseTabsIfTooLarge(u_int uTabToPreserve1,u_int uTabToPreserve2)
{
	#if 1 
	//float fMaxWidth = (Asura_Viewport::GetWidth()/ASURA_TEXT_OUTPUT_FONT_WIDTH)-10.0f;

	if(m_xTabList.size() > 25)
	{
		//find a tab that can safely be removed
		for(u_int uTabToDelete=0; uTabToDelete<(u_int)m_xTabList.size();uTabToDelete++)
		{
			if(uTabToDelete == uTabToPreserve1) continue;			
			if(uTabToDelete == uTabToPreserve2) continue;
			if(!m_xTabList[uTabToDelete]->CanBeClosed()) continue;

			
			m_pxTabsDialog->DeleteTab(uTabToDelete);

			//and delete entry from the list
			delete m_xTabList[uTabToDelete];
			m_xTabList.delete_entry(m_xTabList[uTabToDelete]);

			break;
		}
	}
	#endif
}

void Tools_SoundEvent_Overlay::PerFrameUpdate()
{
	Asura_Overlay::PerFrameUpdate();

	for(LIF<Tools_SoundEventTab*> xTabLIF(&m_xTabList);!xTabLIF.done();xTabLIF.next())
	{
		xTabLIF()->PerFrameUpdate();
	}

	//Update test sound.
	//This may end up being called more than once per frame , but it doesn't really matter
	UpdateSound();

	//Make sure that the event/sample tabs are on the correct overlays
	u_int uTabIndex = 0;
	for(LIF<Tools_SoundEventTab*> xTabLIF(&m_xTabList);!xTabLIF.done();)
	{
		bool bSample = xTabLIF()->IsSample();
		//events go on overlay 0 , and samples on 0 or 1 depending on the combine overlay setting
		int iDesiredOverlay = (bSample && !s_bCombineEventAndSampleOverlay) ? 1 : 0;

		if(iDesiredOverlay != m_iOverlayIndex)
		{
			Asura_Hash_ID uHash = xTabLIF()->GetHashID();	
			//on wrong overlay. delete and edit again
			m_pxTabsDialog->DeleteTab(uTabIndex);
			delete xTabLIF();
			xTabLIF.delete_current();

			if(bSample)
			{
				EditSample(uHash);
			}
			else
			{
				EditEvent(uHash);
			}
		}
		else
		{
			xTabLIF.next();
			uTabIndex++;
		}
	}


	//set the overlay title based on the current selection
	if(m_xTabList.size())
	{
		Tools_SoundEventTab* pxSelectedTab = m_xTabList[m_pxTabsDialog->GetSelected()];
		
		char szTitle[100];

		const char* szPrefix = 0;
		if(pxSelectedTab->IsSample())
		{
			szPrefix = "Sample";
		}
		else if(Tools_SoundEvent_System::IsCodeEvent(pxSelectedTab->GetHashID()))
		{
			szPrefix = "Code Event";
		}
		else
		{
			szPrefix = "Event";
		}

		Asura_PrintfToBfr(szTitle,"%s - %s",sizeof(szTitle),true,szPrefix,pxSelectedTab->GetName());

		SetTitle(szTitle);
	}
	else
	{
		Asura_Overlay_System::CloseOverlays(m_uID);
	}
}

void Tools_SoundEvent_Overlay::PlaySoundEvent(Asura_Hash_ID uEventHash)
{
	StopSound();

	const Asura_Sound_Event* pxEvent = Asura_Sound_Event_Storage::GetEventByHash(uEventHash);
	if(!pxEvent) return;

	Asura_Vector_3 xPos = *Asura_Camera::GetPosition();

	Asura_Sound_Event_System::PlayEvent(pxEvent,&s_iEventSoundHandle,&xPos);
}

void Tools_SoundEvent_Overlay::PlaySoundSample(Asura_Hash_ID uSampleHash)
{
	StopSound();

	s_xSoundSamplePreview.Play(uSampleHash);
}


void Tools_SoundEvent_Overlay::StopSound()
{

	Asura_Sound_Event_System::Stop(s_iEventSoundHandle);	
	s_xSoundSamplePreview.Stop();
	Tools_SoundConversionPreview::StopSound();
}

void Tools_SoundEvent_Overlay::UpdateSound()
{
	s_xSoundSamplePreview.Update();
}


void Tools_SoundEvent_Overlay::UpdateEventFromAsuraData(Asura_Hash_ID uEventHash)
{
	Tools_SoundEvent_Overlay* pxOverlay = GetEventOverlay(false);
	if(!pxOverlay) return;

	//Is there a tab for this event?
	for(int i=0;i<pxOverlay->m_xTabList.size();i++)
	{
		if(pxOverlay->m_xTabList[i]->IsEvent() && pxOverlay->m_xTabList[i]->GetHashID() == uEventHash)
		{
			pxOverlay->m_xTabList[i]->UpdateFromAsuraData();
			return;
		}
	}					  
}

void Tools_SoundEvent_Overlay::UpdateSampleFromAsuraData(Asura_Hash_ID uSampleHash)
{
	Tools_SoundEvent_Overlay* pxOverlay = GetSampleOverlay(false);
	if(!pxOverlay) return;

	//Is there a tab for this sample?
	for(int i=0;i<pxOverlay->m_xTabList.size();i++)
	{
		if(pxOverlay->m_xTabList[i]->IsSample() && pxOverlay->m_xTabList[i]->GetHashID() == uSampleHash)
		{
			pxOverlay->m_xTabList[i]->UpdateFromAsuraData();
			return;
		}
	}					  
}


Tools_SoundEvent_Overlay* Tools_SoundEvent_Overlay::GetEventOverlay(bool bCreate)
{
	int iIndex = 0;
	if(!s_apxThis[iIndex] && bCreate)
	{
		new Tools_SoundEvent_Overlay(iIndex);
		s_apxThis[iIndex]->ShowOverlay();
	}
	return s_apxThis[iIndex];
}

Tools_SoundEvent_Overlay* Tools_SoundEvent_Overlay::GetSampleOverlay(bool bCreate)
{
	int iIndex = s_bCombineEventAndSampleOverlay ? 0 : 1;
	if(!s_apxThis[iIndex] && bCreate)
	{
		new Tools_SoundEvent_Overlay(iIndex);
		s_apxThis[iIndex]->ShowOverlay();
	}
	return s_apxThis[iIndex];
}

void Tools_SoundEvent_Overlay::CreateEvent()
{
	Tools_SoundEvent_System::CreateEvent(0,0,0,0);
}

void Tools_SoundEvent_Overlay::CreateCodeEvent()
{
	Tools_SoundEvent_System::CreateCodeEvent();
}

void Tools_SoundEvent_Overlay::CreateSample()
{
	Tools_SoundEvent_System::CreateSample(0,0,0,0);
}

bool Tools_SoundEvent_Overlay::IsSoundEventTab()
{
	u_int uTab = m_pxTabsDialog->GetSelected();
	return m_xTabList[uTab]->IsEvent();
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*----------------------------**
** Tools_SoundCategory_Dialog **
**----------------------------*/

int Tools_SoundCategory_Dialog::s_iListValidID;

Tools_SoundCategory_Dialog::Tools_SoundCategory_Dialog(const Asura_Vector_2& xOffset,Asura_String* pxCategory,bool* pbModified)
:Asura_DropDown_List_Dialog(xOffset)
{
	m_bInitialised = false;
	m_pszCategoryList = 0;
	m_uCategoryListSize = 0;
	m_pxCategoryNameTree = 0;

	m_pbModified = pbModified;
	m_pxCategory = pxCategory;
	Asura_Assert(m_pxCategory,"");

	m_bSelectionChanged = false;
	m_xCategoryBackup = *m_pxCategory;


	UpdateCategoryList();
	Initialise(xOffset,m_pszCategoryList,&m_iSelectedIndex,10,m_uCategoryListSize,NULL,NULL,"Category");
	m_bInitialised = true;

	SetOnSelectCallback(OnSelectionChanged,(int) this);

}

Tools_SoundCategory_Dialog::~Tools_SoundCategory_Dialog()
{
	DeleteCategoryList();
}


void Tools_SoundCategory_Dialog::UpdateCategoryList()
{
	DeleteCategoryList();

	m_pxCategoryNameTree = new Asura_NameTree;

	//make sure the category being edited isn't blank
	if(m_pxCategory->IsBlank())
	{
		*m_pxCategory = "Default";
		m_xCategoryBackup = *m_pxCategory;
	}
	//and add it to the tree
	int iJunkIndex;
	m_pxCategoryNameTree->FindNodeIndexByName(m_pxCategory->GetString(),true,iJunkIndex);


	//add the default categories
	for(int i=0;i<Tools_SoundEvent_System::GetNumDefaultCategories();i++)
	{
		const char* szCategory = Tools_SoundEvent_System::GetDefaultCategory(i);
 		m_pxCategoryNameTree->FindNodeIndexByName(szCategory,true,iJunkIndex);
 	}

	//add the category names from all the sounds and events
	for(u_int i=0;i<Asura_Sound_Event_Storage::GetNumberOfEvents();i++)
	{
		const Asura_Sound_Event* pxEvent = Asura_Sound_Event_Storage::GetEventByIndex(i);
		const char* szCategory = pxEvent->GetEventType();

		if(szCategory && *szCategory)
		{
			m_pxCategoryNameTree->FindNodeIndexByName(szCategory,true,iJunkIndex);
		}
	}

	for(u_int i=0;i<Asura_Sound_Event_Storage::GetNumberOfSamples();i++)
	{
		const Asura_Sound_Sample* pxSample = Asura_Sound_Event_Storage::GetSampleByIndex(i);
		const char* szCategory = pxSample->GetSoundType();

		if(szCategory && *szCategory)
		{
			m_pxCategoryNameTree->FindNodeIndexByName(szCategory,true,iJunkIndex);
		}
	}



	//Fill array with these strings , adding an entry for new category at the beginning
	m_uCategoryListSize = m_pxCategoryNameTree->GetNumberOfNodes()+1;
	m_pszCategoryList = new const char*[m_uCategoryListSize];
	m_pszCategoryList[0] = "*New*";
	m_pxCategoryNameTree->MakeSortedNameList(m_pszCategoryList+1);

	//find the index of the current string in the sorted list
	m_iSelectedIndex = 0;
	for(u_int i=1;i<m_uCategoryListSize;i++)
	{
		if(!m_pxCategory->Compare(m_pszCategoryList[i]))
		{
			m_iSelectedIndex = (int) i;
			break;
		}
	}
	Asura_Assert(m_iSelectedIndex,"Failed to find category in list");

	if(m_bInitialised)
	{
		ReplaceStrings(m_uCategoryListSize,m_pszCategoryList);
	}

	m_iListValidID = s_iListValidID;
}

void Tools_SoundCategory_Dialog::DeleteCategoryList()
{
	delete [] m_pszCategoryList;
	m_pszCategoryList = 0;
	m_uCategoryListSize = 0;
	delete m_pxCategoryNameTree;
	m_pxCategoryNameTree = 0;
}

void Tools_SoundCategory_Dialog::OnSelectionChanged()
{
	if(m_iSelectedIndex)
	{
		//an existing category has been chosen
		*m_pxCategory = m_pszCategoryList[m_iSelectedIndex];
		if(m_pbModified) *m_pbModified = true;
	}
	else
	{
		//The new category option has been chosen.
		//Ask for a new name
		GetString_Overlay xOverlay("New Category Name :","");
		if(xOverlay.DoModal())
		{
			const char* szCategory = xOverlay.GetString();
			if(*szCategory)
			{
				*m_pxCategory = szCategory;
				if(m_pbModified) *m_pbModified = true;

				//Bump the list valid ID so that other catefory dialogs will be updated as well
				s_iListValidID++;
			}
		}
		//Update the list so that the new category (if any) is added , and so that the selection index
		//is put abck to a non-zero value.
		UpdateCategoryList();
	}
	m_xCategoryBackup = *m_pxCategory;
	m_bSelectionChanged = false;

}

void Tools_SoundCategory_Dialog::PerFrameUpdate()
{
	//check for external changes to the category
	if(!m_bSelectionChanged && m_xCategoryBackup.Compare(*m_pxCategory))
	{
		m_xCategoryBackup = *m_pxCategory;
		UpdateCategoryList();
	}

	if(m_iListValidID != s_iListValidID && !IsHidden())
	{
		//update strings
		UpdateCategoryList();
	}

	Asura_DropDown_List_Dialog::PerFrameUpdate();
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*-------------------------------**
** Tools_SoundSubCategory_Dialog **
**-------------------------------*/

int Tools_SoundSubCategory_Dialog::s_iListValidID;

Tools_SoundSubCategory_Dialog::Tools_SoundSubCategory_Dialog(const Asura_Vector_2& xOffset,const Asura_String* pxCategory,Asura_String* pxSubCategory,bool* pbModified)
:Asura_DropDown_List_Dialog(xOffset)
{
	m_bInitialised = false;
	m_pszSubCategoryList = 0;
	m_uSubCategoryListSize = 0;
	m_pxSubCategoryNameTree = 0;

	m_pbModified = pbModified;
	m_pxCategory = pxCategory;
	Asura_Assert(m_pxCategory,"");
	m_pxSubCategory = pxSubCategory;
	Asura_Assert(m_pxSubCategory,"");
	

	m_bSelectionChanged = false;
	m_xCategoryBackup = *m_pxCategory;
	m_xSubCategoryBackup = *m_pxSubCategory;


	UpdateSubCategoryList();
	Initialise(xOffset,m_pszSubCategoryList,&m_iSelectedIndex,10,m_uSubCategoryListSize,NULL,NULL,"Subcategory");
	m_bInitialised = true;

	SetOnSelectCallback(OnSelectionChanged,(int) this);

}

Tools_SoundSubCategory_Dialog::~Tools_SoundSubCategory_Dialog()
{
	DeleteSubCategoryList();
}


void Tools_SoundSubCategory_Dialog::UpdateSubCategoryList()
{
	DeleteSubCategoryList();

	m_pxSubCategoryNameTree = new Asura_NameTree;

	//make sure the category being edited isn't blank
	if(m_pxSubCategory->IsBlank())
	{
		*m_pxSubCategory = "Default";
		m_xSubCategoryBackup = *m_pxSubCategory;
	}
	//and add it to the tree
	int iJunkIndex;
	m_pxSubCategoryNameTree->FindNodeIndexByName(m_pxSubCategory->GetString(),true,iJunkIndex);



	//add the sub category names from all the sounds and events that use the specified category
	for(u_int i=0;i<Asura_Sound_Event_Storage::GetNumberOfEvents();i++)
	{
		const Asura_Sound_Event* pxEvent = Asura_Sound_Event_Storage::GetEventByIndex(i);
		const char* szCategory = pxEvent->GetEventType();
		const char* szSubCategory = pxEvent->GetEventSubType();

		if(szCategory && !stricmp(szCategory,m_pxCategory->GetString()))
		{
			if(szSubCategory && *szSubCategory)
			{
				m_pxSubCategoryNameTree->FindNodeIndexByName(szSubCategory,true,iJunkIndex);
			}
		}
	}

	for(u_int i=0;i<Asura_Sound_Event_Storage::GetNumberOfSamples();i++)
	{
		const Asura_Sound_Sample* pxSample = Asura_Sound_Event_Storage::GetSampleByIndex(i);
		const char* szCategory = pxSample->GetSoundType();
		const char* szSubCategory = pxSample->GetSoundSubType();

		if(szCategory && !stricmp(szCategory,m_pxCategory->GetString()))
		{
			if(szSubCategory && *szSubCategory)
			{
				m_pxSubCategoryNameTree->FindNodeIndexByName(szSubCategory,true,iJunkIndex);
			}
		}
	}



	//Fill array with these strings , adding an entry for new category at the beginning
	m_uSubCategoryListSize = m_pxSubCategoryNameTree->GetNumberOfNodes()+1;
	m_pszSubCategoryList = new const char*[m_uSubCategoryListSize];
	m_pszSubCategoryList[0] = "*New*";
	m_pxSubCategoryNameTree->MakeSortedNameList(m_pszSubCategoryList+1);

	//find the index of the current string in the sorted list
	m_iSelectedIndex = 0;
	for(u_int i=1;i<m_uSubCategoryListSize;i++)
	{
		if(!m_pxSubCategory->Compare(m_pszSubCategoryList[i]))
		{
			m_iSelectedIndex = (int) i;
			break;
		}
	}
	Asura_Assert(m_iSelectedIndex,"Failed to find category in list");

	if(m_bInitialised)
	{
		ReplaceStrings(m_uSubCategoryListSize,m_pszSubCategoryList);
	}

	m_iListValidID = s_iListValidID;
}

void Tools_SoundSubCategory_Dialog::DeleteSubCategoryList()
{
	delete [] m_pszSubCategoryList;
	m_pszSubCategoryList = 0;
	m_uSubCategoryListSize = 0;
	delete m_pxSubCategoryNameTree;
	m_pxSubCategoryNameTree = 0;
}

void Tools_SoundSubCategory_Dialog::OnSelectionChanged()
{
	if(m_iSelectedIndex)
	{
		//an existing category has been chosen
		*m_pxSubCategory = m_pszSubCategoryList[m_iSelectedIndex];
		if(m_pbModified) *m_pbModified = true;
	}
	else
	{
		//The new category option has been chosen.
		//Ask for a new name
		GetString_Overlay xOverlay("New Sub Category Name :","");
		if(xOverlay.DoModal())
		{
			const char* szSubCategory = xOverlay.GetString();
			if(*szSubCategory)
			{
				*m_pxSubCategory = szSubCategory;
				if(m_pbModified) *m_pbModified = true;

				//Bump the list valid ID so that other category dialogs will be updated as well
				s_iListValidID++;
			}
		}
		//Update the list so that the new category (if any) is added , and so that the selection index
		//is put abck to a non-zero value.
		UpdateSubCategoryList();
	}
	m_xSubCategoryBackup = *m_pxSubCategory;
	m_bSelectionChanged = false;

}

void Tools_SoundSubCategory_Dialog::PerFrameUpdate()
{
	//check for external changes to the subcategory
	if(!m_bSelectionChanged && m_xSubCategoryBackup.Compare(*m_pxSubCategory))
	{
		m_xSubCategoryBackup = *m_pxSubCategory;
		UpdateSubCategoryList();
	}

	//check for external changes to the category
	if(m_xCategoryBackup.Compare(*m_pxCategory))
	{
		m_xCategoryBackup = *m_pxCategory;
		UpdateSubCategoryList();
	}

	if(m_iListValidID != s_iListValidID && !IsHidden())
	{
		//update strings
		UpdateSubCategoryList();
	}

	Asura_DropDown_List_Dialog::PerFrameUpdate();
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*---------------------------**
** Tools_SoundFilename_Dialog **
**----------------------------*/

int Tools_SoundFilename_Dialog::s_iListValidID;

Tools_SoundFilename_Dialog::Tools_SoundFilename_Dialog(const Asura_Vector_2& xOffset,Asura_String* pxFilename,bool* pbModified)
:Asura_DropDown_List_Dialog(xOffset)
{
	m_bInitialised = false;
	m_pszFilenameList = 0;
	m_uFilenameListSize = 0;
	m_pxFilenameTree = 0;

	m_pbModified = pbModified;
	m_pxFilename = pxFilename;
	Asura_Assert(m_pxFilename,"");

	m_bSelectionChanged = false;
	m_xFilenameBackup = *m_pxFilename;

	UpdateFilenameList();
	Initialise(xOffset,m_pszFilenameList,&m_iSelectedIndex,10,m_uFilenameListSize,NULL,&m_bSelectionChanged,"File");
	m_bInitialised = true;

	SetOnSelectCallback(OnSelectionChanged,(int) this);

}

Tools_SoundFilename_Dialog::~Tools_SoundFilename_Dialog()
{
	DeleteFilenameList();
}


void Tools_SoundFilename_Dialog::UpdateFilenameList()
{
	DeleteFilenameList();

	m_pxFilenameTree = new Asura_NameTree;

	//make sure the filename being edited isn't blank
	if(m_pxFilename->IsBlank())
	{
		*m_pxFilename = "Default";
		m_xFilenameBackup = *m_pxFilename;
	}
	//and add it to the tree
	int iJunkIndex;
	m_pxFilenameTree->FindNodeIndexByName(m_pxFilename->GetString(),true,iJunkIndex);

	//add the filenames from all the sounds and events
	for(u_int i=0;i<Asura_Sound_Event_Storage::GetNumberOfEvents();i++)
	{
		const Asura_Sound_Event* pxEvent = Asura_Sound_Event_Storage::GetEventByIndex(i);
		const char* szFilename = pxEvent->GetFilename();

		if(szFilename && *szFilename)
		{
			m_pxFilenameTree->FindNodeIndexByName(szFilename,true,iJunkIndex);
		}
	}

	for(u_int i=0;i<Asura_Sound_Event_Storage::GetNumberOfSamples();i++)
	{
		const Asura_Sound_Sample* pxSample = Asura_Sound_Event_Storage::GetSampleByIndex(i);
		const char* szFilename = pxSample->GetFilename();

		if(szFilename && *szFilename)
		{
			m_pxFilenameTree->FindNodeIndexByName(szFilename,true,iJunkIndex);
		}
	}

	//Fill array with these strings , adding an entry for new category at the beginning
	m_uFilenameListSize = m_pxFilenameTree->GetNumberOfNodes()+1;
	m_pszFilenameList = new const char*[m_uFilenameListSize];
	m_pszFilenameList[0] = "*New*";
	m_pxFilenameTree->MakeSortedNameList(m_pszFilenameList+1);

	//find the index of the current string in the sorted list
	m_iSelectedIndex = 0;
	for(u_int i=1;i<m_uFilenameListSize;i++)
	{
		if(!m_pxFilename->Compare(m_pszFilenameList[i]))
		{
			m_iSelectedIndex = (int) i;
			break;
		}
	}
	Asura_Assert(m_iSelectedIndex,"Failed to find category in list");

	if(m_bInitialised)
	{
		ReplaceStrings(m_uFilenameListSize,m_pszFilenameList);
	}

	m_iListValidID = s_iListValidID;
}

void Tools_SoundFilename_Dialog::DeleteFilenameList()
{
	delete [] m_pszFilenameList;
	m_pszFilenameList = 0;
	m_uFilenameListSize = 0;
	delete m_pxFilenameTree;
	m_pxFilenameTree = 0;
}

void Tools_SoundFilename_Dialog::OnSelectionChanged()
{
	if(m_iSelectedIndex)
	{
		//an existing filename has been chosen
		*m_pxFilename = m_pszFilenameList[m_iSelectedIndex];
		if(m_pbModified) *m_pbModified = true;
	}
	else
	{
		//The new filename option has been chosen.
		//Ask for a new name
		GetString_Overlay xOverlay("New Filename (without path/extension):","");
		if(xOverlay.DoModal())
		{
			const char* szFilename = xOverlay.GetString();
			if(*szFilename)
			{
				*m_pxFilename = szFilename;
				if(m_pbModified) *m_pbModified = true;

				//Bump the list valid ID so that other filename dialogs will be updated as well
				s_iListValidID++;
			}
		}
		//Update the list so that the new filename (if any) is added , and so that the selection index
		//is put abck to a non-zero value.
		UpdateFilenameList();
	}
	m_xFilenameBackup = *m_pxFilename;
	m_bSelectionChanged = false;
}

void Tools_SoundFilename_Dialog::PerFrameUpdate()
{
	//check for external changes to the filename
	if(!m_bSelectionChanged && m_xFilenameBackup.Compare(*m_pxFilename))
	{
		m_xFilenameBackup = *m_pxFilename;
		UpdateFilenameList();
	}

	if(m_iListValidID != s_iListValidID && !IsHidden())
	{
		//update strings
		UpdateFilenameList();
	}

	Asura_DropDown_List_Dialog::PerFrameUpdate();
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*---------------------------------**
** Tools_SoundInstanceGroup_Dialog **
**---------------------------------*/

int Tools_SoundInstanceGroup_Dialog::s_iListValidID;

Tools_SoundInstanceGroup_Dialog::Tools_SoundInstanceGroup_Dialog(const Asura_Vector_2& xOffset,Asura_String* pxInstanceGroup,bool* pbModified)
:Asura_DropDown_List_Dialog(xOffset)
{
	m_bInitialised = false;
	m_pszInstanceGroupList = 0;
	m_uInstanceGroupListSize = 0;
	m_pxInstanceGroupNameTree = 0;

	m_pbModified = pbModified;
	m_pxInstanceGroup = pxInstanceGroup;
	Asura_Assert(m_pxInstanceGroup,"");

	m_bSelectionChanged = false;
	m_xInstanceGroupBackup = *m_pxInstanceGroup;


	UpdateInstanceGroupList();
	Initialise(xOffset,m_pszInstanceGroupList,&m_iSelectedIndex,10,m_uInstanceGroupListSize,NULL,NULL,"Group");
	m_bInitialised = true;

	SetOnSelectCallback(OnSelectionChanged,(int) this);

}

Tools_SoundInstanceGroup_Dialog::~Tools_SoundInstanceGroup_Dialog()
{
	DeleteInstanceGroupList();
}


void Tools_SoundInstanceGroup_Dialog::UpdateInstanceGroupList()
{
	DeleteInstanceGroupList();

	m_pxInstanceGroupNameTree = new Asura_NameTree;

	//make sure the category being edited isn't blank
//	if(m_pxInstanceGroup->IsBlank())
//	{
//		*m_pxInstanceGroup = "None";
//		m_xInstanceGroupBackup = *m_pxInstanceGroup;
//	}
	//and add it to the tree
	//const char* szGroup = m_pxInstanceGroup->IsBlank() ? "None" : m_pxInstanceGroup->GetString();

	int iJunkIndex;
	if(!m_pxInstanceGroup->IsBlank())
	{
		m_pxInstanceGroupNameTree->FindNodeIndexByName(m_pxInstanceGroup->GetString(), true,iJunkIndex);
	}



	//add the group names from all the events
	for(u_int i=0;i<Asura_Sound_Event_Storage::GetNumberOfEvents();i++)
	{
		const Asura_Sound_Event* pxEvent = Asura_Sound_Event_Storage::GetEventByIndex(i);
		const char* szInstanceGroup = pxEvent->GetInstanceGroupName();

		if(szInstanceGroup && *szInstanceGroup)
		{
			m_pxInstanceGroupNameTree->FindNodeIndexByName(szInstanceGroup,true,iJunkIndex);
		}
	}




	//Fill array with these strings , adding entries for a new category and none at the beginning
	m_uInstanceGroupListSize = m_pxInstanceGroupNameTree->GetNumberOfNodes()+2;
	m_pszInstanceGroupList = new const char*[m_uInstanceGroupListSize];
	m_pszInstanceGroupList[0] = "*New*";
	m_pszInstanceGroupList[1] = "*None*";
	m_pxInstanceGroupNameTree->MakeSortedNameList(m_pszInstanceGroupList+2);

	//find the index of the current string in the sorted list
	m_iSelectedIndex = 1;
	if(!m_pxInstanceGroup->IsBlank())
	{
		for(u_int i=2;i<m_uInstanceGroupListSize;i++)
		{
			if(!m_pxInstanceGroup->Compare(m_pszInstanceGroupList[i]))
			{
				m_iSelectedIndex = (int) i;
				break;
			}
		}
		Asura_Assert(m_iSelectedIndex,"Failed to find instance group in list");
	}

	if(m_bInitialised)
	{
		ReplaceStrings(m_uInstanceGroupListSize,m_pszInstanceGroupList);
	}

	m_iListValidID = s_iListValidID;
}

void Tools_SoundInstanceGroup_Dialog::DeleteInstanceGroupList()
{
	delete [] m_pszInstanceGroupList;
	m_pszInstanceGroupList = 0;
	m_uInstanceGroupListSize = 0;
	delete m_pxInstanceGroupNameTree;
	m_pxInstanceGroupNameTree = 0;
}

void Tools_SoundInstanceGroup_Dialog::OnSelectionChanged()
{
	if(m_iSelectedIndex == 1)
	{
		//'None' selected
		m_pxInstanceGroup->MakeNull();
		if(m_pbModified) *m_pbModified = true;
	}
	else if(m_iSelectedIndex)
	{
		//an existing group has been chosen
		*m_pxInstanceGroup = m_pszInstanceGroupList[m_iSelectedIndex];
		if(m_pbModified) *m_pbModified = true;
	}
	else
	{
		//The new group option has been chosen.
		//Ask for a new name
		GetString_Overlay xOverlay("New Instance Group Name :","");
		if(xOverlay.DoModal())
		{
			const char* szInstanceGroup = xOverlay.GetString();
			if(*szInstanceGroup)
			{
				*m_pxInstanceGroup = szInstanceGroup;
				if(m_pbModified) *m_pbModified = true;

				//Bump the list valid ID so that other group dialogs will be updated as well
				s_iListValidID++;
			}
		}
		//Update the list so that the new group (if any) is added , and so that the selection index
		//is put abck to a non-zero value.
		UpdateInstanceGroupList();
	}
	m_xInstanceGroupBackup = *m_pxInstanceGroup;
	m_bSelectionChanged = false;

}

void Tools_SoundInstanceGroup_Dialog::PerFrameUpdate()
{
	//check for external changes to the group
	if(!m_bSelectionChanged)
	{
		if(m_xInstanceGroupBackup.IsBlank() != m_pxInstanceGroup->IsBlank() ||
			m_xInstanceGroupBackup.Compare(*m_pxInstanceGroup))
		{
	
			m_xInstanceGroupBackup = *m_pxInstanceGroup;
			UpdateInstanceGroupList();
		}
	}

	if(m_iListValidID != s_iListValidID && !IsHidden())
	{
		//update strings
		UpdateInstanceGroupList();
	}

	Asura_DropDown_List_Dialog::PerFrameUpdate();
}

//eof
