#include "Core/Asura.h"

#include "Misc/Tools_Modal_Overlay.h"

#include "Blueprint/Asura_Blueprint_System.h"
#include "Core/Asura_UserInput.h"
#include "Core/Asura_Timers.h"
#include "Core/Asura_Windows.h"
#include "Debugging/Asura_Assert.h"
#include "Debugging/Asura_CPUProfile_Renderer.h"
#include "Interface/Asura_Cursor.h"
#include "Interface/Asura_Dialog.h"
#include "Interface/Asura_Gadget.h"
#include "Interface/Asura_Interface.h"
#include "Interface/Asura_Key_Combination.h"
#include "Interface/Asura_Overlay.h"
#include "Interface/Asura_Overlay_System.h"
#include "Interface/Asura_Tree_Dialog.h"
#include "Rendering/Asura_DeferredShading.h"
#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_Text_Output.h"
#include "Rendering/Asura_Viewport.h"
#include "Sound/Asura_Audio_Systems_Manager.h"
#include "Sound/Asura_Sound.h"
#include "SpecialFX/Asura_Skybox.h"

#include "Tools_System.h"
#include "Tools_Mode.h"
#include "Blueprint/Tools_Mode_Blueprint.h"
#include "Interface/Tools_WindowsCursor.h"
#include "Load&Save/Tools_BatchProcessing.h"
#include "Misc/Tools_Help.h"
#include "Misc/Tools_Render.h"
#include "Misc/Tools_Timed_Messages.h"
#include "Movement/Tools_Movement.h"
#include "Objects/Tools_Object_System.h"

#include "Rendering/Asura_Direct3D.h"

#include "PC/FileSystem/Tools_FileSystem.h"



///////////////////////////////////////////////////////////////////////////////////////////////////
/*---------------------**
** Tools_Modal_Overlay **
**---------------------*/

bool Tools_Modal_Overlay::s_bActive = false;

Tools_Modal_Overlay::Tools_Modal_Overlay(const Asura_String& xTitle)
{
	m_iID = (int) this;
	m_xTitle = xTitle;
}

void Tools_Modal_Overlay::DoModal(Asura_Overlay* pxOverlay)
{
	Asura_Assert(pxOverlay,"");

	//get previous topmost overlay
	int iTopID = Asura_Overlay_System::GetTopOverlayID();
	
	//flag as always on top
	Asura_Overlay_System::SetAlwaysOnTop(pxOverlay, true);
	pxOverlay->m_bAlwaysOnTop = true;

	//run the overlay
	DoModal(0,pxOverlay);

	//restore original top overlay
	Asura_Overlay_System::BringOverlayToTop(iTopID);

}

void Tools_Modal_Overlay::ShowOverlay()
{
	//Skip all modal overlays when doing batch processes. RAR 23/2/2005
	if(Tools_BatchProcessing::IsBatch()) return;
	
	Asura_Windows::CheckForWindowsMessages();
	Asura_UserInput::Update();

	//create the overlay
	Asura_Overlay* pxNewOverlay;
	pxNewOverlay = new Asura_Overlay(m_xTitle.GetString(),true);

	//get the derived class to intitialise the overlay
	InitialiseOverlay(pxNewOverlay);

	//set the id
	pxNewOverlay->m_uID = m_iID;

	Asura_Overlay_System::SubmitNewOverlay(pxNewOverlay);

	DoModal(this,0);

}

void Tools_Modal_Overlay::DoModal(Tools_Modal_Overlay* pxModalOverlay,Asura_Overlay* pxAsuraOverlay)
{
	Asura_Assert(pxModalOverlay || pxAsuraOverlay,"");

	int iID = pxModalOverlay ? pxModalOverlay->m_iID : pxAsuraOverlay->m_uID;

	s_bActive = true;

	// pause profiling to avoid assert
	ASURA_CPUPROFILE_SCOPED_PAUSE

	//loop while the overlay remains in existence
	while(Asura_Overlay_System::DoesOverlayExist(iID))
	{
		Asura_Windows::CheckForWindowsMessages();

		if(Tools_System::IsActive())
		{
			//update state of middle mouse button
			Tools_Movement::UpdateMouseRotationState();

			Asura_Cursor::EnableCursorMovement(!Tools_Movement::IsUsingMouseRotation());
			Asura_UserInput::Update();
			Asura_Cursor::EnableCursorMovement(true);//(make sure cursor movement is back on for any other 'main loops' that may occur
		}
		else
		{
			Asura_UserInput::Update();
		}

		Asura_Cursor::OncePerFrameUpdate();
		//just updating the overlay system.. don't want the menus to be used while the overlay is up
		Asura_Overlay_System::Update(false);
		Tools_Help::Update();

		if(Tools_System::IsActive())
		{
			//Allow movement updates , as long as it's the camera
			if(!Tools_Movement::GetFlyingObject())
			{
				Tools_Movement::Update();
			}
			Tools_WindowsCursor::Update();
		}


		//if(Asura_Windows::IsApplicationActive())
		{
			if(Tools_System::IsActive())
			{
				Tools_System::Render();
			}
			else
			{
				Asura_Render::BeginFrame();
				Asura_Skybox::Render();
				Asura_Render::CurrentStates.RequestWireFrameMode(false);
				Asura_Overlay_System::Render();
				Asura_Cursor::Render();
				Asura_Render::EndFrame();
			}
		}

		#if defined ASURA_USE_AUDIO_SYSTEM
		Asura_Audio_Systems_Manager::Update();
		#else
		Asura_Sound_System::Update();
		#endif
	
		Asura_Timers::HandlePrimaryTimer();
		if(Asura_Overlay_System::DoesOverlayExist(iID))
		{
			if(pxModalOverlay)
			{
				pxModalOverlay->Update();
			}
			if(pxAsuraOverlay && !pxAsuraOverlay->m_bAlwaysOnTop)
			{
				break;
			}
		}
		if(Tools_System::ShouldQuitToDesktop())
		{
			Asura_Overlay_System::CloseOverlays(iID);
		}
	}


	//Ensure that any text entry gadgets no longer have focus.
	//(Otherwise they may try using a no longer valid pointer).
	Asura_Overlay_System::ReleaseFocus();

	//Update input one final time in an attempt to debounce any key press that
	//may have been used to close the overlay.
	Asura_Windows::CheckForWindowsMessages();
	Asura_UserInput::Update();

	s_bActive = false;
}

void Tools_Modal_Overlay::RebuildOverlay()
{
	Asura_Overlay_System::CloseOverlays(m_iID);

	//create the overlay
	Asura_Overlay* pxNewOverlay;
	pxNewOverlay = new Asura_Overlay(m_xTitle.GetString(),true);

	//get the derived class to intitialise the overlay
	InitialiseOverlay(pxNewOverlay);

	//set the id
	pxNewOverlay->m_uID = m_iID;

	Asura_Overlay_System::SubmitNewOverlay(pxNewOverlay);
}

Asura_Overlay* Tools_Modal_Overlay::GetOverlay()
{
	return Asura_Overlay_System::GetOverlay(m_iID);
}

void Tools_Modal_Overlay::CloseOverlay()
{
	Asura_Overlay_System::CloseOverlays(m_iID);
}

////////////////////////////////////////////////////////////////////////////////////////////////
/*---------------------------**
** Tools_BaseMessage_Overlay **
**---------------------------*/
Tools_BaseMessage_Overlay::Tools_BaseMessage_Overlay(const Asura_String& xText)
:Tools_Modal_Overlay(" ")
{
	m_xText = xText;
}

void Tools_BaseMessage_Overlay::InitialiseOverlay(class Asura_Overlay* pxOverlay)
{
	const int iMaxLineLength = 60;

	//setup the controls on the overlay
	Asura_Vector_2 xOffset = Asura_Vector_2(0.5f,0.5f);
	Asura_Dialog* pxDialog;

	//do word wrapping on the text if its too long
	const char* pszText = m_xText.GetString();
	while(strlen(pszText))
	{
		char szBuffer[iMaxLineLength+1];
		
		int iLastSpace = -1;
		int iLineLength = -1;
		//search for a point to split the line , either a new line character , or the last space
		for(int i=0;i<=iMaxLineLength;i++)
		{
			if(pszText[i] == 0 || pszText[i] == '\n')
			{
				iLineLength = i;
				break;
			}


			if(pszText[i] == ' ')
			{
				iLastSpace = i;
			}
		}

		if(iLineLength == -1)
		{
			if(iLastSpace>iMaxLineLength/2)
			{
				iLineLength = iLastSpace;
			}
			else
			{
				iLineLength = iMaxLineLength;
			}
		}

		strncpy(szBuffer,pszText,iLineLength);
		szBuffer[iLineLength] = 0;

		//add dialog control
		pxDialog = new Asura_Dialog(szBuffer,xOffset);
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=1.0f;

		pszText += iLineLength;
		if(*pszText == ' ' || *pszText == '\n')
		{
			//line was split on a new line character , or a space.
			//skip it
			pszText++;
		}


	}
	xOffset.x = 1.0f;
	xOffset.y += 1.5f;

	InitialiseOverlay(pxOverlay,xOffset);

}

////////////////////////////////////////////////////////////////////////////////////////////////
/*---------------**
** YesNo_Overlay **
**---------------*/

bool YesNo_Overlay::ReturnValue;

YesNo_Overlay::YesNo_Overlay(Asura_String xQuestion)
:Tools_BaseMessage_Overlay(xQuestion)
{
}

bool YesNo_Overlay::DoModal()
{
	ShowOverlay();
	return ReturnValue;
}

void YesNo_Overlay::InitialiseOverlay(class Asura_Overlay* pxOverlay,Asura_Vector_2& xOffset)
{
	Asura_Dialog* pxDialog;

	pxDialog = new Asura_FunctionWithKeybinding_Dialog("(Y)es",xOffset,YesSelected,'y',&pxOverlay->m_bShouldClose);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=2.0f;

	pxDialog = new Asura_FunctionWithKeybinding_Dialog("(N)o",xOffset,NoSelected,'n',&pxOverlay->m_bShouldClose);
	pxOverlay->AddDialog(pxDialog);
}

bool ShowYesNoOverlay(const char* pszFormat,...)
{
	char szBuffer[1025];

	//Get the string to be shown
	va_list ap;
	va_start(ap,pszFormat);
    Asura_PrintfToBfr( szBuffer, pszFormat, ap, sizeof(szBuffer), true );
	va_end(ap);

	//create the overlay , and show it
	Asura_String xTitle = szBuffer;
	YesNo_Overlay xOverlay(xTitle);
	return xOverlay.DoModal();

}

////////////////////////////////////////////////////////////////////////////////////////////////
/*---------------**
** TrueFalse_Overlay **
**---------------*/

TrueFalse_Overlay::TrueFalse_Overlay(Asura_String xQuestion)
:YesNo_Overlay(xQuestion)
{
}

void TrueFalse_Overlay::InitialiseOverlay(class Asura_Overlay* pxOverlay,Asura_Vector_2& xOffset)
{
    Asura_Dialog* pxDialog;

    pxDialog = new Asura_FunctionWithKeybinding_Dialog("(T)rue",xOffset,YesSelected,'t',&pxOverlay->m_bShouldClose);
    pxOverlay->AddDialog(pxDialog);
    xOffset.y+=2.0f;

    pxDialog = new Asura_FunctionWithKeybinding_Dialog("(F)alse",xOffset,NoSelected,'f',&pxOverlay->m_bShouldClose);
    pxOverlay->AddDialog(pxDialog);
}

////////////////////////////////////////////////////////////////////////////////////////////////
/*---------------------**
** YesNoCancel_Overlay **
**---------------------*/

bool YesNoCancel_Overlay::ReturnValue;
bool YesNoCancel_Overlay::SuccessValue;

YesNoCancel_Overlay::YesNoCancel_Overlay(Asura_String xQuestion)
:Tools_BaseMessage_Overlay(xQuestion)
{
	SuccessValue=true;
}

bool YesNoCancel_Overlay::DoModal()
{
	ShowOverlay();
	return SuccessValue;
}


bool YesNoCancel_Overlay::GetResult()
{
	return ReturnValue;
}

void YesNoCancel_Overlay::InitialiseOverlay(class Asura_Overlay* pxOverlay,Asura_Vector_2& xOffset)
{
	Asura_Dialog* pxDialog;

	pxDialog = new Asura_FunctionWithKeybinding_Dialog("(Y)es",xOffset,YesSelected,'y',&pxOverlay->m_bShouldClose);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=2.0f;

	pxDialog = new Asura_FunctionWithKeybinding_Dialog("(N)o",xOffset,NoSelected,'n',&pxOverlay->m_bShouldClose);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=2.0f;

	pxDialog = new Asura_FunctionWithKeybinding_Dialog("(C)ancel",xOffset,CancelSelected,'c',&pxOverlay->m_bShouldClose);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=2.0f;
}

////////////////////////////////////////////////////////////////////////////////////////////////
/*---------------------**
** TrueFalseCancel_Overlay **
**---------------------*/

TrueFalseCancel_Overlay::TrueFalseCancel_Overlay(Asura_String xQuestion)
:YesNoCancel_Overlay(xQuestion)
{
    SuccessValue=true;
}

void TrueFalseCancel_Overlay::InitialiseOverlay(class Asura_Overlay* pxOverlay,Asura_Vector_2& xOffset)
{
    Asura_Dialog* pxDialog;

    pxDialog = new Asura_FunctionWithKeybinding_Dialog("(T)rue",xOffset,YesSelected,'t',&pxOverlay->m_bShouldClose);
    pxOverlay->AddDialog(pxDialog);
    xOffset.y+=2.0f;

    pxDialog = new Asura_FunctionWithKeybinding_Dialog("(F)alse",xOffset,NoSelected,'f',&pxOverlay->m_bShouldClose);
    pxOverlay->AddDialog(pxDialog);
    xOffset.y+=2.0f;

    pxDialog = new Asura_FunctionWithKeybinding_Dialog("(C)ancel",xOffset,CancelSelected,'c',&pxOverlay->m_bShouldClose);
    pxOverlay->AddDialog(pxDialog);
    xOffset.y+=2.0f;
}

////////////////////////////////////////////////////////////////////////////////////////////////
/*------------------**
** YesNoAll_Overlay **
**------------------*/

YesNoAll_Overlay* YesNoAll_Overlay::s_pxThis = 0;

YesNoAll_Overlay::YesNoAll_Overlay(Asura_String xQuestion)
:Tools_BaseMessage_Overlay(xQuestion)
{
}

bool YesNoAll_Overlay::DoModal()
{
	m_bAll = false;
	s_pxThis = this;
	ShowOverlay();
	s_pxThis = 0;
	return m_bReturnValue;
}

void YesNoAll_Overlay::InitialiseOverlay(class Asura_Overlay* pxOverlay,Asura_Vector_2& xOffset)
{
	Asura_Dialog* pxDialog;


	pxDialog = new Asura_FunctionWithKeybinding_Dialog("(Y)es",xOffset,_YesSelected,'y',&pxOverlay->m_bShouldClose);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=2.0f;

	pxDialog = new Asura_FunctionWithKeybinding_Dialog("(N)o",xOffset,_NoSelected,'n',&pxOverlay->m_bShouldClose);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y-=2.0f;
	xOffset.x+=15.0f;
	
	pxDialog = new Asura_Function_Dialog("Yes All",xOffset,_YesAllSelected,&pxOverlay->m_bShouldClose);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=2.0f;

	pxDialog = new Asura_Function_Dialog("No All",xOffset,_NoAllSelected,&pxOverlay->m_bShouldClose);
	pxOverlay->AddDialog(pxDialog);

}



////////////////////////////////////////////////////////////////////////////////////////////////
/*-----------------**
** Message_Overlay **
**-----------------*/

Message_Overlay::Message_Overlay(Asura_String xMessage)
:Tools_BaseMessage_Overlay(xMessage)
{
}

void Message_Overlay::DoModal()
{
	ShowOverlay();
}

void Message_Overlay::InitialiseOverlay(class Asura_Overlay* pxOverlay,Asura_Vector_2& xOffset)
{
	Asura_Dialog* pxDialog;
	pxDialog = new Asura_FunctionWithKeybinding_Dialog("Ok",xOffset,0,ASURA_KEYID_ENTER,&pxOverlay->m_bShouldClose);
	pxOverlay->AddDialog(pxDialog);
}

void ShowMessageOverlay(const char* pszFormat,...)
{
	char szBuffer[1024];

	//Get the string to be shown
	va_list ap;
	va_start(ap,pszFormat);
    Asura_PrintfToBfr( szBuffer, pszFormat, ap, sizeof(szBuffer), true );
	va_end(ap);

	//create the message overlay , and show it
	Asura_String xTitle = szBuffer;
	Message_Overlay xOverlay(xTitle);
	xOverlay.DoModal();

}

////////////////////////////////////////////////////////////////////////////////////////////////
/*-------------------**
** GetString_Overlay **
**-------------------*/

bool GetString_Overlay::ReturnValue;

GetString_Overlay::GetString_Overlay(Asura_String _Title,const char* InitialString,int iMaxLength)
:Tools_Modal_Overlay(_Title)
{
	Asura_Assert(iMaxLength > 0 , "");
	if(InitialString)
	{
		String = InitialString;
	}
	m_iMaxLength = iMaxLength;
	m_bDisableCancel = false;
}


bool GetString_Overlay::DoModal()
{
	m_bInputDebounced = !Asura_UserInput::DebouncedHasAnyKeyBeenPressed();
	ShowOverlay();
	return ReturnValue;
}

char* GetString_Overlay::GetString()
{
	return String.GetString();
}

void GetString_Overlay::InitialiseOverlay(class Asura_Overlay* Overlay)
{
	//setup the controls on the overlay
	Asura_Vector_2 offset = Asura_Vector_2(1.0f,3.0f);
	Asura_Dialog* newDialog;
	Asura_String_Dialog* pxStringDialog;


	int iBoxLength = Asura_Maths::Min(40,m_iMaxLength);
	newDialog = pxStringDialog = new Asura_String_Dialog("",offset,&String,iBoxLength,0,true);
	if(m_iMaxLength > iBoxLength)
	{
		pxStringDialog->SetMaxStringLength(m_iMaxLength);
	}
	Overlay->AddDialog(newDialog);
	offset.y+=2.0f;

	newDialog = new Asura_Function_Dialog("Okay",offset,OkaySelected,&Overlay->m_bShouldClose);
	Overlay->AddDialog(newDialog);
	offset.y+=2.0f;

	newDialog = new Asura_Function_Dialog("Cancel",offset,CancelSelected,&Overlay->m_bShouldClose);
	newDialog->SetEnabled(!m_bDisableCancel);
	Overlay->AddDialog(newDialog);

}

void GetString_Overlay::Update()
{
	if(m_bInputDebounced)
	{
		if(Asura_UserInput::DebouncedKeyPressed(ASURA_KEYID_ENTER))
		{
			GetOverlay()->m_bShouldClose = true;			
			OkaySelected();
			return;
		}
		if(Asura_UserInput::DebouncedKeyPressed(ASURA_KEYID_ESCAPE) && !m_bDisableCancel)
		{
			GetOverlay()->m_bShouldClose = true;			
			CancelSelected();
			return;
		}
	}
	else
	{
		m_bInputDebounced = !Asura_UserInput::DebouncedHasAnyKeyBeenPressed();
	}		
}

////////////////////////////////////////////////////////////////////////////////////////////////
/*-------------------**
** GetNumber_Overlay **
**-------------------*/

bool GetNumber_Overlay::ReturnValue;

GetNumber_Overlay::GetNumber_Overlay(Asura_String title,float initialValue,float min,float max,const char* pszFormat /*= 0*/)
:Tools_Modal_Overlay(title)
{
	Value = initialValue;
	Min = min;
	Max = max;
	m_xFormat = pszFormat ? pszFormat : "%.3f";
}

bool GetNumber_Overlay::DoModal()
{
	m_bInputDebounced = !Asura_UserInput::DebouncedHasAnyKeyBeenPressed();
	ShowOverlay();
	return ReturnValue;
}


void GetNumber_Overlay::InitialiseOverlay(class Asura_Overlay* Overlay)
{
	//setup the controls on the overlay
	Asura_Vector_2 offset = Asura_Vector_2(1.0f,3.0f);
	Asura_Dialog* newDialog;

	newDialog = new Asura_Numerical_Dialog("",offset,&Value,Min,Max,1.0f,m_xFormat.GetString());
	((Asura_Numerical_Dialog*)newDialog)->SetDefaultFocus(true);
	Overlay->AddDialog(newDialog);
	offset.y+=2.0f;

	newDialog = new Asura_Function_Dialog("Okay",offset,OkaySelected,&Overlay->m_bShouldClose);
	Overlay->AddDialog(newDialog);
	offset.y+=2.0f;

	newDialog = new Asura_Function_Dialog("Cancel",offset,CancelSelected,&Overlay->m_bShouldClose);
	Overlay->AddDialog(newDialog);

}

void GetNumber_Overlay::Update()
{
	if(m_bInputDebounced)
	{
		if(Asura_UserInput::DebouncedKeyPressed(ASURA_KEYID_ENTER))
		{
			GetOverlay()->m_bShouldClose = true;			
			OkaySelected();
			return;
		}
		if(Asura_UserInput::DebouncedKeyPressed(ASURA_KEYID_ESCAPE))
		{
			GetOverlay()->m_bShouldClose = true;			
			CancelSelected();
			return;
		}
	}
	else
	{
		m_bInputDebounced = !Asura_UserInput::DebouncedHasAnyKeyBeenPressed();
	}		
}


////////////////////////////////////////////////////////////////////////////////////////////////
/*----------------------**
** GetIntNumber_Overlay **
**----------------------*/

bool GetIntNumber_Overlay::ReturnValue;

GetIntNumber_Overlay::GetIntNumber_Overlay(Asura_String title,int initialValue,int min,int max)
:Tools_Modal_Overlay(title)
{
	Value = initialValue;
	Min = min;
	Max = max;
}

bool GetIntNumber_Overlay::DoModal()
{
	m_bInputDebounced = !Asura_UserInput::DebouncedHasAnyKeyBeenPressed();
	ShowOverlay();
	return ReturnValue;
}

void GetIntNumber_Overlay::InitialiseOverlay(class Asura_Overlay* Overlay)
{
	//setup the controls on the overlay
	Asura_Vector_2 offset = Asura_Vector_2(1.0f,3.0f);
	Asura_Dialog* newDialog;

	newDialog = new Asura_Numerical_Dialog("",offset,&Value,Min,Max,1,"%d");
	((Asura_Numerical_Dialog*)newDialog)->SetDefaultFocus(true);
	Overlay->AddDialog(newDialog);
	offset.y+=2.0f;

	newDialog = new Asura_Function_Dialog("Okay",offset,OkaySelected,&Overlay->m_bShouldClose);
	Overlay->AddDialog(newDialog);
	offset.y+=2.0f;

	newDialog = new Asura_Function_Dialog("Cancel",offset,CancelSelected,&Overlay->m_bShouldClose);
	Overlay->AddDialog(newDialog);

}

void GetIntNumber_Overlay::Update()
{
	if(m_bInputDebounced)
	{
		if(Asura_UserInput::DebouncedKeyPressed(ASURA_KEYID_ENTER))
		{
			GetOverlay()->m_bShouldClose = true;			
			OkaySelected();
			return;
		}
		if(Asura_UserInput::DebouncedKeyPressed(ASURA_KEYID_ESCAPE))
		{
			GetOverlay()->m_bShouldClose = true;			
			CancelSelected();
			return;
		}
	}
	else
	{
		m_bInputDebounced = !Asura_UserInput::DebouncedHasAnyKeyBeenPressed();
	}		
}


////////////////////////////////////////////////////////////////////////////////////////////////
/*------------------**
** GetFlags_Overlay **
**------------------*/

bool GetFlags_Overlay::s_bReturnValue = false;

GetFlags_Overlay::GetFlags_Overlay( const Asura_String& xTitle )
: Tools_Modal_Overlay( xTitle ),
  m_pxNameFlagBits( NULL ),
  m_uFlags( 0 )
{
}

GetFlags_Overlay::~GetFlags_Overlay()
{
    while ( m_pxNameFlagBits != NULL )
    {
        NameFlagBit* pxToDelete = m_pxNameFlagBits;
        m_pxNameFlagBits = m_pxNameFlagBits->m_pxNext;
        delete pxToDelete;
    }
}

void GetFlags_Overlay::AddItem( const char* pszName, int iFlagBit )
{
    NameFlagBit* pxNewEntry = new NameFlagBit( pszName, iFlagBit );
    if ( pxNewEntry != NULL )
    {
        pxNewEntry->m_pxNext = m_pxNameFlagBits;
        m_pxNameFlagBits     = pxNewEntry;
    }
}

bool GetFlags_Overlay::DoModal()
{
    s_bReturnValue = false;
    ShowOverlay();
    return s_bReturnValue;
}

void GetFlags_Overlay::InitialiseOverlay( Asura_Overlay* pxOverlay )
{
    Asura_Vector_2 xOffset = Asura_Vector_2( 1.0f, 3.0f );

    Asura_Dialog* pxDialog;
    NameFlagBit*  pxNameFlagBit = m_pxNameFlagBits;
    while ( pxNameFlagBit != NULL )
    {
        pxDialog = new Asura_FlagTickbox_Dialog( pxNameFlagBit->m_pszName, xOffset, &m_uFlags, pxNameFlagBit->m_iFlagBit );
        pxOverlay->AddDialog( pxDialog );
        xOffset.y += Asura_Dialog::DefaultSpacing();

        pxNameFlagBit = pxNameFlagBit->m_pxNext;
    }

    Asura_Dialog* pxOKDialog = new Asura_FunctionWithKeybinding_Dialog( "Okay", xOffset, OkaySelected, ASURA_KEYID_ENTER, &pxOverlay->m_bShouldClose );
    pxOverlay->AddDialog( pxOKDialog );
    xOffset.x += 10.0f;
    Asura_Dialog* pxCancelDialog = new Asura_FunctionWithKeybinding_Dialog( "Cancel", xOffset, CancelSelected, ASURA_KEYID_ESCAPE, &pxOverlay->m_bShouldClose );
    pxOverlay->AddDialog( pxCancelDialog );
}

////////////////////////////////////////////////////////////////////////////////////////////////
/*-----------------------**
** ListSelection_Overlay **
**-----------------------*/

bool                   ListSelection_Overlay::s_bReturnValue;
ListSelection_Overlay* ListSelection_Overlay::s_pxThis = NULL;

ListSelection_Overlay::ListSelection_Overlay( const Asura_String& xTitle )
    : Tools_Modal_Overlay( xTitle )
{
	m_ppcStrings  = 0;
	m_ppcHelpStrings = 0;
	m_iNumLines   = 0;
	m_piIndexConv = 0;
	m_bSort = true;
}

ListSelection_Overlay::~ListSelection_Overlay()
{
	Reset();
}

void ListSelection_Overlay::Reset()
{
	while( m_xItems.size() )
	{
		delete m_xItems.first_entry();
		m_xItems.delete_first_entry();
	}

	delete[] m_ppcStrings;
	m_ppcStrings = NULL;
	delete[] m_ppcHelpStrings;
	m_ppcHelpStrings = NULL;
	delete[] m_piIndexConv;
	m_piIndexConv = 0;

	m_iNumLines = 0;
}

void ListSelection_Overlay::AddItem( const char* szString, int iData, bool bIsSelectedInitially, const char* szHelpString/* = NULL  */, bool bDontAddIfDuplicateString/* = false*/)
{
	if (bDontAddIfDuplicateString)
	{
		LIF<ListSelectionItem*> ilif( &m_xItems );
		for(;!ilif.done();ilif.next())
		{
			Asura_String xTemp(szString);
			if( xTemp.Compare(ilif()->xString, true) == 0 )
			{
				return;
			}
		}		
	}
	ListSelectionItem* pxNewItem = new ListSelectionItem();

	pxNewItem->xString            = szString;
	pxNewItem->xHelpString = szHelpString ? szHelpString : "";
	pxNewItem->iData              = iData;
	pxNewItem->bSelectedInitially = bIsSelectedInitially;
	pxNewItem->bHidden            = false;
	pxNewItem->bTicked            = false;

	//add it into the list (sorted)
	if(m_bSort)
	{
		LIF<ListSelectionItem*> ilif( &m_xItems );
		for(;!ilif.done();ilif.next())
		{
			if( pxNewItem->xString.Compare(ilif()->xString, true) <= 0 )
			{
				m_xItems.add_entry_before( pxNewItem, ilif() );
				break;
			}
		}
		if(ilif.done())
		{
			m_xItems.add_entry( pxNewItem );
		}
	}
	else
	{
		m_xItems.add_entry( pxNewItem );
	}
}

bool ListSelection_Overlay::DoModal( void )
{
	m_szInput[0] = 0;
	m_fInputTimer = 0;
	
	s_pxThis = this;
	ShowOverlay();
	s_pxThis = 0;

	return s_bReturnValue;
}

void ListSelection_Overlay::InitialiseOverlay( Asura_Overlay* pxOverlay )
{
	delete[] m_piIndexConv;
	delete[] m_ppcStrings;
	delete[] m_ppcHelpStrings;

    int iNumItems = m_xItems.size();
    if( iNumItems > 0 )
    {
	    m_ppcStrings  = new const char*[iNumItems];
		m_ppcHelpStrings = new const char*[iNumItems];
	    m_piIndexConv = new int[iNumItems];

	    m_iSelection = 0;
	    for( int i = 0; i < iNumItems; ++i )
	    {
		    m_ppcStrings[i]  = m_xItems[i]->xString.GetString();
			m_ppcHelpStrings[i] = m_xItems[i]->xHelpString.GetString();
		    m_piIndexConv[i] = i;

		    if( m_xItems[i]->bSelectedInitially )
		    {
			    m_iSelection = i;
		    }
	    }
    }
    else
    {
        m_ppcStrings  = new const char*[1];
		m_ppcHelpStrings = new const char*[1];
        m_piIndexConv = new int[1];

        m_ppcStrings[0]  = GetEmptyListString();
		m_ppcHelpStrings[0] = GetEmptyListString();
        m_piIndexConv[0] = -1;

    	m_iSelection = -1;
        iNumItems    = 1;
    }

	m_iListSelection = 0;
    m_iNumLines      = Asura_Maths::Min( iNumItems, 15 );
	
	//setup the controls on the overlay
	Asura_Vector_2 xOffset = Asura_Vector_2(1.0f,3.0f);

	const char* pszHeading = GetListHeadingText();
	if(pszHeading)
	{
		Asura_Dialog* pxDialog = new Asura_Dialog(pszHeading,xOffset);
		pxOverlay->AddDialog(pxDialog);
		xOffset.y += Asura_Dialog::SmallSpacing();
	}

	m_pxInputDialog = new Asura_Dialog( "", xOffset );
	pxOverlay->AddDialog( m_pxInputDialog );
	xOffset.y += Asura_Dialog::SmallSpacing();

	if(IsTickboxList())
	{
		m_pxListDialog = new Asura_List_Dialog( xOffset, m_ppcStrings,&m_iListSelection, m_iNumLines, iNumItems, _IsTicked, _ToggleTicked,(int) this );
	}
	else
	{
		m_pxListDialog = new Asura_List_Dialog( xOffset, m_ppcStrings,&m_iListSelection, m_iNumLines, iNumItems, NULL );
	}
	m_pxListDialog->SetListHelpStrings(m_ppcHelpStrings);
	m_pxListDialog->SetDoubleClickFunction(_DoubleClicked,(int)this);
	pxOverlay->AddDialog( m_pxListDialog );
	xOffset.y = m_pxListDialog->m_xMaxCoord.y + 1.0f;

	if (IsListResizable())
	{
		Asura_Dialog* pxResize = new Asura_Resize_Dialog(m_pxListDialog, GetResizableUserSize());
		pxOverlay->AddDialog(pxResize);
	}

	if(IsTickboxList())
	{
		//add extra buttons for tickbox list
		const char* pszButtonText = GetTickAllButtonText();
		if(pszButtonText)
		{
			Asura_Dialog* pxDialog = new Asura_Function_Dialog(pszButtonText,xOffset,_TickAll,(int)this);
			pxOverlay->AddDialog(pxDialog);
			xOffset.y+=Asura_Dialog::DefaultSpacing();
		}

		pszButtonText = GetTickNoneButtonText();
		if(pszButtonText)
		{
			Asura_Dialog* pxDialog = new Asura_Function_Dialog(pszButtonText,xOffset,_TickNone,(int)this);
			pxOverlay->AddDialog(pxDialog);
			xOffset.y+=Asura_Dialog::DefaultSpacing();
		}

		pszButtonText = GetInvertButtonText();
		if(pszButtonText)
		{
			Asura_Dialog* pxDialog = new Asura_Function_Dialog(pszButtonText,xOffset,_InvertTicks,(int)this);
			pxOverlay->AddDialog(pxDialog);
			xOffset.y+=Asura_Dialog::DefaultSpacing();
		}
		xOffset.y += 1.0f;
	}

	//add derived class controls
	AddExtraOverlayControls( pxOverlay, xOffset );

    const char* szOKText = GetOKButtonText();
    if( szOKText )
    {
	    Asura_Dialog* pxOKDialog = new Asura_FunctionWithKeybinding_Dialog( szOKText, xOffset, OkaySelected, ASURA_KEYID_ENTER, &pxOverlay->m_bShouldClose );
	    pxOverlay->AddDialog( pxOKDialog );
	    xOffset.x += 10.0f;
    }

    const char* szCancelText = GetCancelButtonText();
    if( szCancelText )
    {
	    Asura_Dialog* pxCancelDialog = new Asura_FunctionWithKeybinding_Dialog( szCancelText, xOffset, CancelSelected, ASURA_KEYID_ESCAPE, &pxOverlay->m_bShouldClose );
	    pxOverlay->AddDialog( pxCancelDialog );
    }

	UpdateStringList();

	if (IsListResizable())
	{
		pxOverlay->MakeDialogsRelativeToResizableDialog();
	}
}

void ListSelection_Overlay::UpdateStringList( void )
{
    if( !m_xItems.size() ) { return; }

	//update the stirng list to show the visible entries
	m_iNumVisible = 0;
	int iSelected = 0;
	for( int i = 0; i < m_xItems.size(); ++i )
	{
        if( m_xItems[i]->bHidden ) { continue; }

		if(m_iSelection == i)
		{
			iSelected = m_iNumVisible;
		}
		
		m_ppcStrings[m_iNumVisible]  = m_xItems[i]->xString.GetString();
		m_ppcHelpStrings[m_iNumVisible] = m_xItems[i]->xHelpString.GetString();
		m_piIndexConv[m_iNumVisible] = i;

		m_iNumVisible++;
	}

	if(!m_iNumVisible)
	{
		m_ppcStrings[0]  = "<All Hidden>";
		m_ppcHelpStrings[0] = GetEmptyListString();
		m_piIndexConv[0] = -1;
		m_iNumVisible++;
	}

	//update the list dialog
	m_pxListDialog->ReplaceStrings( m_iNumVisible, m_ppcStrings );
	m_pxListDialog->SetListHelpStrings(m_ppcHelpStrings);

	//set the selection
	m_iListSelection = iSelected;

	m_iSelection = m_piIndexConv[m_iListSelection];
}

void ListSelection_Overlay::UpdateVisibilities( void )
{
    if( !m_xItems.size() ) { return; }

	//check which items should be shown
	for(int i=0;i<m_xItems.size();i++)
	{
		m_xItems[i]->bHidden = ShouldItemBeHidden( m_xItems[i]->xString.GetString(), m_xItems[i]->iData, m_xItems[i]->bHidden );
	}
	
	//rebuild the string list
	m_iSelection = m_piIndexConv[m_iListSelection];
	UpdateStringList();
}

const char* ListSelection_Overlay::GetSelectionString( void ) const
{
    if( m_iSelection < 0 ) { return NULL; }

	Asura_Assert( m_iSelection < m_xItems.size(), "" );
	return m_xItems[m_iSelection]->xString.GetString();
}

int ListSelection_Overlay::GetSelectionData( void ) const
{
    if( m_iSelection < 0 ) { return 0; }

	Asura_Assert( m_iSelection < m_xItems.size(), "" );
	return m_xItems[m_iSelection]->iData;
}

bool ListSelection_Overlay::SetSelectionByData( int iData )
{
    if( !m_xItems.size() ) { return false; }

	//search for a match
	for(int i=0;i<m_iNumVisible;i++)
	{
        if( m_piIndexConv[i] < 0 ) { continue; }
		
		if( m_xItems[m_piIndexConv[i]]->iData == iData )
		{
			//select this one
			m_iListSelection = i;
			return true;
		}
	}

    return false;
}

void ListSelection_Overlay::Update( void )
{
    if( !m_xItems.size() ) { return; }

	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
		bool bChanged	= false;
		int iLength		= (int)strlen( m_szInput );
		char cKey		= Asura_Gadget::GetAsciiKeyPressed();
		if( cKey == 0 )
		{
			// BACKSPACE pressed?
			if( Asura_UserInput::DebouncedKeyPressed( ASURA_KEYID_BACKSPACE ) )
			{
				if( iLength > 0 )
				{
					iLength--;
					m_szInput[ iLength ] = 0;
					bChanged = true;
				}
			}
		}
		else
		{
			if( iLength < sizeof( m_szInput ) - 1 )
			{
				m_szInput[iLength++] = cKey;
				m_szInput[iLength++] = 0;
				bChanged = true;
			}
		}

		if( bChanged )
		{
			m_fInputTimer = 2.5f;
			m_pxInputDialog->UpdateLabel( m_szInput );

			//advance list to first entry greater than the current input string
			for(int i=0;i<m_iNumVisible;i++)
			{
				int iIndex = m_piIndexConv[i];
				if(iIndex<0) continue;

				Asura_Assert(m_xItems[iIndex]->xString.GetString(), "NULL string in list!");
				if( stricmp(m_xItems[iIndex]->xString.GetString(), m_szInput) >= 0 )
				{
					//move selection to this
					m_iListSelection = i;

					break;
				}

			}
		}
	}

	m_iSelection = m_piIndexConv[m_iListSelection];
}

bool ListSelection_Overlay::Contains( int iData ) const
{
    for( CLIF<ListSelectionItem*> xIt(&m_xItems); !xIt.done(); xIt.next() )
    {
        if( xIt()->iData == iData ) { return true; }
    }
    return false;
}

const char* ListSelection_Overlay::GetCancelButtonText( void ) const
{
    return "Cancel";
}

const char* ListSelection_Overlay::GetEmptyListString( void ) const
{
    return "<no items in list>";
}

const char* ListSelection_Overlay::GetOKButtonText( void ) const
{
    return "Okay";
}

void ListSelection_Overlay::SetItemTicked(int iData,bool bTicked)
{
	for(LIF<ListSelectionItem*> xItemLIF(&m_xItems);!xItemLIF.done();xItemLIF.next())
	{
		if(xItemLIF()->iData == iData)
		{
			xItemLIF()->bTicked = bTicked;
			return;
		}
	}
	Asura_Assert(0,"Failed to find item in list overlay");
}

void ListSelection_Overlay::SetItemTicked(const char* pszName,bool bTicked)
{
	for(LIF<ListSelectionItem*> xItemLIF(&m_xItems);!xItemLIF.done();xItemLIF.next())
	{
		if(!stricmp(xItemLIF()->xString.GetString(),pszName))
		{
			xItemLIF()->bTicked = bTicked;
			return;
		}
	}
	Asura_Assert(0,"Failed to find item in list overlay");
}

const char* ListSelection_Overlay::GetItemName(int iIndex) const
{
	Asura_Assert(iIndex>=0 && iIndex<m_xItems.size(),"");
	return m_xItems[iIndex]->xString.GetString();
}

int ListSelection_Overlay::GetItemData(int iIndex) const
{
	Asura_Assert(iIndex>=0 && iIndex<m_xItems.size(),"");
	return m_xItems[iIndex]->iData;
}

bool ListSelection_Overlay::GetItemTicked(int iIndex) const
{
	Asura_Assert(iIndex>=0 && iIndex<m_xItems.size(),"");
	return m_xItems[iIndex]->bTicked;
}

const char* ListSelection_Overlay::GetItemNameByData( int iData ) const
{
	for( CLIF<ListSelectionItem*> xIt(&m_xItems); !xIt.done(); xIt.next() )
	{
		if ( xIt()->iData == iData )
		{
			return xIt()->xString.GetString();
		}
	}
	return NULL;
}

bool ListSelection_Overlay::IsTicked(int iIndex)
{
	int iListIndex = m_piIndexConv[iIndex];
	return m_xItems[iListIndex]->bTicked;
}

void ListSelection_Overlay::ToggleTicked(int iIndex)
{
	int iListIndex = m_piIndexConv[iIndex];
	m_xItems[iListIndex]->bTicked = !m_xItems[iListIndex]->bTicked;
}

void ListSelection_Overlay::TickAll()
{
	//Going to skip any hidden items for now , not sure what the desirable behaviour 
	//would be , but this seems as good as any.

	for(LIF<ListSelectionItem*> xItemLIF(&m_xItems);!xItemLIF.done();xItemLIF.next())
	{
		if(!xItemLIF()->bHidden)
		{
			xItemLIF()->bTicked = true;
		}
	}
}

void ListSelection_Overlay::TickNone()
{
	//Going to skip any hidden items for now , not sure what the desirable behaviour 
	//would be , but this seems as good as any.

	for(LIF<ListSelectionItem*> xItemLIF(&m_xItems);!xItemLIF.done();xItemLIF.next())
	{
		if(!xItemLIF()->bHidden)
		{
			xItemLIF()->bTicked = false;
		}
	}
}

void ListSelection_Overlay::InvertTicks()
{
	//Going to skip any hidden items for now , not sure what the desirable behaviour 
	//would be , but this seems as good as any.

	for(LIF<ListSelectionItem*> xItemLIF(&m_xItems);!xItemLIF.done();xItemLIF.next())
	{
		if(!xItemLIF()->bHidden)
		{
			xItemLIF()->bTicked = !xItemLIF()->bTicked;
		}
	}
}

void ListSelection_Overlay::DoubleClicked()
{
	CloseOverlay();
	OkaySelected();
}

void ListSelection_Overlay::SetInitialSelection(int iData)
{
	for(LIF<ListSelectionItem*> xItemLIF( &m_xItems );!xItemLIF.done();xItemLIF.next())
	{
		xItemLIF()->bSelectedInitially = (xItemLIF()->iData == iData);
	}
}

void ListSelection_Overlay::SetInitialSelection(const Asura_String& xString)
{
	for(LIF<ListSelectionItem*> xItemLIF( &m_xItems );!xItemLIF.done();xItemLIF.next())
	{
		xItemLIF()->bSelectedInitially = (xItemLIF()->xString == xString);
	}
}


////////////////////////////////////////////////////////////////////////////////////////////////
/*-----------------------**
** ListOrdering_Overlay  **
**-----------------------*/

bool ListOrdering_Overlay::s_bReturnValue;
ListOrdering_Overlay* ListOrdering_Overlay::s_pxThis = 0;


ListOrdering_Overlay::ListOrdering_Overlay(const char* szTitle, int iNumStrings, const char** pszStrings, void** ppUserData /*= 0*/)
:Tools_Modal_Overlay(szTitle)
{

	m_iNumStrings = iNumStrings;
	m_pszStrings = pszStrings;
	m_ppUserData = ppUserData;

	//take a copy of the original lists
	m_pszCancelStrings = new const char*[m_iNumStrings];
	for(int i=0; i<m_iNumStrings; i++)
	{
		m_pszCancelStrings[i] = m_pszStrings[i];
	}

	m_ppCancelUserData = 0;
	if(m_ppUserData)
	{
		m_ppCancelUserData = new void*[m_iNumStrings];
		for(int i=0; i<m_iNumStrings; i++)
		{
			m_ppCancelUserData[i] = m_ppUserData[i];
		}
	}

}

ListOrdering_Overlay::~ListOrdering_Overlay()
{
	while (m_xFunctionList.size())
	{
		delete m_xFunctionList.first_entry();
		m_xFunctionList.delete_first_entry();
	}
	delete[] m_pszCancelStrings;
	delete[] m_ppCancelUserData;
}

void ListOrdering_Overlay::Update()
{
	if(Asura_UserInput::KeyPressed(ASURA_KEYID_CTRL))
	{
		if (Asura_UserInput::DebouncedKeyPressed(ASURA_KEYID_UP))
		{
			MoveCurrentSelectionUp();
		}
		else if (Asura_UserInput::DebouncedKeyPressed(ASURA_KEYID_DOWN))
		{
			MoveCurrentSelectionDown();
		}
		else if (Asura_UserInput::DebouncedKeyPressed(ASURA_KEYID_PAGEUP))
		{
			MoveCurrentSelectionTop();
		}
		else if (Asura_UserInput::DebouncedKeyPressed(ASURA_KEYID_PAGEDOWN))
		{
			MoveCurrentSelectionBottom();
		}
	}
}


void ListOrdering_Overlay::AddFunction(const Asura_String xName, void(*pfnFunction)(void))
{
	OrderedListFunction *pNewFunction = new OrderedListFunction;
	pNewFunction->m_pfnFunction = pfnFunction;
	pNewFunction->m_xName = xName;
	m_xFunctionList.add_entry(pNewFunction);
}

bool ListOrdering_Overlay::DoModal()
{
	Asura_Assert(m_pszStrings, "Error, NULL string list");
	if(!m_iNumStrings)
	{
		Asura_Assert(0,"No strings for the selection overlay");
		return false;
	}
	s_pxThis = this;
	ShowOverlay();
	s_pxThis = 0;
	if (!s_bReturnValue)
	{
		// They cancelled, so put the list back the way it was.aw.
		for(int i=0; i<m_iNumStrings; i++)
		{
			m_pszStrings[i] = m_pszCancelStrings[i];
			if(m_ppUserData) m_ppUserData[i] = m_ppCancelUserData[i];
		}
	}

	return s_bReturnValue;
}

void ListOrdering_Overlay::InitialiseOverlay(Asura_Overlay* pxOverlay)
{

	
	//setup the controls on the overlay
	Asura_Vector_2 xOffset = Asura_Vector_2(1.0f,3.0f);
	Asura_Dialog* pxDialog;

	const char* pszHelpText = "Use up and down arrow keys to move the selected item in the list.\nUse Page Up to move the selected item to the top of the list.\nUse Page Down to move the selected item to the bottom of the list.";

	pxDialog = new Asura_Dialog("Keys : Ctrl + Up,Down,PgUp,PgDown",xOffset);
	pxDialog->SetHelpText(pszHelpText);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=1.5f;

	m_iSelection = 0;
	int iNumberOfLines = Asura_Maths::Min(m_iNumStrings,15);

	pxDialog = new Asura_List_Dialog(xOffset,m_pszStrings,&m_iSelection,iNumberOfLines,m_iNumStrings,NULL);
	pxDialog->SetHelpText(pszHelpText);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y=pxDialog->m_xMaxCoord.y;
	xOffset.y+=1.0f;

	pxOverlay->AddDialog(new Asura_Resize_Dialog(pxDialog,NULL));


	pxDialog = new Asura_Function_Dialog("Move Up", xOffset, MoveUp);
	pxDialog->SetHelpText("Move the selection earlier in the list");
	pxOverlay->AddDialog(pxDialog);

	xOffset.x+=10.0f;
	pxDialog = new Asura_Function_Dialog("Move Down", xOffset, MoveDown);
	pxDialog->SetHelpText("Move the selection later in the list");
	pxOverlay->AddDialog(pxDialog);
	xOffset.x-=10.0f;
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Move Start", xOffset, MoveTop);
	pxDialog->SetHelpText("Move the selection to the start of the list");
	pxOverlay->AddDialog(pxDialog);

	xOffset.x+=10.0f;
	pxDialog = new Asura_Function_Dialog("Move End", xOffset, MoveBottom);
	pxDialog->SetHelpText("Move the selection to the end of the list");
	pxOverlay->AddDialog(pxDialog);
	xOffset.x-=10.0f;
	xOffset.y += Asura_Dialog::LargeSpacing();
	

	for (int i=0;i<m_xFunctionList.size();i++)
	{
		pxDialog = new Asura_Function_Dialog(m_xFunctionList[i]->m_xName.GetString(), xOffset, m_xFunctionList[i]->m_pfnFunction);
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=1.5f;
	}

	pxDialog = new Asura_FunctionWithKeybinding_Dialog("Okay",xOffset,OkaySelected,ASURA_KEYID_ENTER,&pxOverlay->m_bShouldClose);
	pxOverlay->AddDialog(pxDialog);
	xOffset.x+=10.0f;

	pxDialog = new Asura_FunctionWithKeybinding_Dialog("Cancel",xOffset,CancelSelected,ASURA_KEYID_ESCAPE,&pxOverlay->m_bShouldClose);
	pxOverlay->AddDialog(pxDialog);

	pxOverlay->MakeDialogsRelativeToResizableDialog();;
	m_pxOverlay = pxOverlay;
}

void ListOrdering_Overlay::OkaySelected() 
{
	s_bReturnValue = true;
}

void ListOrdering_Overlay::CancelSelected() 
{
	s_bReturnValue = false;
}

void ListOrdering_Overlay::MoveCurrentSelectionUp()
{

	if(m_iSelection > 0)
	{
		const char* szTemp = m_pszStrings[m_iSelection-1];
		m_pszStrings[m_iSelection-1] = m_pszStrings[m_iSelection];
		m_pszStrings[m_iSelection] = szTemp;

		if(m_ppUserData)
		{
			void* pTemp = m_ppUserData[m_iSelection-1];
			m_ppUserData[m_iSelection-1] = m_ppUserData[m_iSelection];
			m_ppUserData[m_iSelection] = pTemp;
		}
		
		m_iSelection--;
	}
}

void ListOrdering_Overlay::MoveCurrentSelectionDown()
{

	if (m_iSelection < m_iNumStrings-1)
	{
		const char* szTemp = m_pszStrings[m_iSelection+1];
		m_pszStrings[m_iSelection+1] = m_pszStrings[m_iSelection];
		m_pszStrings[m_iSelection] = szTemp;

		if(m_ppUserData)
		{
			void* pTemp = m_ppUserData[m_iSelection+1];
			m_ppUserData[m_iSelection+1] = m_ppUserData[m_iSelection];
			m_ppUserData[m_iSelection] = pTemp;
		}
		
		m_iSelection++;
	}
}

void ListOrdering_Overlay::MoveCurrentSelectionTop()
{
	if(m_iSelection > 0)
	{
		const char* szTemp = m_pszStrings[m_iSelection];
		void* pTemp = m_ppUserData ? m_ppUserData[m_iSelection] : 0;

		for(int i=m_iSelection; i>0; i--)
		{
			m_pszStrings[i] = m_pszStrings[i-1];
			if(m_ppUserData) m_ppUserData[i] = m_ppUserData[i-1];
		}

		m_pszStrings[0] = szTemp;
		if(m_ppUserData) m_ppUserData[0] = pTemp;

		m_iSelection = 0;
	}
}

void ListOrdering_Overlay::MoveCurrentSelectionBottom()
{
	if(m_iSelection < m_iNumStrings-1)
	{
		const char* szTemp = m_pszStrings[m_iSelection];
		void* pTemp = m_ppUserData ? m_ppUserData[m_iSelection] : 0;

		for(int i=m_iSelection; i<m_iNumStrings-1; i++)
		{
			m_pszStrings[i] = m_pszStrings[i+1];
			if(m_ppUserData) m_ppUserData[i] = m_ppUserData[i+1];
		}

		m_pszStrings[m_iNumStrings-1] = szTemp;
		if(m_ppUserData) m_ppUserData[m_iNumStrings-1] = pTemp;

		m_iSelection = m_iNumStrings-1;
	}
}



//////////////////////////////////////////////////////////////////////////////
//
// TreeSelect_Overlay
// Provides a modal overlay for selecting from an arbitrary tree.
//
//////////////////////////////////////////////////////////////////////////////

TreeSelect_Overlay::TreeSelect_Overlay( const char* pszTitleString, const bool bAllowNone )
: Tools_Modal_Overlay( pszTitleString )
, m_bReturnValue( false )
, m_pxSelectionItem( NULL )
, m_bAllowNone( bAllowNone )
, m_pxTree( new Asura_TreeDialog_Tree )
, m_bOwnsTree( true)
, m_pfnOkayEnabled(NULL)
, m_iOkayEnabledUserData(0)
{
}

TreeSelect_Overlay::TreeSelect_Overlay( const char* pszTitleString, Asura_TreeDialog_Tree* pxTree, const bool bAllowNone )
: Tools_Modal_Overlay( pszTitleString )
, m_bReturnValue( false )
, m_pxSelectionItem( NULL )
, m_bAllowNone( bAllowNone )
, m_pxTree( pxTree )
, m_bOwnsTree( false)
, m_pfnOkayEnabled(NULL)
, m_iOkayEnabledUserData(0)
{
}

TreeSelect_Overlay::~TreeSelect_Overlay()
{
	if(m_bOwnsTree)
	{
		delete m_pxTree;
	}
}

void TreeSelect_Overlay::AddItem( const char* szItemName, const char* szParentName/*=NULL*/, int iItemData/*=0*/ )
{
	// Already present in tree? Don't add twice.
	if( m_pxTree->FindByName( szItemName, m_pxTree->FindByName( szParentName ) ) ) return;
	
	Asura_TreeDialog_Item* pxParent = m_pxTree->FindByName( szParentName );
	if( pxParent )
	{
		m_pxTree->AddItem( pxParent, szItemName, iItemData );
	}
	else
	{
		m_pxTree->AddItem( szItemName, iItemData );
	}	
}

bool TreeSelect_Overlay::DoModal()
{
	m_bReturnValue = false;
	ShowOverlay();
	return m_bReturnValue;
}

void TreeSelect_Overlay::Update()
{
	Tools_Modal_Overlay::Update();

	if ( Asura_UserInput::DebouncedKeyPressed( ASURA_KEYID_ENTER ) || Asura_UserInput::DebouncedKeyPressed( 'o' ) )
	{
		OkaySelected( reinterpret_cast< int >( this ) );
	}
	else if ( m_bAllowNone && Asura_UserInput::DebouncedKeyPressed( 'n' ) )
	{
		NoneSelected( reinterpret_cast< int >( this ) );
	}
	else if ( Asura_UserInput::DebouncedKeyPressed( ASURA_KEYID_ESCAPE ) || Asura_UserInput::DebouncedKeyPressed( 'c' ) )
	{
		CancelSelected( reinterpret_cast< int >( this ) );
	}
}

const char* TreeSelect_Overlay::GetSelection() const
{
	return((m_pxSelectionItem == NULL) ? NULL : m_pxSelectionItem->GetName());
}

int TreeSelect_Overlay::GetSelectionData() const
{
	return((m_pxSelectionItem == NULL) ? 0 : m_pxSelectionItem->GetUserData());
}

Asura_Hash_ID TreeSelect_Overlay::GetSelectionHash() const
{
	return((m_pxSelectionItem == NULL) ? ASURA_HASH_ID_UNSET : m_pxSelectionItem->GetHash());
}

const Asura_TreeDialog_Item* TreeSelect_Overlay::GetSelectionItem() const
{
	return(m_pxSelectionItem);
}

void TreeSelect_Overlay::InitialiseOverlay( Asura_Overlay* pxOverlay )
{
	// First initialise any overlay data.
	m_bReturnValue = false;

	// Submit the dialogs.
	Asura_Vector_2 xOffset( 1.0f, 3.0f );
	Asura_Tree_Dialog* pxTreeDialog = new Asura_Tree_Dialog( xOffset, m_pxTree, 30.0f, 30.0f );
	pxTreeDialog->SetDoubleClickedFunction(SelectCurrentItem, reinterpret_cast<int>(this));
	pxOverlay->AddDialog( pxTreeDialog );
	xOffset.y = pxTreeDialog->m_xMaxCoord[1] + 1.0f;

	Asura_Dialog* pxDialog = new Asura_Function_Dialog( "(O)kay", xOffset, OkaySelected, reinterpret_cast<int>(this) );
	if(m_pfnOkayEnabled != NULL)
	{
		pxDialog->SetIsEnabledFunction(m_pfnOkayEnabled, m_iOkayEnabledUserData);
	}
	pxOverlay->AddDialog( pxDialog );

	xOffset.x	= pxDialog->m_xMaxCoord.x + 3.0f;
	pxDialog = new Asura_Function_Dialog( "(C)ancel", xOffset, CancelSelected, reinterpret_cast<int>(this) );
	pxOverlay->AddDialog( pxDialog );		

	if(m_bAllowNone)
	{
		xOffset.x	= pxDialog->m_xMaxCoord.x + 3.0f;
		pxDialog = new Asura_Function_Dialog( "(N)one", xOffset, NoneSelected, reinterpret_cast<int>(this) );
		pxOverlay->AddDialog( pxDialog );		
	}
}

void TreeSelect_Overlay::OnOkaySelected()
{
	const Asura_TreeDialog_Item* pxItem = m_pxTree->GetSelectedItem();
	if(pxItem != NULL)
	{
		Asura_Overlay* pxOverlay = GetOverlay();
		m_bReturnValue = true;
		pxOverlay->m_bShouldClose = true;
	}

	m_pxSelectionItem = pxItem;
}

void TreeSelect_Overlay::OnCancelSelected()
{
	Asura_Overlay* pxOverlay = GetOverlay();
	m_bReturnValue = false;
	pxOverlay->m_bShouldClose = true;
}

void TreeSelect_Overlay::OnNoneSelected()
{
	Asura_Overlay* pxOverlay = GetOverlay();
	m_bReturnValue = true;
	m_pxSelectionItem = NULL;
	pxOverlay->m_bShouldClose = true;
}

void TreeSelect_Overlay::OnSelectCurrentItem()
{
	const Asura_TreeDialog_Item* pxItem = m_pxTree->GetSelectedItem();
	// We can only double-click for child-items in the tree...
	if(pxItem->GetNumChildren() == 0)
	{
		Asura_Overlay* pxOverlay = GetOverlay();
		m_bReturnValue = true;
		pxOverlay->m_bShouldClose = true;
	}

	m_pxSelectionItem = pxItem;
}

void TreeSelect_Overlay::OkaySelected( int iUserData )
{
	TreeSelect_Overlay*	pxThis = reinterpret_cast<TreeSelect_Overlay*>(iUserData);
	pxThis->OnOkaySelected();
}

void TreeSelect_Overlay::CancelSelected( int iUserData )
{
	TreeSelect_Overlay*	pxThis = reinterpret_cast<TreeSelect_Overlay*>(iUserData);
	pxThis->OnCancelSelected();
}

void TreeSelect_Overlay::NoneSelected( int iUserData )
{
	TreeSelect_Overlay*	pxThis = reinterpret_cast<TreeSelect_Overlay*>(iUserData);
	pxThis->OnNoneSelected();
}

void TreeSelect_Overlay::SelectCurrentItem(int iUserData)
{
	TreeSelect_Overlay*	pxThis = reinterpret_cast<TreeSelect_Overlay*>(iUserData);
	pxThis->OnSelectCurrentItem();
}

//////////////////////////////////////////////////////////////////////////////
//
// BlueprintSelect_Overlay
// Provides a modal overlay for selecting a blueprint.
//
//////////////////////////////////////////////////////////////////////////////

static int CompareTreeItemsByName( const Asura_TreeDialog_Item& xOne, const Asura_TreeDialog_Item& xTwo )
{
	return stricmp( xOne.GetName(), xTwo.GetName() );
}

BlueprintSelect_Overlay::BlueprintSelect_Overlay( const char* pszTitleString, Asura_Hash_ID* puBPSelection, Asura_Hash_ID* puTypeSelection, int iNumValidTypes/* = 0*/, const Asura_Hash_ID* puValidTypesArray/* = NULL*/, int iNumValidParents/* = 0*/, const Asura_Hash_ID* puValidParentsArray/* = NULL*/ )
: PARENT( pszTitleString )
, m_puBPHashID( puBPSelection )
, m_puTypeHashID( puTypeSelection )
{
	Tools_Mode_Blueprint::PopulateBlueprintTree( m_pxTree, NULL, iNumValidTypes, puValidTypesArray, iNumValidParents, puValidParentsArray );

	m_pxTree->InsertItem( CompareTreeItemsByName, "*None*" );

	// Try to find the previously selected blueprint.
	if( m_puBPHashID && m_puTypeHashID )
	{		
		Asura_TreeDialog_Item* pxSelectedItem = NULL;
		const Asura_Blueprint* pxBlueprint = ( *m_puTypeHashID != ASURA_HASH_ID_UNSET ) ?
			Asura_Blueprint_System::GetBlueprintByHashID( *m_puTypeHashID, *m_puBPHashID ) : NULL;

		if ( pxBlueprint )
		{
            // Find the type node in the tree first, then the blueprint node in the type sub-tree.
            pxSelectedItem = m_pxTree->FindByName( Asura_Blueprint_System::GetTypeName( *m_puTypeHashID )->GetString() );
            if ( pxSelectedItem )
            {
                pxSelectedItem = m_pxTree->FindByName( pxBlueprint->GetName().GetString(), pxSelectedItem );
            }
		}
		else
		{			
			for(Asura_Blueprint_Type_ConstIterator xIterator; !xIterator.Done(); xIterator.Next())
			{
				pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID( xIterator.GetCurrentHash(), *m_puBPHashID );
				if ( pxBlueprint )
				{
					Asura_TreeDialog_Item* pxTreeItem = m_pxTree->FindByName( pxBlueprint->GetName().GetString() );
					if( pxTreeItem )
					{
						// If pxSelectedItem is non-null, we've found a blueprint with this name already 
						// so there must be more than one. Rather than selecting the wrong one, just select
						// none at all.
						if( pxSelectedItem )																
						{
							pxSelectedItem = NULL;
							break;
						}
						else
						{
							pxSelectedItem = pxTreeItem;
						}
					}					
				}								
			}
		}

		m_pxTree->SetSelectedItem( pxSelectedItem );
	}
}

void BlueprintSelect_Overlay::OnOkaySelected()
{
	PARENT::OnOkaySelected();

	m_bReturnValue = UpdateBPHash();

	if( GetOverlay() )
	{
		GetOverlay()->m_bShouldClose = true;
	}
}

void BlueprintSelect_Overlay::OnSelectCurrentItem()
{
	PARENT::OnSelectCurrentItem();

	if( m_pxTree->GetSelectedItem() )
	{
		if( m_pxTree->GetSelectedItem()->GetNumChildren() == 0 )
		{
			m_bReturnValue = UpdateBPHash();
		}
	}
}


bool BlueprintSelect_Overlay::UpdateBPHash()
{
	bool bReturnValue = true;

	if ( m_pxTree->GetSelectedItem() )
	{
		if ( stricmp( m_pxTree->GetSelectedItem()->GetName(), "*None*" ) == 0 )
		{
			if ( m_puBPHashID )
			{
				*m_puBPHashID = ASURA_HASH_ID_UNSET;
			}
			if ( m_puTypeHashID )
			{
				*m_puTypeHashID = ASURA_HASH_ID_UNSET;
			}

			bReturnValue = true;
		}
		else
		{
			if ( m_puBPHashID )
			{
				*m_puBPHashID = Asura_GetHashID( m_pxTree->GetSelectedItem()->GetName() );
			}
			if ( m_puTypeHashID )
			{
				*m_puTypeHashID = static_cast< Asura_Hash_ID >( m_pxTree->GetSelectedItem()->GetUserData() );
			}

			// If we have selected a type instead of a blueprint then return false.
			// (The user data is zero for types and the type name hash ID for blueprints.)
			bReturnValue = ( m_pxTree->GetSelectedItem()->GetUserData() != 0 );
		}
	}
	else
	{
		// Set the return value to true to allow for the user selecting 'none'.
		bReturnValue = true;
	}

	return bReturnValue;
}

//////////////////////////////////////////////////////////////////////////////
//
// FileSelect_Overlay
// Provides a modal overlay for selecting a file from tree-list
//
//////////////////////////////////////////////////////////////////////////////

FileSelect_Overlay::FileSelect_Overlay( const char* pszTitleString, Asura_TreeDialog_FileTree* pxTree, const bool bAllowNone )
: PARENT( pszTitleString, pxTree, bAllowNone )
{
	SetOkayEnabledFunction(IsSelectedTreeItemNotADir, reinterpret_cast<int>(m_pxTree));
}

FileSelect_Overlay::FileSelect_Overlay( const char* pszTitleString, const char* pszDirectory, const char* pszFileExtension, const bool bAllowNone )
: PARENT( pszTitleString, new Asura_TreeDialog_FileTree (pszDirectory, pszFileExtension, bAllowNone), bAllowNone )
{
	SetOkayEnabledFunction(IsSelectedTreeItemNotADir, reinterpret_cast<int>(m_pxTree));

	// NOTE: The base-class hasn't created the tree, but it can tidy it up for us...
	m_bOwnsTree	= true;
}

void FileSelect_Overlay::SelectFile( Asura_Hash_ID uSelectedItemHashID, const bool bIsPathIncludedInHash )
{
	Asura_TreeDialog_FileTree*	pxFileTree	= static_cast<Asura_TreeDialog_FileTree*>(m_pxTree);
	Asura_TreeDialog_Item*		pxTreeItem	= NULL;

	if( uSelectedItemHashID==ASURA_HASH_ID_UNSET )
	{
		pxTreeItem = pxFileTree->FindByName( "[NONE]");
	}
	else
	{
		// Select currently existing object.
		if( bIsPathIncludedInHash==false )
		{
			pxTreeItem = pxFileTree->FindByHash( uSelectedItemHashID );
		}
		else
		{
			pxTreeItem = pxFileTree->FindTreeItemByHashWithPath( pxFileTree->GetRoot(), uSelectedItemHashID );
		}
	}

	if( pxTreeItem )
	{
		pxFileTree->SetSelectedItem( pxTreeItem );
	}
}

void FileSelect_Overlay::GetSelectedFile( Asura_Hash_ID& uSelectedItemHashID, Asura_String& xSelectedItemName )
{
	const Asura_TreeDialog_FileTree*	pxFileTree	= static_cast<Asura_TreeDialog_FileTree*>(m_pxTree);

	if(pxFileTree->IsCurrentItemAFile())
	{
		const Asura_TreeDialog_Item*	pxCurrentItem	= GetSelectionItem();

		uSelectedItemHashID = pxCurrentItem->GetHash();
		xSelectedItemName	= pxCurrentItem->GetName();
	}
	else
	{
		// Didn't find any item, so set is as unset
		uSelectedItemHashID = ASURA_HASH_ID_UNSET;
		xSelectedItemName.MakeNull();
	}
}

void FileSelect_Overlay::GetSelectedFileWithPath( Asura_Hash_ID& uSelectedItemWithPathHashID, Asura_String& xSelectedItemWithPathName )
{
	const Asura_TreeDialog_FileTree*	pxFileTree	= static_cast<Asura_TreeDialog_FileTree*>(m_pxTree);

	if(pxFileTree->IsCurrentItemAFile())
	{
		const Asura_TreeDialog_Item*	pxCurrentItem	= GetSelectionItem();

		pxCurrentItem->ConcatenatePath( xSelectedItemWithPathName );

		// Create hash from path and filename.
		uSelectedItemWithPathHashID = Asura_GetHashID( xSelectedItemWithPathName.GetString() );
	}
	else
	{
		// Didn't find any item, so set is as unset
		uSelectedItemWithPathHashID = ASURA_HASH_ID_UNSET;
		xSelectedItemWithPathName.MakeNull();
	}
}

bool FileSelect_Overlay::IsSelectedTreeItemNotADir(int iUserData)
{
	const Asura_TreeDialog_FileTree*	pxFileTree	= reinterpret_cast<Asura_TreeDialog_FileTree*>(iUserData);

	return(!pxFileTree->IsCurrentItemADirectory());
}



//////////////////////////////////////////////////////////////////////////////
//
// BlueprintTypeSelect_Overlay
// Provides a modal overlay for selecting a blueprint type.
//
//////////////////////////////////////////////////////////////////////////////

BlueprintTypeSelect_Overlay::BlueprintTypeSelect_Overlay( const Asura_String& xTitle, Asura_Hash_ID& uTypeHashIDOut )
: PARENT( xTitle )
, m_uTypeHashIDOut( uTypeHashIDOut )
{
	AddItem( "*None*", ASURA_HASH_ID_UNSET );
	for ( Asura_Blueprint_Type_Iterator xItr; !xItr.Done(); xItr.Next() )
	{
		AddItem( xItr.GetCurrent()->m_xTypeName.GetString(), static_cast< int >( xItr.GetCurrentHash() ) );
	}
}

bool BlueprintTypeSelect_Overlay::DoModal()
{
	if ( PARENT::DoModal() )
	{
		if ( HasSelectedItem() )
		{
			m_uTypeHashIDOut = static_cast< Asura_Hash_ID >( GetSelectionData() );
		}
		return true;
	}
	return false;
}

void BlueprintTypeSelect_Overlay::InitialiseOverlay( Asura_Overlay* pxOverlay )
{
	PARENT::InitialiseOverlay( pxOverlay );

	if ( m_uTypeHashIDOut != ASURA_HASH_ID_UNSET )
	{
		SetSelectionByData( static_cast< int >( m_uTypeHashIDOut ) );
	}
}

//////////////////////////////////////////////////////////////////////////////
//
// 
//
//////////////////////////////////////////////////////////////////////////////

