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

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

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

//tools
#include "Sound/Tools_SoundEvent_Overlay.h"
#include "Sound/Tools_SoundEvent_System.h"



Asura_String Tools_PickSoundEvent_Overlay::s_xFileFilter;
Asura_String Tools_PickSoundEvent_Overlay::s_xCategoryFilter;
Asura_String Tools_PickSoundEvent_Overlay::s_xSubCategoryFilter;
Asura_String Tools_PickSoundEvent_Overlay::s_xMixerFilter;
Asura_String Tools_PickSoundEvent_Overlay::s_xLastFindEventID;
Tools_PickSoundEvent_Overlay* Tools_PickSoundEvent_Overlay::s_pxThis;
bool Tools_PickSoundEvent_Overlay::s_bShowCodeEvent = true;
bool Tools_PickSoundEvent_Overlay::s_bShowNonCodeEvent = true;
bool Tools_PickSoundEvent_Overlay::s_bAutoPreview = true;

static const char* g_szFilterNone = "*All*";
static const char* g_szEventNone = "*None*";
static const char* g_szEventUnknown = "*Unknown*";

//search types
enum
{
	DIALOGUE_SEARCH_BEGINS,
	DIALOGUE_SEARCH_CONTAINS,
};

static const char* g_aszSearchOptions[] =
{
	"Begins With",
	"Contains",
};


Tools_PickSoundEvent_Overlay::Tools_PickSoundEvent_Overlay(const char* szTitle,bool bMultipleSelection)
:Tools_Modal_Overlay(szTitle)
{
	m_uFileListSize = 0;
	m_apszFileList = 0;
	m_pxFileStringTree = new Asura_StringTree;

	m_uCategoryListSize = 0;
	m_apszCategoryList = 0;
	m_pxCategoryStringTree = new Asura_StringTree;

	m_uSubCategoryListSize = 0;
	m_apszSubCategoryList = 0;
	m_pxSubCategoryStringTree = new Asura_StringTree;

	m_uMixerListSize = 0;
	m_apszMixerList = 0;
	m_pxMixerStringTree = new Asura_StringTree;

	m_uEventListSize = 0;
	m_apszEventList = 0;
	m_pxEventStringTree = new Asura_StringTree;

	m_iSoundHandle = ASURA_INVALID_HANDLE;

	m_bMultipleSelection = bMultipleSelection;

	m_iSearchOption = 0;
}

Tools_PickSoundEvent_Overlay::~Tools_PickSoundEvent_Overlay()
{
	DeleteFilterLists();
	DeleteEventList();


	delete m_pxFileStringTree;
	delete m_pxCategoryStringTree;
	delete m_pxSubCategoryStringTree;
	delete m_pxMixerStringTree;
	delete m_pxEventStringTree;
}


Asura_Hash_ID Tools_PickSoundEvent_Overlay::DoModal(Asura_Hash_ID uInitialSelection)
{
	m_uInitialSelection = uInitialSelection;
	m_uMostRecentSelectedEvent = m_uSelectedEvent = m_uInitialSelection;
	m_fInputTimer = 0.0f;
	m_szInput[0] = 0;
	m_pxEventListDialog = 0;
	m_pxSubCategoryListDialog = 0;
	m_eResult = OVERLAY_CANCEL;

	s_pxThis = this;
	ShowOverlay();
	StopSound();
	s_pxThis = 0;

	if(m_bMultipleSelection)
	{
		//return value isn't that useful for multuple selection , but default to the first in the list
		m_uSelectedEvent = m_xSelectionList.size() ? m_xSelectionList.first_entry() : 0;
	}
	

	switch(m_eResult)
	{
		case OVERLAY_CANCEL :
		{
			while(m_xSelectionList.size()) m_xSelectionList.delete_first_entry();
			return m_uInitialSelection;
		}

		case OVERLAY_OKAY :
		{
			return m_uSelectedEvent;
		}
			
		case OVERLAY_NEWEVENT :
		{
			Asura_Hash_ID uNewHash = Tools_SoundEvent_System::CreateEvent(s_xFileFilter.GetString(),s_xCategoryFilter.GetString(),s_xSubCategoryFilter.GetString(),0);
			return uNewHash ? uNewHash : m_uInitialSelection;
		}

		case OVERLAY_NEWCODEEVENT :
		{
			Asura_Hash_ID uNewHash = Tools_SoundEvent_System::CreateCodeEvent(s_xFileFilter.GetString(),s_xCategoryFilter.GetString(),s_xSubCategoryFilter.GetString());
			return uNewHash ? uNewHash : m_uInitialSelection;
		}
		
		case OVERLAY_EDITEVENT :
		{
			if(m_bMultipleSelection)
			{
				for(LIF<Asura_Hash_ID> xSelectionLIF(&m_xSelectionList);!xSelectionLIF.done();xSelectionLIF.next())
				{
					Tools_SoundEvent_Overlay::EditEvent(xSelectionLIF());
				}
			}
			else
			{
				if(Asura_Sound_Event_Storage::GetEventByHash(m_uSelectedEvent)) Tools_SoundEvent_Overlay::EditEvent(m_uSelectedEvent);
			}
			return m_uSelectedEvent;
		}
	}

	return m_uInitialSelection;
}

void Tools_PickSoundEvent_Overlay::InitialiseOverlay(Asura_Overlay* pxOverlay)
{
	BuildFilterLists();
	//make sure the default selection will be visible by the current filters.
	const Asura_Sound_Event* pxEvent = Asura_Sound_Event_Storage::GetEventByHash(m_uInitialSelection);
	if(pxEvent)
	{
		if(!s_xFileFilter.IsBlank())
		{
			const char* szFilename = pxEvent->GetFilename();
			if(!szFilename || s_xFileFilter.Compare(szFilename))
			{
				s_xFileFilter.MakeNull();
				m_iSelectedFileFilter = 0;
			}
		}
		if(!s_xCategoryFilter.IsBlank())
		{
			const char* szCategory = pxEvent->GetEventType();
			if(!szCategory || s_xCategoryFilter.Compare(szCategory))
			{
				s_xCategoryFilter.MakeNull();
				m_iSelectedCategoryFilter = 0;
			}
		}
		if(!s_xSubCategoryFilter.IsBlank())
		{
			const char* szSubCategory = pxEvent->GetEventSubType();
			if(!szSubCategory || s_xSubCategoryFilter.Compare(szSubCategory))
			{
				s_xSubCategoryFilter.MakeNull();
				m_iSelectedSubCategoryFilter = 0;
			}
		}
		if(!s_xMixerFilter.IsBlank())
		{
			const char* szMixer = pxEvent->GetMixerChannelName();
			if(!szMixer || s_xMixerFilter.Compare(szMixer))
			{
				s_xMixerFilter.MakeNull();
				m_iSelectedMixerFilter = 0;
			}
		}

		
		if(Tools_SoundEvent_System::IsCodeEvent(pxEvent->GetHashID()))
		{
			s_bShowCodeEvent = true;
		}
		else
		{
			s_bShowNonCodeEvent = true;
		}
		

	}
	BuildEventList();

	Asura_Vector_2 xOffset(1.0f,3.0f);
	Asura_Dialog* pxDialog;

	m_bFilterChanged = false;
	m_bCategoryChanged = false;

	//determine the maximum number of lines to use for the various lists
	float fMaxPrintY = Asura_Viewport::GetHeight() / ASURA_TEXT_OUTPUT_FONT_HEIGHT;
	const int iMaxLines = Asura_Maths::FloatToInt_Chop((fMaxPrintY - 24.0f)/1.2f);

	float fDropDownListWidth = CalculateDropDownListWidth();


	//add drop down lists for filters
	Asura_DropDown_List_Dialog* pxDropListDialog;
	
	pxDropListDialog = new Asura_DropDown_List_Dialog(xOffset,m_apszFileList,&m_iSelectedFileFilter,iMaxLines,m_uFileListSize,NULL,&m_bFilterChanged,"File");
	pxDropListDialog->SetHelpText("Filter event list by the file that they belong to.\nAlso used as tne initial file for new events.");
	pxDropListDialog->SetMinimumWidth(fDropDownListWidth);
	pxOverlay->AddDialog(pxDropListDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	float fDropDownMaxX = pxDropListDialog->m_xMaxCoord.x;


	pxDropListDialog = new Asura_DropDown_List_Dialog(xOffset,m_apszCategoryList,&m_iSelectedCategoryFilter,iMaxLines,m_uCategoryListSize,NULL,&m_bCategoryChanged,"Category");
	pxDropListDialog->SetHelpText("Filter event list by the category that they belong to.\nAlso used as tne initial category for new events.");
	pxDropListDialog->SetMinimumWidth(fDropDownListWidth);
	pxOverlay->AddDialog(pxDropListDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();
	fDropDownMaxX = Asura_Maths::Max(pxDropListDialog->m_xMaxCoord.x,fDropDownMaxX);

	pxDropListDialog = m_pxSubCategoryListDialog = new Asura_DropDown_List_Dialog(xOffset,m_apszSubCategoryList,&m_iSelectedSubCategoryFilter,iMaxLines,m_uSubCategoryListSize,NULL,&m_bFilterChanged,"Subcategory");
	pxDropListDialog->SetHelpText("Filter event list by the subcategory that they belong to.\nAlso used as tne initial subcategory for new events.");
	pxDropListDialog->SetMinimumWidth(fDropDownListWidth);
	pxOverlay->AddDialog(pxDropListDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();
	fDropDownMaxX = Asura_Maths::Max(pxDropListDialog->m_xMaxCoord.x,fDropDownMaxX);

	pxDropListDialog = new Asura_DropDown_List_Dialog(xOffset,m_apszMixerList,&m_iSelectedMixerFilter,iMaxLines,m_uMixerListSize,NULL,&m_bFilterChanged,"Mixer");
	pxDropListDialog->SetHelpText("Filter event list by mixer channel used.");
	pxDropListDialog->SetMinimumWidth(fDropDownListWidth);
	pxOverlay->AddDialog(pxDropListDialog);
	xOffset.y += Asura_Dialog::LargeSpacing();
	fDropDownMaxX = Asura_Maths::Max(pxDropListDialog->m_xMaxCoord.x,fDropDownMaxX);

	//add code/non code checkboxes to the right

	xOffset.y -= (Asura_Dialog::DefaultSpacing()+Asura_Dialog::LargeSpacing());
	xOffset.x = fDropDownMaxX + 2.0f;

	pxDialog = new Asura_Tickbox_Dialog("Code",xOffset,&s_bShowCodeEvent,&m_bFilterChanged);
	pxDialog->SetHelpText("Show code events");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog("Non-Code",xOffset,&s_bShowNonCodeEvent,&m_bFilterChanged);
	pxDialog->SetHelpText("Show non-code events");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::LargeSpacing();

	xOffset.x = 1.0f;

	//Search/Search Type
	pxDropListDialog = new Asura_DropDown_List_Dialog(xOffset,g_aszSearchOptions,&m_iSearchOption,iMaxLines,2,NULL,&m_bFilterChanged,"");
	pxDropListDialog->SetHelpText("Determines how the search text should be used for filtering the list.");
	pxOverlay->AddDialog(pxDropListDialog);


	xOffset.x = pxDropListDialog->m_xMaxCoord.x + 1.0f;
	pxDialog = new Asura_String_Dialog("",xOffset,&m_xSearchText,30,&m_bFilterChanged);
	((Asura_String_Dialog*)pxDialog)->SetUpdateImmediatelyWhileTyping(true);
	pxDialog->SetHelpText("Filters the event list so that it only shows those beginning with/containing this string");;
	xOffset.x = 1.0f;
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::LargeSpacing();
	

	//control for searching by first few letters in name
	m_pxInputDialog = new Asura_Dialog( "", xOffset );
	pxOverlay->AddDialog( m_pxInputDialog );
	xOffset.y += Asura_Dialog::SmallSpacing();

	//list of events
	m_bEventChanged = false;
	pxDialog = m_pxEventListDialog = new Asura_List_Dialog(xOffset,m_apszEventList,&m_iEventIndex,iMaxLines,m_uEventListSize,NULL,&m_bEventChanged);
	m_pxEventListDialog->EnableMultipleSelection(m_bMultipleSelection);
	m_pxEventListDialog->SetDoubleClickFunction(OnDoubleClickList,0);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += iMaxLines*1.2f + 1.0f;


	pxDialog = new Asura_Function_Dialog("Play Event",xOffset,TestSound);
	pxDialog->SetHelpText("Tests the current sound event.");
	pxOverlay->AddDialog(pxDialog);

	xOffset.x += 12.0f;
	pxDialog = new Asura_Function_Dialog("Stop Event",xOffset,StopSound);
	pxDialog->SetHelpText("Stops the test sound.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.x -= 12.0f;

	xOffset.x += 24.0f;
	pxDialog = new Asura_Tickbox_Dialog("Auto Preview",xOffset,&s_bAutoPreview);
	pxDialog->SetHelpText("Automatically previews each sound as it is selected.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.x -= 24.0f;
	xOffset.y += Asura_Dialog::LargeSpacing();

	pxDialog = new Asura_Function_Dialog("New Event",xOffset,OnNewEvent);
	pxDialog->SetHelpText("Create and select a new event.");
	pxOverlay->AddDialog(pxDialog);

	xOffset.x += 12.0f;
	pxDialog = new Asura_Function_Dialog("Edit Event",xOffset,OnEditEvent);
	pxDialog->SetHelpText("Selects the current event and opens the overlay for editing it.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.x -= 12.0f;

	xOffset.x += 24.0f;
	pxDialog = new Asura_Function_Dialog("Delete",xOffset,DeleteEvents,(int)this);
	pxDialog->SetHelpText("Delete the selected sample(s).");
	pxOverlay->AddDialog(pxDialog);
	xOffset.x -= 24.0f;

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

	pxDialog = new Asura_Function_Dialog("New Code Event",xOffset,OnNewCodeEvent);
	pxDialog->SetHelpText("Create and select a new event , picking a name from the list of events declared by code.");
	pxOverlay->AddDialog(pxDialog);
	
	xOffset.x += 12.0f;
	pxDialog = new Asura_Function_Dialog("Find Event By ID",xOffset,OnFindEventByID);
	pxDialog->SetHelpText("Allows you to type in a hash ID and locate the corresponding event. Mainly a debugging aid.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.x -= 12.0f;

	xOffset.y += Asura_Dialog::LargeSpacing();

	pxDialog = new Asura_Function_Dialog("Okay",xOffset,OnOkay);
	pxOverlay->AddDialog(pxDialog);

	xOffset.x += 12.0f;
	pxDialog = new Asura_Function_Dialog("Cancel",xOffset,OnCancel);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();
	xOffset.x -= 12.0f;


}

void Tools_PickSoundEvent_Overlay::BuildFilterLists()
{
	DeleteFilterLists();

	int iJunkIndex;
	//get a list of all files and categories used by the events and samples
	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_pxCategoryStringTree->FindNodeIndexByName(szCategory,true,iJunkIndex);
		}
		*/

		const char* szFilename = pxSample->GetFilename();
		if(szFilename && *szFilename)
		{
			m_pxFileStringTree->FindNodeIndexByName(szFilename,true,iJunkIndex);
		}
	}
	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_pxCategoryStringTree->FindNodeIndexByName(szCategory,true,iJunkIndex);
		}

		const char* szFilename = pxEvent->GetFilename();
		if(szFilename && *szFilename)
		{
			m_pxFileStringTree->FindNodeIndexByName(szFilename,true,iJunkIndex);
		}
	}
	//get the mixer channel names
	for(u_int i=0;i<Asura_Sound_Mixer_System::GetNumberOfChannels();i++)
	{
		const Asura_Sound_Mixer_Channel* pxChannel = Asura_Sound_Mixer_System::GetChannelByIndex(i);
		const char* szName = pxChannel->GetName();

		if(szName && *szName)
		{
			m_pxMixerStringTree->FindNodeIndexByName(szName,true,iJunkIndex);
		}
	}
	//add the master mixer
	m_pxMixerStringTree->FindNodeIndexByName("Master",true,iJunkIndex);



	//get lists from the name tree for the list dialogs
	m_uFileListSize = m_pxFileStringTree->GetNumberOfNodes()+1;
	m_apszFileList = new const char*[m_uFileListSize];
	m_apszFileList[0] = g_szFilterNone;
	m_pxFileStringTree->MakeSortedNameList(m_apszFileList+1);


	m_uCategoryListSize = m_pxCategoryStringTree->GetNumberOfNodes()+1;
	m_apszCategoryList = new const char*[m_uCategoryListSize];
	m_apszCategoryList[0] = g_szFilterNone;
	m_pxCategoryStringTree->MakeSortedNameList(m_apszCategoryList+1);

	m_uMixerListSize = m_pxMixerStringTree->GetNumberOfNodes()+1;
	m_apszMixerList = new const char*[m_uMixerListSize];
	m_apszMixerList[0] = g_szFilterNone;
	m_pxMixerStringTree->MakeSortedNameList(m_apszMixerList+1);


	//determine the index of the initial selections
	m_iSelectedFileFilter = 0;
	if(!s_xFileFilter.IsBlank())
	{
		for(u_int i=1;i<m_uFileListSize;i++)
		{
			if(!s_xFileFilter.Compare(m_apszFileList[i]))
			{
				m_iSelectedFileFilter = i;
				break;
			}
		}
		if(!m_iSelectedFileFilter) s_xFileFilter.MakeNull();
	}

	m_iSelectedCategoryFilter = 0;
	if(!s_xCategoryFilter.IsBlank())
	{
		for(u_int i=1;i<m_uCategoryListSize;i++)
		{
			if(!s_xCategoryFilter.Compare(m_apszCategoryList[i]))
			{
				m_iSelectedCategoryFilter = i;
				break;
			}
		}
		if(!m_iSelectedCategoryFilter) s_xCategoryFilter.MakeNull();
	}

	m_iSelectedMixerFilter = 0;
	if(!s_xMixerFilter.IsBlank())
	{
		for(u_int i=1;i<m_uMixerListSize;i++)
		{
			if(!s_xMixerFilter.Compare(m_apszMixerList[i]))
			{
				m_iSelectedMixerFilter = i;
				break;
			}
		}
		if(!m_iSelectedMixerFilter) s_xMixerFilter.MakeNull();
	}

	BuildSubCategoryList();

}

void Tools_PickSoundEvent_Overlay::DeleteFilterLists()
{
	delete [] m_apszFileList;
	m_apszFileList = 0;
	m_uFileListSize = 0;
	m_pxFileStringTree->Reset();

	delete [] m_apszCategoryList;
	m_apszCategoryList = 0;
	m_uCategoryListSize = 0;
	m_pxCategoryStringTree->Reset();

	delete [] m_apszMixerList;
	m_apszMixerList = 0;
	m_uMixerListSize = 0;
	m_pxMixerStringTree->Reset();

	DeleteSubCategoryList();
}

void Tools_PickSoundEvent_Overlay::BuildSubCategoryList()
{
	DeleteSubCategoryList();

	int iJunkIndex;

	//get a list of all subcategories used by the events with the current category filter
	for(u_int i=0;i<Asura_Sound_Event_Storage::GetNumberOfEvents();i++)
	{
		const Asura_Sound_Event* pxEvent = Asura_Sound_Event_Storage::GetEventByIndex(i);

		//event uses current category?
		if(!s_xCategoryFilter.IsBlank())
		{
			const char* szCategory = pxEvent->GetEventType();
			if(!szCategory || s_xCategoryFilter.Compare(szCategory))
			{
				continue;
			}
		}
		
		const char* szSubCategory = pxEvent->GetEventSubType();
		if(szSubCategory && *szSubCategory)
		{
			m_pxSubCategoryStringTree->FindNodeIndexByName(szSubCategory,true,iJunkIndex);
		}
	}


	m_uSubCategoryListSize = m_pxSubCategoryStringTree->GetNumberOfNodes()+1;
	m_apszSubCategoryList = new const char*[m_uSubCategoryListSize];
	m_apszSubCategoryList[0] = g_szFilterNone;
	m_pxSubCategoryStringTree->MakeSortedNameList(m_apszSubCategoryList+1);


	//determine the index of the initial selection
	int iSelectedSubCategoryFilter = 0;
	if(!s_xSubCategoryFilter.IsBlank())
	{
		for(u_int i=1;i<m_uSubCategoryListSize;i++)
		{
			if(!s_xSubCategoryFilter.Compare(m_apszSubCategoryList[i]))
			{
				iSelectedSubCategoryFilter = i;
				break;
			}
		}
		if(!iSelectedSubCategoryFilter) s_xSubCategoryFilter.MakeNull();
	}

	//update the list dialog if it has been created yet
	if(m_pxSubCategoryListDialog)
	{
		m_pxSubCategoryListDialog->ReplaceStrings(m_uSubCategoryListSize,m_apszSubCategoryList);
	}

	m_iSelectedSubCategoryFilter = iSelectedSubCategoryFilter;
}

void Tools_PickSoundEvent_Overlay::DeleteSubCategoryList()
{
	delete [] m_apszSubCategoryList;
	m_apszSubCategoryList = 0;
	m_uSubCategoryListSize = 0;
	m_pxSubCategoryStringTree->Reset();
}


void Tools_PickSoundEvent_Overlay::BuildEventList()
{
	DeleteEventList();
	
	char szWildcardSearchString[100];
	if(!m_xSearchText.IsBlank() && m_iSearchOption == DIALOGUE_SEARCH_CONTAINS)
	{
		//prepare search string for wildcard search
		Asura_PrintfToBfr(szWildcardSearchString,"*%s*",sizeof(szWildcardSearchString),true,m_xSearchText.GetString());
	}
	
	//get a list of all events that meet the current filter requirements
	int iJunkIndex;
	for(u_int i=0;i<Asura_Sound_Event_Storage::GetNumberOfEvents();i++)
	{
		const Asura_Sound_Event* pxEvent = Asura_Sound_Event_Storage::GetEventByIndex(i);

		if(!s_xFileFilter.IsBlank())
		{
			const char* szFilename = pxEvent->GetFilename();
			if(!szFilename || s_xFileFilter.Compare(szFilename))
			{
				continue;
			}
		}

		if(!s_xCategoryFilter.IsBlank())
		{
			const char* szCategory = pxEvent->GetEventType();
			if(!szCategory || s_xCategoryFilter.Compare(szCategory))
			{
				continue;
			}
		}

		if(!s_xSubCategoryFilter.IsBlank())
		{
			const char* szSubCategory = pxEvent->GetEventSubType();
			if(!szSubCategory || s_xSubCategoryFilter.Compare(szSubCategory))
			{
				continue;
			}
		}

		if(!s_xMixerFilter.IsBlank())
		{
			const char* szMixer = pxEvent->GetMixerChannelName();
			if(!szMixer || s_xMixerFilter.Compare(szMixer))
			{
				continue;
			}
		}

		
		if(!s_bShowCodeEvent || !s_bShowNonCodeEvent)
		{
			if(Tools_SoundEvent_System::IsCodeEvent(pxEvent->GetHashID()))
			{
				if(!s_bShowCodeEvent) continue;
			}
			else
			{
				if(!s_bShowNonCodeEvent) continue;
			}
		}

		if(!m_xSearchText.IsBlank())
		{
			if(m_iSearchOption == DIALOGUE_SEARCH_BEGINS)
			{
				if(strnicmp(m_xSearchText.GetString(),pxEvent->GetName(),m_xSearchText.GetLength()))
				{
					continue;
				}
			}
			else
			{
				if(!Asura_StringUtil::CompareAgainstWildcardString(pxEvent->GetName(),szWildcardSearchString,true))
				{
					continue;
				}
			}
		}
		m_pxEventStringTree->FindNodeIndexByName(pxEvent->GetName(),true,iJunkIndex);

	}

	//need an entry for 'unknown' ? 
	bool bNeedUnknown = false;
	if(m_uInitialSelection && !Asura_Sound_Event_Storage::GetEventByHash(m_uInitialSelection))
	{
		bNeedUnknown = true;
	}

	u_int uSpecialEntries = bNeedUnknown ? 2 : 1;

	//get array of event names , and fill in the special entries that arne't in the tree
	m_uEventListSize = m_pxEventStringTree->GetNumberOfNodes()+uSpecialEntries;
	m_apszEventList = new const char*[m_uEventListSize];
	m_apszEventList[0] = g_szEventNone;
	if(bNeedUnknown) m_apszEventList[1] = g_szEventUnknown;
	m_pxEventStringTree->MakeSortedNameList(m_apszEventList+uSpecialEntries);

	//Update the current selected index to be as close as possible to the previous selection
	if(m_uMostRecentSelectedEvent == 0)
	{
		m_iEventIndex = 0;
	}
	else if(bNeedUnknown && m_uMostRecentSelectedEvent == m_uInitialSelection)
	{
		m_iEventIndex = 1;
	}
	else
	{
		const Asura_Sound_Event* pxEvent = Asura_Sound_Event_Storage::GetEventByHash(m_uMostRecentSelectedEvent);
		m_iEventIndex = 0;
		if(pxEvent)
		{
			for(u_int i=uSpecialEntries;i<m_uEventListSize;i++)
			{
				if(stricmp(pxEvent->GetName(),m_apszEventList[i])>=0)
				{
					m_iEventIndex = i;
				}
				else
				{
					break;
				}
			}
		}
	}

	//get the hash ID of the event selected
	m_uSelectedEvent = GetEventHash(m_iEventIndex);

	//multiple selection list will be junk after list change
	while(m_xSelectionList.size()) m_xSelectionList.delete_first_entry();

	//update the list dialog if it has been created yet
	if(m_pxEventListDialog)
	{
		m_pxEventListDialog->ReplaceStrings(m_uEventListSize,m_apszEventList);
	}
}

Asura_Hash_ID Tools_PickSoundEvent_Overlay::GetEventHash(int iIndex) const
{
	if(!stricmp(m_apszEventList[iIndex],g_szEventNone))
	{
		return 0;
	}
	else if(!stricmp(m_apszEventList[iIndex],g_szEventUnknown))
	{
		return m_uInitialSelection;
	}
	else
	{
		return Asura_GetHashID(m_apszEventList[iIndex]);
	}
}

void Tools_PickSoundEvent_Overlay::DeleteEventList()
{
	delete [] m_apszEventList;
	m_apszEventList = 0;
	m_uEventListSize = 0;
	m_pxEventStringTree->Reset();
}


void Tools_PickSoundEvent_Overlay::OnOkay()
{
	Asura_Assert(s_pxThis,"");
	s_pxThis->m_eResult = OVERLAY_OKAY;
	s_pxThis->GetOverlay()->m_bShouldClose = true;			

}

void Tools_PickSoundEvent_Overlay::OnCancel()
{
	Asura_Assert(s_pxThis,"");
	s_pxThis->m_eResult = OVERLAY_CANCEL;
	s_pxThis->GetOverlay()->m_bShouldClose = true;			
}

void Tools_PickSoundEvent_Overlay::OnNewEvent()
{
	Asura_Assert(s_pxThis,"");
	s_pxThis->m_eResult = OVERLAY_NEWEVENT;
	s_pxThis->GetOverlay()->m_bShouldClose = true;			
}

void Tools_PickSoundEvent_Overlay::OnNewCodeEvent()
{
	Asura_Assert(s_pxThis,"");
	s_pxThis->m_eResult = OVERLAY_NEWCODEEVENT;
	s_pxThis->GetOverlay()->m_bShouldClose = true;			
}


void Tools_PickSoundEvent_Overlay::OnEditEvent()
{
	Asura_Assert(s_pxThis,"");
	s_pxThis->m_eResult = OVERLAY_EDITEVENT;
	s_pxThis->GetOverlay()->m_bShouldClose = true;			
}

void Tools_PickSoundEvent_Overlay::Update()
{
	if(m_bCategoryChanged)
	{
		if(m_iSelectedCategoryFilter)
		{
			s_xCategoryFilter = m_apszCategoryList[m_iSelectedCategoryFilter];
		}
		else
		{
			s_xCategoryFilter.MakeNull();
		}

		BuildSubCategoryList();
		m_bCategoryChanged = false;
		m_bFilterChanged = true;
	}

	if(m_bFilterChanged)
	{
		if(m_iSelectedFileFilter)
		{
			s_xFileFilter = m_apszFileList[m_iSelectedFileFilter];
		}
		else
		{
			s_xFileFilter.MakeNull();
		}
		

		if(m_iSelectedSubCategoryFilter)
		{
			s_xSubCategoryFilter = m_apszSubCategoryList[m_iSelectedSubCategoryFilter];
		}
		else
		{
			s_xSubCategoryFilter.MakeNull();
		}

		if(m_iSelectedMixerFilter)
		{
			s_xMixerFilter = m_apszMixerList[m_iSelectedMixerFilter];
		}
		else
		{
			s_xMixerFilter.MakeNull();
		}


		//rebuild the event list
		BuildEventList();
		m_bFilterChanged = false;
	}

	if(m_fInputTimer>0)
	{
		//update timer to see if next key pressed should be considered to have been done quickly enough
		//to be part of the same string
		m_fInputTimer -= Asura_Timers::GetFrameTime();
		if(m_fInputTimer<=0)
		{ 
			m_fInputTimer = 0;
			m_szInput[0] = 0;
			m_pxInputDialog->UpdateLabel(m_szInput);
		}
	}
	
	if(!Asura_Overlay_System::GetFocusGadget())
	{
		//see if any letter key has been pressed
		char cKey = Asura_Gadget::GetAsciiKeyPressed();

		if(cKey)
		{
			int iLength = strlen(m_szInput);
			if(iLength<sizeof(m_szInput)-1)
			{
				m_szInput[iLength++] = cKey;
				m_szInput[iLength++] = 0;
				m_fInputTimer = 0.5f;
				m_pxInputDialog->UpdateLabel(m_szInput);

			}

			//advance list to first entry greater than the current input string
			for(u_int i=0;i<m_uEventListSize;i++)
			{
				if( stricmp(m_apszEventList[i], m_szInput) >= 0 )
				{
					//move selection to this
					m_iEventIndex = i;
					m_bEventChanged = true;
					break;
				}
			}
		}
	}

	if(m_bEventChanged)
	{
		//get the hash of the new event
		m_uMostRecentSelectedEvent = m_uSelectedEvent = GetEventHash(m_iEventIndex);
		m_bEventChanged = false;

		//If doing multiple selection , get hashes of all in list
		if(m_bMultipleSelection)
		{
			while(m_xSelectionList.size()) m_xSelectionList.delete_first_entry();

			int iNumSelected = m_pxEventListDialog->GetNumSelected();
			for(int i=0;i<iNumSelected;i++)
			{
				int iIndex = m_pxEventListDialog->GetSelection(i);
				Asura_Hash_ID uHash = GetEventHash(iIndex);
				if(uHash) m_xSelectionList.add_entry(uHash);
			}
		}
		if(s_bAutoPreview)
		{
			TestSound();
		}
	}

	if(Asura_UserInput::DebouncedKeyPressed(ASURA_KEYID_ENTER))
	{
		OnOkay();
	}
	if(Asura_UserInput::DebouncedKeyPressed(ASURA_KEYID_ESCAPE))
	{
		OnCancel();
	}

}

void Tools_PickSoundEvent_Overlay::TestSound()
{
	if(!s_pxThis) return;
	StopSound();
	
	const Asura_Sound_Event* pxEvent = Asura_Sound_Event_Storage::GetEventByHash(s_pxThis->m_uSelectedEvent);
	if(!pxEvent) return;

	Asura_Vector_3 xPos = *Asura_Camera::GetPosition();
	Asura_Sound_Event_System::PlayEvent(pxEvent,&s_pxThis->m_iSoundHandle,&xPos);
}

void Tools_PickSoundEvent_Overlay::StopSound()
{
	if(!s_pxThis) return;
	Asura_Sound_Event_System::Stop(s_pxThis->m_iSoundHandle);
}

int Tools_PickSoundEvent_Overlay::GetNumSelected() const
{
	Asura_Assert(m_bMultipleSelection,"Multiple selection not enabled");
	return m_xSelectionList.size();
}

Asura_Hash_ID Tools_PickSoundEvent_Overlay::GetSelection(int iIndex) const
{
	Asura_Assert(m_bMultipleSelection,"Multiple selection not enabled");
	Asura_Assert(iIndex>=0 && iIndex < m_xSelectionList.size(),"Invalid selection index");
	return m_xSelectionList[iIndex];
}

void Tools_PickSoundEvent_Overlay::DeleteEvents()
{
	if(!ShowYesNoOverlay("Delete the selected events?")) return;

	if(m_bMultipleSelection)
	{
		while(m_xSelectionList.size())
		{
			Tools_SoundEvent_System::DeleteEvent(m_xSelectionList.first_entry(),false);
			m_xSelectionList.delete_first_entry();
		}
	}
	else
	{
		Tools_SoundEvent_System::DeleteEvent(m_uSelectedEvent,false);
	}
	//flag the filter as changed so that the deleted entries will be removed.
	m_bFilterChanged = true;
}

float Tools_PickSoundEvent_Overlay::CalculateDropDownListWidth()
{
	//get the max width of all the names that will appear in the drop down lists , so that they can be made the same size
	float fWidth = 0.0f;

	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)
		{
			fWidth = Asura_Maths::Max(Asura_DropDown_List_Dialog::GetWidthForString(szCategory), fWidth);
		}

		const char* szSubCategory = pxSample->GetSoundSubType();
		if(szSubCategory && *szSubCategory)
		{
			fWidth = Asura_Maths::Max(Asura_DropDown_List_Dialog::GetWidthForString(szSubCategory), fWidth);
		}

		const char* szFilename = pxSample->GetFilename();
		if(szFilename && *szFilename)
		{
			fWidth = Asura_Maths::Max(Asura_DropDown_List_Dialog::GetWidthForString(szFilename), fWidth);
		}
	}
	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)
		{
			fWidth = Asura_Maths::Max(Asura_DropDown_List_Dialog::GetWidthForString(szCategory), fWidth);
		}

		const char* szSubCategory = pxEvent->GetEventSubType();
		if(szSubCategory && *szSubCategory)
		{
			fWidth = Asura_Maths::Max(Asura_DropDown_List_Dialog::GetWidthForString(szSubCategory), fWidth);
		}

		const char* szFilename = pxEvent->GetFilename();
		if(szFilename && *szFilename)
		{
			fWidth = Asura_Maths::Max(Asura_DropDown_List_Dialog::GetWidthForString(szFilename), fWidth);
		}
	}
	//get the mixer channel names
	for(u_int i=0;i<Asura_Sound_Mixer_System::GetNumberOfChannels();i++)
	{
		const Asura_Sound_Mixer_Channel* pxChannel = Asura_Sound_Mixer_System::GetChannelByIndex(i);
		const char* szName = pxChannel->GetName();

		if(szName && *szName)
		{
			fWidth = Asura_Maths::Max(Asura_DropDown_List_Dialog::GetWidthForString(szName), fWidth);
		}
	}
	
	return fWidth;
}

void Tools_PickSoundEvent_Overlay::OnFindEventByID()
{
	Asura_Assert(s_pxThis,"");

	Asura_Hash_ID uHashID = ASURA_HASH_ID_UNSET;

	// pop up a modal text box
	GetString_Overlay xModalOverlay( "Enter ID", s_xLastFindEventID.GetString() );	
	
	if( xModalOverlay.DoModal() )
	{
		s_xLastFindEventID = xModalOverlay.GetString();

		// covert text to hash ID
		if( Asura_StringUtil::GetIntValue( xModalOverlay.GetString(), (int*)&uHashID )
		 && (uHashID != ASURA_HASH_ID_UNSET)
		 && Asura_Sound_Event_Storage::FindEvent( uHashID ) )
		{
			// Search for event
			for( int i=0; i<(int)s_pxThis->m_uEventListSize; ++i )
			{
				if( s_pxThis->GetEventHash(i) == uHashID )
				{
					// found event, set selection
					s_pxThis->m_pxEventListDialog->SetSelection(i);
					s_pxThis->m_iEventIndex = i;
					s_pxThis->m_bEventChanged = true;
					return;
				}
			}
		}	

		Message_Overlay xOverlay( "Event not found" );
		xOverlay.DoModal();
	}
}

//eof
