// Tools_SoundMixer_Overlay.cpp
// Created by Richard Rice - 06/07/2006
// Revised by Thomas Young - 08/07/2009
// Copyright (C) 2006 Rebellion, All Rights Reserved.

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

#include "Sound/Tools_SoundMixer_Overlay.h"

#include "Core/Asura_NameTree.h"
#include "Entity/Asura_StaticMessage_System.h"
#include "Interface/Asura_Overlay_System.h"
#include "Interface/Asura_Cursor.h"

#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_Text_Output.h"
#include "Rendering/Asura_Viewport.h"
#include "Sound/Asura_Sound_Event.h"
#include "Sound/Asura_Sound_Event_Storage.h"
#include "Sound/Asura_Sound_Mixer.h"
#ifdef ASURA_USE_DIALOGUE_SYSTEM
#include "Sound/Asura_Dialogue.h"
#endif
#include "Tools_Overlays.h"
#include "Load&Save/Tools_ModeDataFile.h"
#include "Misc/Tools_Dialog.h"
#include "Misc/Tools_Modal_Overlay.h"
#include "Network/Asura_Message.h"
#include "Sound/Tools_Audio_DSP.h"
#ifdef ASURA_USE_DIALOGUE_SYSTEM
#include "Sound/Tools_Dialogue_System.h"
#endif
#include "Sound/Tools_SoundEvent_System.h"
#include "Sound/Tools_SoundEventTab_Event.h"

// E X T E R N A L S /////////////////////////////////////////////////////////

extern unsigned int Asura_OverlayBrightness;

// C O N S T A N T S /////////////////////////////////////////////////////////

static const char* const g_aszTabs[] = 
{ 
	"Mixer Presets", 
	"Mixer Channel" 
};

static const float fMIXER_CHANNEL_DIALOG_WIDTH					= 160.0f;
static const float fMIXER_CHANNEL_DIALOG_HEIGHT					= 60.0f;
static const float fMIXER_CHANNEL_DIALOG_NODE_SIZE				= 10.0f;
static const float fMIXER_CHANNEL_DIALOG_SPACING_X				= 20.0f;
static const float fMIXER_CHANNEL_DIALOG_SPACING_Y				= 10.0f;
static const float fMIXER_CHANNEL_DIALOG_NAMETAG_HEIGHT			= ASURA_TEXT_OUTPUT_FONT_HEIGHT + 4.0f;

static const u_int uMIXER_CHANNEL_DIALOG_BASE_COLOUR			= 0x00B1D7FC;
static const u_int uMIXER_CHANNEL_DIALOG_BASE_SEL_COLOUR		= 0x00C1E7FC;
static const u_int uMIXER_CHANNEL_DIALOG_BASE_PULSE1_COLOUR		= 0x0086ACE6;
static const u_int uMIXER_CHANNEL_DIALOG_BASE_PULSE2_COLOUR		= 0x00F1E2FE;
static const u_int uMIXER_CHANNEL_DIALOG_BORDER_COLOUR			= 0x00000050;
static const u_int uMIXER_CHANNEL_DIALOG_NODE_COLOUR			= 0x00FFFF9F;
static const u_int uMIXER_CHANNEL_DIALOG_NODE_SEL_COLOUR		= 0x009BDB6C;
static const u_int uMIXER_CHANNEL_DIALOG_CONNECTOR_COLOUR		= 0x00E6E6FF;
static const u_int uMIXER_CHANNEL_DIALOG_NAMETAG_COLOUR			= 0x00567CB6;
static const u_int uMIXER_CHANNEL_DIALOG_NAMETAG_SEL_COLOUR		= 0x00668CC6;
static const u_int uMIXER_CHANNEL_DIALOG_VOLUME_BACK_COLOUR 	= 0x00D6D6E6;
static const u_int uMIXER_CHANNEL_DIALOG_VOLUME_FILL_COLOUR 	= 0x00A5D189;
static const u_int uMIXER_CHANNEL_DIALOG_VOLUME_LINE_COLOUR		= 0x00151515;
static const u_int uMIXER_CHANNEL_DIALOG_DETAILS_TEXT_COLOUR	= 0x00606080;
static const u_int uMIXER_CHANNEL_DIALOG_SHADOW_COLOUR			= 0x40000000;
static const u_int uMIXER_CHANNEL_SUBWINDOW_BACKGROUND_COLOUR	= 0x00505070;

// S T A T I C   M E M B E R   V A R I A B L E S /////////////////////////////

int										Tools_SoundMixerHash_Dialog::s_iListValidID							= 0;

int										Tools_SoundMixer_Dialog::s_iListValidID								= 0;

Asura_Vector_2							Tools_SoundMixer_Overlay::s_xOverlayPosition						( 10.0f,10.0f );
Asura_Vector_2							Tools_SoundMixer_Overlay::s_xSubwindowSize							( 0.0f, 0.0f );
u_int 									Tools_SoundMixer_Overlay::s_uPresetPriority							= 1;
float 									Tools_SoundMixer_Overlay::s_fPresetFadeInTime						= 1.0f;
float 									Tools_SoundMixer_Overlay::s_fPresetFadeOutTime						= 1.0f;
float 									Tools_SoundMixer_Overlay::s_fPresetLifeTime							= 0.0f;
Asura_Hash_ID							Tools_SoundMixer_Overlay::s_uPresetDSP								= ASURA_HASH_ID_UNSET;
Tools_SoundMixer_Overlay*				Tools_SoundMixer_Overlay::s_pxThis									= 0;
Tools_SoundMixerPresetList_Dialog*		Tools_SoundMixer_Overlay::s_pxMixerPresetListDialog					= 0;
Tools_MixerTree_Dialog*					Tools_SoundMixer_Overlay::s_pxMixerTreeDialog						= 0;
Tools_TempMixerChannel					Tools_SoundMixer_Overlay::s_xSelectedMixerChannel;
Asura_Dialog*							Tools_SoundMixer_Overlay::s_pxMixerChannelNameDialog				= 0;
bool									Tools_SoundMixer_Overlay::s_bShouldUpdateMixerChannelNameDialog		= false;
Tools_Mixer_DialogPos					Tools_SoundMixer_Overlay::s_axDialogPositions						[ TOOLS_MIXERS_MAX_DIALOG_POSITIONS ];
bool									Tools_SoundMixer_Overlay::s_bMixerPresetModified					= false;

bool									Tools_MixerChannel_Dialog::s_bAnyChannelBeingDragged				= false;
bool									Tools_MixerChannel_Dialog::s_bAnyChannelSelectingOutput				= false;
Asura_Hash_ID							Tools_MixerChannel_Dialog::s_xInputNodeSelected						= ASURA_HASH_ID_UNSET;
Tools_TempMixerChannel*					Tools_MixerChannel_Dialog::s_pxSelectedChannelData					= 0;
Asura_List<Tools_MixerChannel_Dialog>	Tools_MixerChannel_Dialog::s_xMixerDialogList;
bool									Tools_MixerChannel_Dialog::s_bDialogsAcceptMouseInput				= false;

bool (* Tools_MixerChannel_Dialog::s_pfnRequestSelection )	( Asura_Hash_ID )					= 0;
void (* Tools_MixerChannel_Dialog::s_pfnPositionChanged )	( Asura_Hash_ID, Asura_Vector_2 )	= 0;

// F U N C T I O N   D E F I N I T I O N S ///////////////////////////////////

static u_int InterpolateColour ( const u_int uColour1, const u_int uColour2, const float fFactor )
{
	const int iRed1		= static_cast<int>( ( uColour1 >> 16 ) & 0x000000FF ) ;
	const int iGreen1	= static_cast<int>( ( uColour1 >> 8 ) & 0x000000FF ) ;
	const int iBlue1	= static_cast<int>(	uColour1 & 0x000000FF ) ;

	const int iRed2		= static_cast<int>( ( uColour2 >> 16 ) & 0x000000FF ) ;
	const int iGreen2	= static_cast<int>( ( uColour2 >> 8 ) & 0x000000FF ) ;
	const int iBlue2	= static_cast<int>(	uColour2 & 0x000000FF ) ;

	const u_int uRed	= static_cast<u_int>( Asura_Maths::Lerp( iRed1	, iRed2		, fFactor ) )	<< 16;
	const u_int uGreen	= static_cast<u_int>( Asura_Maths::Lerp( iGreen1, iGreen2	, fFactor ) )	<< 8;
	const u_int uBlue	= static_cast<u_int>( Asura_Maths::Lerp( iBlue1	, iBlue2	, fFactor ) );

	return ( uRed | uGreen | uBlue );
}


static u_int ReduceOpacity ( const u_int uColour )
{
	const int iAlpha = static_cast<int>( ( uColour >> 24 ) & 0x000000FF ) / 2;
	
	return ( ( uColour & 0x00FFFFFF ) | ( static_cast<u_int>( iAlpha ) << 24 ) );
}


// _______________________________ Tools_SoundMixer_Overlay __________________________________


Tools_SoundMixer_Overlay::Tools_SoundMixer_Overlay() : Asura_Overlay( "Sound Mixer", false, &s_xOverlayPosition )
{
	Asura_Assert(!s_pxThis,"Should only be one instance of the sound mixer overlay");
	s_pxThis = this;

	m_xPosition = s_xOverlayPosition;

	// Set up the mixer channel dialog static stuff
	s_xSelectedMixerChannel.m_xChannelHash = ASURA_HASH_ID_UNSET;

	Tools_MixerChannel_Dialog::SetMixerChannelDataPointer( &s_xSelectedMixerChannel );

	Tools_MixerChannel_Dialog::SetCallback_RequestSelection	( Callback_RequestSelection );
	Tools_MixerChannel_Dialog::SetCallback_PositionChanged	( Callback_PositionChanged	);

	// zero our dialog pointers
	s_pxMixerPresetListDialog = 0;
	s_pxMixerChannelNameDialog = 0;
	s_pxMixerTreeDialog = 0;

	s_bShouldUpdateMixerChannelNameDialog = false;
}


Tools_SoundMixer_Overlay::~Tools_SoundMixer_Overlay()
{
	// Save the selected mixer
	SaveMixerChannelIfModified();
	ClearSelectedMixerChannel();

	if( Tools_SoundEvent_System::GetMixersModified() && ShowYesNoOverlay( "Save\n\nDo you want to save the mixers file now?" ) )
	{
		Tools_SoundEvent_System::SaveMixers();
	}

	Tools_SoundMixer_Overlay::TransferModeData(TOOLSMODEDATAFILE_SAVE);

	if(s_pxThis == this)
	{
		s_pxThis = 0;
	}
}


void Tools_SoundMixer_Overlay::ShowMixersOverlay ()
{
	Tools_SoundMixer_Overlay::TransferModeData(TOOLSMODEDATAFILE_LOAD);

	if(!s_pxThis)
	{
		s_pxThis = new Tools_SoundMixer_Overlay();
		s_pxThis->ShowOverlay();
	}
	Asura_Overlay_System::BringOverlayToTop(TOOLS_OVERLAY_SOUNDMIXER);
}


void Tools_SoundMixer_Overlay::CloseMixersOverlay()
{
	Asura_Overlay_System::CloseOverlays(TOOLS_OVERLAY_SOUNDMIXER);
	
	ClearSelectedMixerChannel();
}


bool Tools_SoundMixer_Overlay::IsOpen()
{
	return ( s_pxThis != 0);
}


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

	if( false == file.GetError() )
	{
		file.Transfer(s_xOverlayPosition);
		file.Transfer( s_xSubwindowSize );

		// Save our mixer channel positions
		for( int iEntry = 0; iEntry < TOOLS_MIXERS_MAX_DIALOG_POSITIONS; ++iEntry )
		{
			file.Transfer( s_axDialogPositions[ iEntry ].m_xMixerChannel );
			file.Transfer( s_axDialogPositions[ iEntry ].m_xPosition );
		}
	}
}


void Tools_SoundMixer_Overlay::ShowOverlay()
{
	Asura_Vector_2 xOffset(1.0f,3.0f);
	Asura_Dialog* pxDialog;

	//Create the subwindow control for containing all the mixers
	float fSubwindowWidth	= Asura_Maths::Min( 80.0f, ( Asura_Viewport::GetWidth()  - 100 ) / ASURA_TEXT_OUTPUT_FONT_WIDTH  );
	float fSubwindowHeight	= Asura_Maths::Min( 20.0f, ( Asura_Viewport::GetHeight() - 100 ) / ASURA_TEXT_OUTPUT_FONT_HEIGHT );
	
	s_pxMixerTreeDialog = new Tools_MixerTree_Dialog( xOffset,fSubwindowWidth,fSubwindowHeight );
	
	AddDialog( s_pxMixerTreeDialog );
	xOffset.y += s_pxMixerTreeDialog->m_xMaxCoord.y;

	//add control for resizing the subwindow dialog
	pxDialog = new Asura_Resize_Dialog( s_pxMixerTreeDialog, 0 );
	AddDialog(pxDialog);

	Asura_Tabs_Dialog* pxTabs = new Asura_Tabs_Dialog( xOffset, g_aszTabs, 2, 200 );
	pxTabs->SetRelativePosition( s_pxMixerTreeDialog, true, false );
	AddDialog( pxTabs );
		
	//redirect all subsequent dialogs to the tab dialog
	SetCurrentParentDialog( pxTabs );	

	// Add controls to the 'Mixer Presets' tab
	pxTabs->SelectTab( 0 );

	xOffset = pxTabs->GetTopDialogPosition();

	s_pxMixerPresetListDialog = new Tools_SoundMixerPresetList_Dialog( xOffset );
	s_pxMixerPresetListDialog->UpdateMixerPresetList ( Asura_GetHashID( "default" ) );
	s_pxMixerPresetListDialog->SetRelativePosition( s_pxMixerTreeDialog, true, false );
	AddDialog( s_pxMixerPresetListDialog );
	xOffset.x += s_pxMixerPresetListDialog->m_xMaxCoord.x;
	
	// Now we have a list of presets, load the selected one
	LoadPreset();

	pxDialog = new Asura_Function_Dialog( "New Preset", xOffset, _NewPreset );
	pxDialog->SetHelpText("Create a new mixer preset");
	pxDialog->SetRelativePosition( s_pxMixerTreeDialog, true, false );
	AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	/*pxDialog = new Asura_Function_Dialog( "Rename Preset", xOffset, _RenamePreset );
	pxDialog->SetHelpText("Rename the current mixer preset");
	pxDialog->SetIsEnabledFunction( IsDefaultPresetNotSelected );
	AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::LargeSpacing();*/

	pxDialog = new Asura_Function_Dialog( "Delete Preset", xOffset, _DeletePreset );
	pxDialog->SetHelpText("Delete the current mixer preset");
	pxDialog->SetIsEnabledFunction( IsDefaultPresetNotSelected );
	pxDialog->SetRelativePosition( s_pxMixerTreeDialog, true, false );
	AddDialog(pxDialog);

	xOffset = pxTabs->GetTopDialogPosition();
	xOffset.x += 40.0f;

	pxDialog = new Asura_Numerical_Dialog("Preset Priority", xOffset, &s_uPresetPriority, 0, 99, 1, "%d", &s_bMixerPresetModified);
	pxDialog->SetHelpText("Set the priority for this preset (1 = Lowest, 99 = Highest)");
	pxDialog->SetIsEnabledFunction( IsDefaultPresetNotSelected );
	
	pxDialog->SetRelativePosition( s_pxMixerTreeDialog, true, false );
	AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Numerical_Dialog("Fade In", xOffset, &s_fPresetFadeInTime, 0.0f, 100.0f, 0.1f, "%.1f", &s_bMixerPresetModified);
	pxDialog->SetHelpText("Set time in seconds for the preset to fade in when activated");
	pxDialog->SetIsEnabledFunction( IsDefaultPresetNotSelected );
	pxDialog->SetRelativePosition( s_pxMixerTreeDialog, true, false );
	AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Numerical_Dialog("Lifetime", xOffset, &s_fPresetLifeTime, 0.0f, 100.0f, 0.1f, "%.1f", &s_bMixerPresetModified);
	pxDialog->SetHelpText("Set time in seconds for the preset to be queued for. A life time of 0.0 means the preset will be queued indefinitely (or until unqueued).");
	pxDialog->SetIsEnabledFunction( IsDefaultPresetNotSelected );
	pxDialog->SetRelativePosition( s_pxMixerTreeDialog, true, false );
	AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Numerical_Dialog("Fade Out", xOffset, &s_fPresetFadeOutTime, 0.0f, 100.0f, 0.1f, "%.1f", &s_bMixerPresetModified);
	pxDialog->SetHelpText("Set time in seconds for the preset to fade out when deactivated");
	pxDialog->SetIsEnabledFunction( IsDefaultPresetNotSelected );
	pxDialog->SetRelativePosition( s_pxMixerTreeDialog, true, false );
	AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	#if defined ASURA_USE_AUDIO_SYSTEM
	pxDialog = new Tools_DSPChain_DropDownList_Dialog(xOffset, 10, &s_uPresetDSP, &s_bMixerPresetModified, "DSP");
	pxDialog->SetHelpText("Sets the DSP chain to apply when this preset is active. Only sounds tagged for DSP will be affected.");
	pxDialog->SetIsEnabledFunction( IsDefaultPresetNotSelected );
	pxDialog->SetRelativePosition( s_pxMixerTreeDialog, true, false );
	AddDialog(pxDialog);
	#endif

	// Add controls to the 'Mixer Channel' tab
	pxTabs->SelectTab( 1 );

	xOffset = pxTabs->GetTopDialogPosition();
	
	Asura_Sound_Mixer_Channel* pxChannel = 0;
	if( IsMixerChannelSelected() )
	{
		pxChannel = Asura_Sound_Mixer_System::FindChannel( s_xSelectedMixerChannel.m_xChannelHash );
	}

	char szText[128];

	if( pxChannel )
	{
		Asura_PrintfToBfr( szText, "Selected Mixer: %s", 128, true, pxChannel->GetName() );
	}
	else
	{
		Asura_PrintfToBfr( szText, "No mixer channel selected", 128, true );
	}

	s_pxMixerChannelNameDialog = new Asura_Dialog( szText, xOffset );
	s_pxMixerChannelNameDialog->SetRelativePosition( s_pxMixerTreeDialog, true, false );
	AddDialog( s_pxMixerChannelNameDialog );
	xOffset.y += Asura_Dialog::LargeSpacing();

	pxDialog = new Asura_Function_Dialog( "Create New", xOffset, _CreateMixerChannel );
	pxDialog->SetHelpText("Create a new mixer channel");
	pxDialog->SetRelativePosition( s_pxMixerTreeDialog, true, false );
	AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::LargeSpacing();

	pxDialog = new Asura_Function_Dialog( "Rename", xOffset, _RenameMixerChannel );
	pxDialog->SetHelpText("Rename a mixer channel");
	pxDialog->SetIsEnabledFunction( IsMixerChannelSelected );
	pxDialog->SetRelativePosition( s_pxMixerTreeDialog, true, false );
	AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::LargeSpacing();

	pxDialog = new Asura_Function_Dialog( "Delete", xOffset, _DeleteMixerChannel );
	pxDialog->SetHelpText("Delete a mixer channel");
	pxDialog->SetIsEnabledFunction( IsMixerChannelSelected );
	pxDialog->SetRelativePosition( s_pxMixerTreeDialog, true, false );
	AddDialog(pxDialog);
	
	xOffset = pxTabs->GetTopDialogPosition();
	xOffset.x += 20.0f;
	xOffset.y += + Asura_Dialog::LargeSpacing();

	pxDialog = new Asura_Tickbox_Dialog( "Include in preset", xOffset, &s_xSelectedMixerChannel.m_bIncludeInPreset );
	pxDialog->SetHelpText("whether this channel is included in saved presets");
	pxDialog->SetIsEnabledFunction( IsMixerChannelSelected );
	reinterpret_cast<Asura_Tickbox_Dialog*>(pxDialog)->SetClickedFunction( SetMixerChannelModified );
	pxDialog->SetRelativePosition( s_pxMixerTreeDialog, true, false );
	AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::LargeSpacing();

	pxDialog = new Asura_Dialog( "Volume", xOffset );
	pxDialog->SetIsEnabledFunction( IsMixerChannelSelected );
	pxDialog->SetRelativePosition( s_pxMixerTreeDialog, true, false );
	AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Scrollbar_Dialog( xOffset, &s_xSelectedMixerChannel.m_xSettings.m_fVolume, 0.00f, 1.33f, true, 16.0f, "%.2f", 0, SetMixerChannelModified );
	pxDialog->SetHelpText("Volume multiplier.\nNote that 1.33 is effectively maximum , since all volumes are scaled by 0.75 and capped at 1.0 at the end.");
	pxDialog->SetIsEnabledFunction( IsMixerChannelSelected );
	pxDialog->SetRelativePosition( s_pxMixerTreeDialog, true, false );
	AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::LargeSpacing();

	pxDialog = new Asura_Tickbox_Dialog( "Mute", xOffset, &s_xSelectedMixerChannel.m_xSettings.m_bMute );
	pxDialog->SetHelpText("Whether this channel is muted or not");
	pxDialog->SetIsEnabledFunction( IsMixerChannelSelected );
	reinterpret_cast<Asura_Tickbox_Dialog*>(pxDialog)->SetClickedFunction( SetMixerChannelModified );
	pxDialog->SetRelativePosition( s_pxMixerTreeDialog, true, false );
	AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog( "Solo", xOffset, &s_xSelectedMixerChannel.m_xSettings.m_bSolo );
	pxDialog->SetHelpText("Whether this channel is a solo");
	pxDialog->SetIsEnabledFunction( IsMixerChannelSelected );
	reinterpret_cast<Asura_Tickbox_Dialog*>(pxDialog)->SetClickedFunction( SetMixerChannelModified );
	pxDialog->SetRelativePosition( s_pxMixerTreeDialog, true, false );
	AddDialog(pxDialog);
	
	xOffset = pxTabs->GetTopDialogPosition();
	xOffset.x += 40.0f;
	xOffset.y += + Asura_Dialog::LargeSpacing();

	pxDialog = new Asura_Tickbox_Dialog( "Ignore Game Speed Pitch ", xOffset, &s_xSelectedMixerChannel.m_xSettings.m_bIgnoreGameSpeedPitch );
	pxDialog->SetHelpText( "All inputs on this mixer ignore any pitch changes because of game speed variation" );
	pxDialog->SetIsEnabledFunction( IsMixerChannelSelected );
	reinterpret_cast<Asura_Tickbox_Dialog*>(pxDialog)->SetClickedFunction( SetMixerChannelModified );
	pxDialog->SetRelativePosition( s_pxMixerTreeDialog, true, false );
	AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog( "Use Reverb ", xOffset, &s_xSelectedMixerChannel.m_xSettings.m_bUseReverb );
	pxDialog->SetHelpText( "All inputs on this mixer use environmental reverb effects (provided event and all connected mixers have it enabled)" );
	pxDialog->SetIsEnabledFunction( IsMixerChannelSelected );
	reinterpret_cast<Asura_Tickbox_Dialog*>(pxDialog)->SetClickedFunction( SetMixerChannelModified );
	pxDialog->SetRelativePosition( s_pxMixerTreeDialog, true, false );
	AddDialog( pxDialog );
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog( "Use Distance Filter ", xOffset, &s_xSelectedMixerChannel.m_xSettings.m_bUseDistanceFilter );
	pxDialog->SetHelpText( "All inputs on this mixer use environmental distance filter effects (provided event and all connected mixers have it enabled)" );
	pxDialog->SetIsEnabledFunction( IsMixerChannelSelected );
	reinterpret_cast<Asura_Tickbox_Dialog*>(pxDialog)->SetClickedFunction( SetMixerChannelModified );
	pxDialog->SetRelativePosition( s_pxMixerTreeDialog, true, false );
	AddDialog( pxDialog );
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog( "Use Doppler ", xOffset, &s_xSelectedMixerChannel.m_xSettings.m_bUseDoppler);
	pxDialog->SetHelpText( "All inputs on this mixer use doppler effects (provided event and all connected mixers have it enabled)" );
	pxDialog->SetIsEnabledFunction( IsMixerChannelSelected );
	reinterpret_cast<Asura_Tickbox_Dialog*>(pxDialog)->SetClickedFunction( SetMixerChannelModified );
	pxDialog->SetRelativePosition( s_pxMixerTreeDialog, true, false );
	AddDialog( pxDialog );
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog( "Use Occlusion ", xOffset, &s_xSelectedMixerChannel.m_xSettings.m_bUseOcclusion);
	pxDialog->SetHelpText( "All inputs on this mixer use occlusion (provided event and all connected mixers have it enabled)" );
	pxDialog->SetIsEnabledFunction( IsMixerChannelSelected );
	reinterpret_cast<Asura_Tickbox_Dialog*>(pxDialog)->SetClickedFunction( SetMixerChannelModified );
	pxDialog->SetRelativePosition( s_pxMixerTreeDialog, true, false );
	AddDialog( pxDialog );
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog( "Use Master DSP ", xOffset, &s_xSelectedMixerChannel.m_xSettings.m_bUseMasterDSP);
	pxDialog->SetHelpText( "All inputs on this mixer use master DSP effect (provided event and all connected mixers have it enabled)" );
	pxDialog->SetIsEnabledFunction( IsMixerChannelSelected );
	reinterpret_cast<Asura_Tickbox_Dialog*>(pxDialog)->SetClickedFunction( SetMixerChannelModified );
	pxDialog->SetRelativePosition( s_pxMixerTreeDialog, true, false );
	AddDialog( pxDialog );
	xOffset.y += Asura_Dialog::DefaultSpacing();

	// TY 06.01.09 - I don't think this pan stuff is really necessary
	/*pxDialog = new Tools_SurroundPan_Dialog(xOffset,&s_xChannel_Surround,&m_bChanged);
	AddDialog(pxDialog);

	xOffset.x = pxDialog->m_xMaxCoord.x + 1.0f;
	pxDialog = pxIcon = new Asura_Icon_Dialog(xOffset,Tools_Dialog::GetIconTexture(),TOOLS_ICON_A+'P'-'A');
	
	pxIcon->SetClickedFlag(&m_bChanged);
	pxIcon->SetCheckboxBool(&s_bChannel_PanOverride);
	pxIcon->SetHelpText("Pan Override");
	AddDialog(pxIcon);*/

	// Select the first tab
	pxTabs->SelectTab( 0 );

	SetCurrentParentDialog( NULL );

	m_uID = TOOLS_OVERLAY_SOUNDMIXER;
	Asura_Overlay_System::SubmitNewOverlay(this);
}


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

	// Update the mixer name dialog if necessary
	if( s_bShouldUpdateMixerChannelNameDialog && s_pxMixerChannelNameDialog  )
	{
		if( s_xSelectedMixerChannel.m_xChannelHash != ASURA_HASH_ID_UNSET )
		{
			const Asura_Sound_Mixer_Channel* pxChannel = Asura_Sound_Mixer_System::GetChannelByHash( s_xSelectedMixerChannel.m_xChannelHash );

			if( pxChannel )
			{
				s_pxMixerChannelNameDialog->SetName( pxChannel->GetName() );
			}
		}
		else
		{
			s_pxMixerChannelNameDialog->SetName( "No mixer channel selected" );
		}
	}

	if( s_bMixerPresetModified )
	{
		// Copy the changes to the preset
		Asura_Sound_Mixer_Preset* pxPreset = Asura_Sound_Mixer_System::GetPresetByHash( GetSelectedPreset() );
		if( pxPreset )
		{
			pxPreset->SetPriority		( static_cast<u_char>(s_uPresetPriority) );
			pxPreset->SetFadeInTime		( s_fPresetFadeInTime ); 
			pxPreset->SetFadeOutTime	( s_fPresetFadeOutTime );
			pxPreset->SetLifeTime		( s_fPresetLifeTime );
			pxPreset->SetDSPChain		( s_uPresetDSP );

			Tools_SoundEvent_System::SetMixersModified();

			s_bMixerPresetModified = false;
		}
	}

	// Check if the user selected another preset
	if( s_pxMixerPresetListDialog && s_pxMixerPresetListDialog->SelectionChanged() )
	{
		// Save the selected mixer and clear it
		SaveMixerChannelIfModified();
		ClearSelectedMixerChannel();

		// Load this preset
		s_pxMixerPresetListDialog->ClearSelectionChangedFlag();
		LoadPreset();
	}
}


void Tools_SoundMixer_Overlay::SaveMixerChannel ( )
{
	const Asura_Sound_Mixer_Channel* pxChannel = Asura_Sound_Mixer_System::GetChannelByHash( s_xSelectedMixerChannel.m_xChannelHash );

	if( !pxChannel )
	{
		return;
	}

	s_xSelectedMixerChannel.CopyToMixerChannel( s_xSelectedMixerChannel.m_xChannelHash );
	
	s_xSelectedMixerChannel.m_bModified = false;
	Tools_SoundEvent_System::SetMixersModified();

	// We need to update the 'include in preset' value on the dialog since it is not calculated on the fly
	Tools_MixerChannel_Dialog* pxDialog = Tools_MixerChannel_Dialog::FindMixerDialog( s_xSelectedMixerChannel.m_xChannelHash );
	if( pxDialog )
	{
		pxDialog->SetIncludedInPreset( s_xSelectedMixerChannel.m_bIncludeInPreset );
	}
}


void Tools_SoundMixer_Overlay::CreateMixerChannel()
{
	GetString_Overlay xOverlay("New Mixer Channel Name :","");
	while( xOverlay.DoModal() )
	{
		const char* szName = xOverlay.GetString();

		if(*szName)
		{
			//make sure this mixer channel doesn't already exist
			Asura_Hash_ID uChannelHash = Asura_GetHashID( szName );

			if( uChannelHash == uMIXER_HASHID_MASTER || Asura_Sound_Mixer_System::FindChannel( uChannelHash ) )
			{
				ShowMessageOverlay("A mixer channel already exists with that name.");
				continue;
			}

			new Asura_Sound_Mixer_Channel( szName );
			s_xSelectedMixerChannel.SetDefaults();
			s_xSelectedMixerChannel.m_xChannelPresetHash = GetSelectedPreset();
			s_xSelectedMixerChannel.CopyToMixerChannel( Asura_GetHashID( szName ) );
			
			//Add the new channel to the default preset (it will have been automatically added to the currently selected preset as well)
			if( GetSelectedPreset() != Asura_GetHashID("default") )
			{
				Asura_Sound_Mixer_Preset* pxPreset = Asura_Sound_Mixer_System::GetPresetByHash( Asura_GetHashID("default") );
				if(!pxPreset)
				{
					//default preset doesn't exist yet
					pxPreset = new Asura_Sound_Mixer_Preset("default");
				}
				pxPreset->AddChannel( s_xSelectedMixerChannel.m_xSettings, szName );
			}
			
			//RAR 26/10/2006 - Let tools system know that there has been at least one mixer , so it is okay to create files when using SaveAll()
			Tools_SoundEvent_System::SetEverHadMixers();
			Tools_SoundEvent_System::SetMixersModified();

			// Repopulate the dialog
			if( s_pxMixerTreeDialog )
			{
				s_pxMixerTreeDialog->SetMixerPreset( GetSelectedPreset() );
				s_pxMixerTreeDialog->SetDialogPositions( s_axDialogPositions, TOOLS_MIXERS_MAX_DIALOG_POSITIONS );

				Tools_MixerChannel_Dialog* pxDialog = Tools_MixerChannel_Dialog::FindMixerDialog( uChannelHash );

				if( pxDialog )
				{
					// TODO: Move this dialog to somewhere convenient for the user
					//pxDialog->m_xOffset = s_pxMixerTreeDialog->m_xOffset;
				}
			}
			// Select this mixer channel
			s_bShouldUpdateMixerChannelNameDialog = true;
			s_xSelectedMixerChannel.CopyFromMixerChannel( uChannelHash );

			break;
		}
	}
}


void Tools_SoundMixer_Overlay::DeleteMixerChannel()
{
	if( s_xSelectedMixerChannel.m_xChannelHash == ASURA_HASH_ID_UNSET )
	{
		// No mixer selected
		return;
	}

	const Asura_Sound_Mixer_Channel* pxChannelToDelete = Asura_Sound_Mixer_System::GetChannelByHash( s_xSelectedMixerChannel.m_xChannelHash );

	if( !pxChannelToDelete )
	{
		return;
	}

	if( !ShowYesNoOverlay( "Warning\n\nAre you sure you want to delete the mixer channel '%s'?", pxChannelToDelete->GetName() ) )
	{
		return;
	}

	//get the channel that will replace it for any other mixers/events using it
	const char* szReplacementChannel = pxChannelToDelete->GetOutputMixerName();
	
	//Check for any events using the mixer
	for(u_int i=0;i<Asura_Sound_Event_Storage::GetNumberOfEvents();i++)
	{
		Asura_Sound_Event* pxEvent = Asura_Sound_Event_Storage::GetEventByIndex(i);
		if(!stricmp( pxChannelToDelete->GetName(), pxEvent->GetMixerChannelName()))
		{
			Tools_SoundEvent_System::SetModified(pxEvent);
			pxEvent->SetMixerChannelName(szReplacementChannel);

			//Update the tab for this event if it was being edited
			Tools_SoundEvent_Overlay::UpdateEventFromAsuraData(pxEvent->GetHashID());
		}
	}

	//Update any mixers that send output to this
	for(u_int i=0;i<Asura_Sound_Mixer_System::GetNumberOfChannels();i++)
	{
		Asura_Sound_Mixer_Channel* pxChannel = Asura_Sound_Mixer_System::GetChannelByIndex(i);
		if( !stricmp( pxChannelToDelete->GetName(), pxChannel->GetOutputMixerName() ) )
		{
			pxChannel->SetOutputMixerName(szReplacementChannel);
		}
	}

	//Remove the mixer from any presets
	for(u_int i=0;i<Asura_Sound_Mixer_System::GetNumberOfPresets();i++)
	{
		Asura_Sound_Mixer_Preset* pxPreset = Asura_Sound_Mixer_System::GetPresetByIndex(i);
		pxPreset->RemoveChannel( s_xSelectedMixerChannel.m_xChannelHash );
	}

	#ifdef ASURA_USE_DIALOGUE_SYSTEM
	//Check for any dialogue audio templates using the mixer
	for(u_int i=0;i<Asura_Dialogue_Storage::GetNumberOfLineTemplates();i++)
	{
		Asura_Dialogue_Line_Template* pxTemplate = Asura_Dialogue_Storage::GetLineTemplateByIndex(i);
		if(pxTemplate->GetMixerChannel() == pxChannelToDelete)
		{
			pxTemplate->m_xMixingChannelName = szReplacementChannel;
			pxTemplate->SetMixerChannel(Asura_Sound_Mixer_System::GetChannelByHash(Asura_GetHashID(szReplacementChannel)));
			Tools_Dialogue_System::SetAudioTemplatesModified();
		}
	}
	#endif //ASURA_USE_DIALOGUE_SYSTEM


	//Delete and save
	delete pxChannelToDelete;
	Tools_SoundEvent_System::SetMixersModified();

	// Delete the dialog for this channel
	s_pxMixerTreeDialog->DeleteDialog( s_xSelectedMixerChannel.m_xChannelHash );

	s_xSelectedMixerChannel.m_xChannelHash = ASURA_HASH_ID_UNSET;
	s_bShouldUpdateMixerChannelNameDialog = true;
	
}


void Tools_SoundMixer_Overlay::RenameMixerChannel()
{
	//get the channel being renamed
	Asura_Hash_ID uChannelHash = s_xSelectedMixerChannel.m_xChannelHash;
	Asura_Sound_Mixer_Channel* pxChannelToRename = Asura_Sound_Mixer_System::GetChannelByHash(uChannelHash);
	
	if(!pxChannelToRename) 
	{
		return;
	}

	const char* szNewName = 0;
	Asura_Hash_ID uOldHash = uChannelHash;
	Asura_Hash_ID uNewHash = 0;
	bool bOkay = false;

	const char* szChannelName = pxChannelToRename->GetName();

	//ask for a new name
	GetString_Overlay xStringOverlay("New name :",szChannelName);
	while(xStringOverlay.DoModal())
	{
		szNewName = xStringOverlay.GetString();
		if( !szNewName || !*szNewName )
		{
			return;
		}

		if( !strcmp( szNewName, szChannelName ) )
		{
			//name not changed
			return;
		}

		//make sure no existing mixer exists with this hash
		uNewHash = Asura_GetHashID(szNewName);

		if( uOldHash != uNewHash && Asura_Sound_Mixer_System::GetChannelByHash( uNewHash ) )
		{
			ShowMessageOverlay("A mixer channel already exists using the same hash ID.");
			continue;
		}

		bOkay = true;
		break;
	}
	if(!bOkay) 
	{
		return;
	}

	//Update the mixer and its position in the storage system
	if( !Asura_Sound_Mixer_System::RenameChannel( szChannelName, szNewName ) )
	{
		Asura_Assert(0,"Error renaming mixer");
		return;
	}
	
	//Update any events using the mixer
	for(u_int i=0;i<Asura_Sound_Event_Storage::GetNumberOfEvents();i++)
	{
		Asura_Sound_Event* pxEvent = Asura_Sound_Event_Storage::GetEventByIndex(i);
		if(!stricmp(szChannelName,pxEvent->GetMixerChannelName()))
		{
			Tools_SoundEvent_System::SetModified(pxEvent);
			pxEvent->SetMixerChannelName(szNewName);

			//Update the tab for this event if it was being edited
			Tools_SoundEvent_Overlay::UpdateEventFromAsuraData(pxEvent->GetHashID());
		}
	}

	//Update any mixers that send output to this
	for(u_int i=0;i<Asura_Sound_Mixer_System::GetNumberOfChannels();i++)
	{
		Asura_Sound_Mixer_Channel* pxChannel = Asura_Sound_Mixer_System::GetChannelByIndex(i);
		if(!stricmp(szChannelName,pxChannel->GetOutputMixerName()))
		{
			pxChannel->SetOutputMixerName(szNewName);
		}
	}

	//Replace the mixer in any presets
	for(u_int i=0;i<Asura_Sound_Mixer_System::GetNumberOfPresets();i++)
	{
		Asura_Sound_Mixer_Preset* pxPreset = Asura_Sound_Mixer_System::GetPresetByIndex(i);
		pxPreset->ReplaceChannel(szChannelName,szNewName);
	}

	Tools_SoundEvent_System::SetMixersModified();
	
	s_xSelectedMixerChannel.CopyFromMixerChannel( Asura_GetHashID( szNewName ) );
	s_bShouldUpdateMixerChannelNameDialog = true;
}


void Tools_SoundMixer_Overlay::NewPreset ()
{
	const char* szName = NULL;
	GetString_Overlay xNewPresetNameOverlay("Preset Name :","");
	while(xNewPresetNameOverlay.DoModal())
	{
		szName = xNewPresetNameOverlay.GetString();

		if(*szName)
		{
			//make sure this preset doesn't already exist
			Asura_Hash_ID uHash = Asura_GetHashID(szName);

			if(Asura_Sound_Mixer_System::FindPreset(uHash))
			{
				ShowMessageOverlay("A preset already exists with that name.");
				continue;
			}
			break;
		}
	}

	if (!szName)
	{
		return;
	}

	Asura_Sound_Mixer_Preset* pxPresetToSave = new Asura_Sound_Mixer_Preset( szName );

	// clear out existing values
	pxPresetToSave->Reset();

	s_xSelectedMixerChannel.SetDefaults();

	// go through all mixer channels and add them to the preset
	const u_int uNumChannels = Asura_Sound_Mixer_System::GetNumberOfChannels();
	for( u_int uChannel = 0; uChannel < uNumChannels; ++uChannel )
	{
		const Asura_Sound_Mixer_Channel* pxChannel = Asura_Sound_Mixer_System::GetChannelByIndex( uChannel );
		if( pxChannel )
		{
			pxPresetToSave->AddChannel( s_xSelectedMixerChannel.m_xSettings, pxChannel->GetName() );
		}
	}

	pxPresetToSave->SetPriority(1);
	pxPresetToSave->SetFadeInTime( 1.0f ); 
	pxPresetToSave->SetFadeOutTime( 1.0f );
	pxPresetToSave->SetLifeTime( 0.0f );
	pxPresetToSave->SetDSPChain( ASURA_HASH_ID_UNSET );

	Tools_SoundEvent_System::SetMixersModified();

	if( s_pxMixerPresetListDialog )
	{
		// Update the preset list
		s_pxMixerPresetListDialog->UpdateMixerPresetList( Asura_GetHashID( szName ) );
		LoadPreset();
	}
}


void Tools_SoundMixer_Overlay::LoadPreset()
{
	const Asura_Hash_ID xPresetHash = GetSelectedPreset();

	// Repopulate the graphical display
	s_xSelectedMixerChannel.m_xChannelPresetHash = xPresetHash;

	s_pxMixerTreeDialog->SetMixerPreset( xPresetHash );

	// Set the dialoge positions based on the saved values
	s_pxMixerTreeDialog->SetDialogPositions( s_axDialogPositions, TOOLS_MIXERS_MAX_DIALOG_POSITIONS );

	const Asura_Sound_Mixer_Preset* pxPreset = Asura_Sound_Mixer_System::FindPreset( xPresetHash );

	s_uPresetPriority		= static_cast<u_int>( pxPreset->GetPriority() );
	s_fPresetFadeInTime		= pxPreset->GetFadeInTime();
	s_fPresetFadeOutTime	= pxPreset->GetFadeOutTime();
	s_fPresetLifeTime		= pxPreset->GetLifeTime();
	s_uPresetDSP			= pxPreset->GetDSPChain();
}


void Tools_SoundMixer_Overlay::DeletePreset()
{
	Asura_Sound_Mixer_Preset* pxPresetToDelete = Asura_Sound_Mixer_System::GetPresetByHash( GetSelectedPreset() );

	Asura_Assert(pxPresetToDelete,"Failed to find preset to delete");
	if(! pxPresetToDelete )
	{
		return;
	}

	if( !ShowYesNoOverlay( "Warning\n\nAre you sure you want to delete the preset '%s'?", pxPresetToDelete->GetName() ) )
	{
		return;
	}

	//Kill the preset.
	delete pxPresetToDelete;
	Tools_SoundEvent_System::SetMixersModified();

	if( s_pxMixerPresetListDialog )
	{
		// Update the preset list
		s_pxMixerPresetListDialog->UpdateMixerPresetList( Asura_GetHashID( "default" ) );
		LoadPreset();
	}
}


bool Tools_SoundMixer_Overlay::Callback_RequestSelection	( Asura_Hash_ID xChannelHash )
{
	// The selected mixer channel may be changing
	s_bShouldUpdateMixerChannelNameDialog = true;

	SaveMixerChannelIfModified ();

	// Can return false here to prevent selection (for example, if we wanted to prompt)
	return true;
}


void Tools_SoundMixer_Overlay::Callback_PositionChanged	( Asura_Hash_ID xChannelHash, Asura_Vector_2 xPosition )
{
	Tools_MixerChannel_Dialog* pxChannelDialog = Tools_MixerChannel_Dialog::FindMixerDialog( xChannelHash );
	if( s_pxMixerTreeDialog && pxChannelDialog )
	{
		s_pxMixerTreeDialog->UpdateSize( pxChannelDialog );
		s_pxMixerTreeDialog->UpdateSize();
	}

	// Save to our list of dialog positions
	s_pxMixerTreeDialog->GetDialogPositions( s_axDialogPositions, TOOLS_MIXERS_MAX_DIALOG_POSITIONS );
}


Asura_Hash_ID Tools_SoundMixer_Overlay::GetSelectedPreset ()
{
	return ( s_pxMixerPresetListDialog ? s_pxMixerPresetListDialog->GetSelectedPresetHash() : ASURA_HASH_ID_UNSET );
}


void Tools_SoundMixer_Overlay::SaveMixerChannelIfModified	()
{
	if( ( s_xSelectedMixerChannel.m_xChannelHash != ASURA_HASH_ID_UNSET ) && s_xSelectedMixerChannel.m_bModified )
	{
		_SaveMixerChannel();
	}
}


void Tools_SoundMixer_Overlay::ClearSelectedMixerChannel ()
{
	s_xSelectedMixerChannel.SetDefaults();
}

// _______________________________ Tools_MixerTree_Dialog __________________________________


Tools_MixerTree_Dialog::Tools_MixerTree_Dialog  ( const Asura_Vector_2& xOffset, const float fWidth, const float fHeight ) : Asura_Subwindow_Dialog ( xOffset, fWidth, fHeight, false )
{
	m_xMixerPreset = ASURA_HASH_ID_UNSET;
}


Tools_MixerTree_Dialog::~Tools_MixerTree_Dialog ()
{

}

bool Tools_MixerTree_Dialog::IsPointInDialog (const Asura_Vector_2& xOrigin,const Asura_Vector_2& xPos) const
{
	if(		( xPos.x > m_xOffset.x + xOrigin.x )
		&&	( xPos.x < m_xOffset.x + xOrigin.x + GetWidth() )
		&&	( xPos.y > m_xOffset.y + xOrigin.y )
		&&	( xPos.y < m_xOffset.y + xOrigin.y + GetHeight() ) )
	{
		return true;
	}

	return false;
}

void Tools_MixerTree_Dialog::SetMixerPreset ( const Asura_Hash_ID xPreset )
{
	// Clear down all of the existing dialogs
	Asura_List<Tools_MixerChannel_Dialog>* pxList = Tools_MixerChannel_Dialog::GetDialogList();

	while( !pxList->IsEmpty() )
	{
		m_pxOverlay->DeleteSingleDialog( pxList->GetHeadObject() );
	}
	
	const Asura_Sound_Mixer_Preset* pxPreset = Asura_Sound_Mixer_System::FindPreset( xPreset );

	if( !pxPreset )
	{
		return;
	}

	int aiChannelColumns[32];	// What y position are we rendering each mixer channel

	Asura_Mem::Set( aiChannelColumns, 0, sizeof( int ) * 32 );

	const u_int uNumChannels = Asura_Sound_Mixer_System::GetNumberOfChannels();

	for( u_int uChannel = 0; uChannel < uNumChannels; ++uChannel )
	{
		const Asura_Sound_Mixer_Channel* pxChannel = Asura_Sound_Mixer_System::GetChannelByIndex( uChannel );
		if( pxChannel )
		{
			int iDepth = GetMixerChannelDepth( pxChannel->GetHashID() );
			
			Asura_Vector_2 xOffset;

			// Calculate the position for this dialog in pixels
			xOffset.x = ( fMIXER_CHANNEL_DIALOG_SPACING_X + static_cast<float>( iDepth ) * ( fMIXER_CHANNEL_DIALOG_WIDTH + fMIXER_CHANNEL_DIALOG_SPACING_X ) );
			xOffset.y = ( fMIXER_CHANNEL_DIALOG_SPACING_Y + static_cast<float>( aiChannelColumns[ iDepth ] ) * ( fMIXER_CHANNEL_DIALOG_HEIGHT + fMIXER_CHANNEL_DIALOG_SPACING_Y ) );

			// convert to weird tools coords
			xOffset.x /= ASURA_TEXT_OUTPUT_FONT_WIDTH;
			xOffset.y /= ASURA_TEXT_OUTPUT_FONT_HEIGHT;

			Tools_MixerChannel_Dialog* pxChannelDialog = new Tools_MixerChannel_Dialog( pxChannel->GetHashID(), xOffset );

			if( !pxChannelDialog )
			{
				continue;
			}

			bool bIncludeInPreset = false;

			const Asura_Sound_Mixer_ChannelPreset* pxChannelPreset = pxPreset->FindChannel( pxChannel->GetHashID() );

			if( pxChannelPreset )
			{
				bIncludeInPreset = true;
			}

			pxChannelDialog->SetIncludedInPreset( bIncludeInPreset );
			
			AddDialog( pxChannelDialog );
			UpdateSize( pxChannelDialog );

			++aiChannelColumns[ iDepth ];
		}
	}

	// Add the master mixer
	int iDepth = GetMixerChannelDepth( Asura_GetHashID( "Master" ) );

	Asura_Vector_2 xOffset;

	// Calculate the position for this dialog in pixels
	xOffset.x = ( fMIXER_CHANNEL_DIALOG_SPACING_X + static_cast<float>( iDepth ) * ( fMIXER_CHANNEL_DIALOG_WIDTH + fMIXER_CHANNEL_DIALOG_SPACING_X ) );
	xOffset.y = ( fMIXER_CHANNEL_DIALOG_SPACING_Y + static_cast<float>( aiChannelColumns[ iDepth ] ) * ( fMIXER_CHANNEL_DIALOG_HEIGHT + fMIXER_CHANNEL_DIALOG_SPACING_Y ) );

	// convert to weird tools coords
	xOffset.x /= ASURA_TEXT_OUTPUT_FONT_WIDTH;
	xOffset.y /= ASURA_TEXT_OUTPUT_FONT_HEIGHT;

	Tools_MixerChannel_Dialog* pxChannelDialog = new Tools_MixerChannel_Dialog( Asura_GetHashID( "Master" ), xOffset );
	AddDialog( pxChannelDialog );
	UpdateSize( pxChannelDialog );
	pxChannelDialog->SetAsMaster();

	UpdateSize();
}


void Tools_MixerTree_Dialog::SetDialogPositions ( const Tools_Mixer_DialogPos* pxArray, const int iArraySize )
{
	for( int iEntry = 0; iEntry < iArraySize; ++iEntry )
	{
		if( pxArray[iEntry].m_xMixerChannel == ASURA_HASH_ID_UNSET )
		{
			continue;
		}

		Tools_MixerChannel_Dialog* pxDialog = Tools_MixerChannel_Dialog::FindMixerDialog( pxArray[iEntry].m_xMixerChannel );

		if( pxDialog )
		{
			pxDialog->MoveTo( pxArray[iEntry].m_xPosition );
			UpdateSize( pxDialog );
		}
	}

	UpdateSize();
}


void Tools_MixerTree_Dialog::GetDialogPositions ( Tools_Mixer_DialogPos* pxArray, const int iArraySize )
{
	// Clear the list
	for( int iEntry = 0; iEntry < TOOLS_MIXERS_MAX_DIALOG_POSITIONS; ++iEntry )
	{
		pxArray[ iEntry ].m_xMixerChannel = ASURA_HASH_ID_UNSET; 
		pxArray[ iEntry ].m_xPosition.Zero();
	}

	Asura_List<Tools_MixerChannel_Dialog>* pxList = Tools_MixerChannel_Dialog::GetDialogList();

	Asura_List_It<Tools_MixerChannel_Dialog> xIter( pxList );

	int iEntry = 0;
	for( ; !xIter.Done(); xIter.Next() )
	{
		Tools_MixerChannel_Dialog* pxDialog = xIter.GetCurrent();
		if( pxDialog )
		{
			pxArray[iEntry].m_xMixerChannel = pxDialog->GetChannelHash();
			pxArray[iEntry].m_xPosition		= pxDialog->GetOffset ();

			++iEntry;
		}
	}
}


int Tools_MixerTree_Dialog::GetMixerChannelDepth ( const Asura_Hash_ID xChannelHash )
{
	// This function tells us what depth in the mixer tree this channel has (0 based)
	// slightly rubbish algorithm, cant really be bothered to make it really complicated since
	// the user is going to re-arrange the channels and save out anyway

	if( Asura_GetHashID("Master") == xChannelHash )
	{
		return 2;
	}

	int iDepth = 0;

	// Check if we are depth zero (no mixers output to this)
	for( u_int uChannel = 0; uChannel < Asura_Sound_Mixer_System::GetNumberOfChannels(); ++uChannel )
	{
		Asura_Sound_Mixer_Channel* pxOtherChannel = Asura_Sound_Mixer_System::GetChannelByIndex( uChannel );
		if(		pxOtherChannel 
			&&	( pxOtherChannel->GetHashID() != xChannelHash )
			&&	( Asura_GetHashID( pxOtherChannel->GetOutputMixerName() ) == xChannelHash ) )
		{
			// Not depth zero
			++ iDepth;
			break;
		}
	}

	return iDepth;
}


void Tools_MixerTree_Dialog::Render( const Asura_Vector_2& xOrigin )
{
	Asura_Vector_2 xActualOrigin = xOrigin + m_xOffset;

	xActualOrigin.x *= ASURA_TEXT_OUTPUT_FONT_WIDTH;
	xActualOrigin.y *= ASURA_TEXT_OUTPUT_FONT_HEIGHT;

	// Background
	Asura_Render::Overlay.Colour			= uMIXER_CHANNEL_SUBWINDOW_BACKGROUND_COLOUR | Asura_OverlayBrightness;
	Asura_Render::Overlay.TextureIndex		= ASURA_TEXTURE_NONE;
	Asura_Render::Overlay.TranslucencyMode	= ASURA_TRANSLUCENCY_NORMAL;
	Asura_Render::Overlay.SetRectXY ( xActualOrigin.x, xActualOrigin.y, GetWidth() * ASURA_TEXT_OUTPUT_FONT_WIDTH, GetHeight() * ASURA_TEXT_OUTPUT_FONT_HEIGHT );
	Asura_Render::Overlay.Render();
	Asura_Render::FlushDisplay();

	Asura_Subwindow_Dialog::Render( xOrigin );
}

void Tools_MixerTree_Dialog::Update( const Asura_Vector_2& xOrigin )
{
	Asura_Subwindow_Dialog::Update( xOrigin );

	float fCursorX, fCursorY;
	Asura_Cursor::GetScreenCoords(fCursorX, fCursorY);

	if( IsPointInDialog( xOrigin, Asura_Vector_2( fCursorX, fCursorY ) ) )
	{
		Tools_MixerChannel_Dialog::SetDialogsAcceptMouseInput( true );
	}
	else
	{
		Tools_MixerChannel_Dialog::SetDialogsAcceptMouseInput( false );
	}
}


void Tools_MixerTree_Dialog::DeleteDialog ( const Asura_Hash_ID xChannelHash )
{
	Tools_MixerChannel_Dialog* pxDialog = Tools_MixerChannel_Dialog::FindMixerDialog( xChannelHash );
	if( pxDialog )
	{
		m_pxOverlay->DeleteSingleDialog( pxDialog );
	}
}

// _______________________________ Tools_MixerChannel_Dialog __________________________________

Tools_MixerChannel_Dialog::Tools_MixerChannel_Dialog ( const Asura_Hash_ID xChannelHash, const Asura_Vector_2 xOffset ) : Asura_Dialog ( 0, xOffset )
{
	m_xChannelHash = xChannelHash;
	m_xOrigin = xOffset;
	m_bIsMaster = false;
	m_bIncludedInPreset = true;

	m_xMaxCoord.x = xOffset.x + fMIXER_CHANNEL_DIALOG_WIDTH / ASURA_TEXT_OUTPUT_FONT_WIDTH;
	m_xMaxCoord.y = xOffset.y + fMIXER_CHANNEL_DIALOG_HEIGHT / ASURA_TEXT_OUTPUT_FONT_WIDTH;

	m_bDragging = false;
	m_xDragOrigin.Zero();

	m_bSelectingOutput = false;

	m_bHighlightName	= false;
	m_bHighlightBody	= false;
	m_bHighlightOutNode = false;
	m_bHighlightInNode	= false;

	s_xMixerDialogList.AddToList( this );
}


Tools_MixerChannel_Dialog::~Tools_MixerChannel_Dialog	()
{
	if( m_bDragging )
	{
		m_bDragging = false;
		s_bAnyChannelBeingDragged = false;
	}
	if( m_bSelectingOutput )
	{
		m_bSelectingOutput = false;
		s_bAnyChannelSelectingOutput = false;
	}

	s_xMixerDialogList.RemoveFromList( this );
}


void Tools_MixerChannel_Dialog::Render ( const Asura_Vector_2& xOrigin )
{
	m_xOrigin = xOrigin;

	Asura_Dialog::Render( xOrigin );

	if( !s_pxSelectedChannelData )
	{
		return;
	}

	const Asura_Sound_Mixer_Preset* pxPreset = Asura_Sound_Mixer_System::FindPreset( s_pxSelectedChannelData->m_xChannelPresetHash );

	if( !pxPreset )
	{
		return;
	}
	const Asura_Sound_Mixer_Channel* pxChannel = Asura_Sound_Mixer_System::GetChannelByHash( m_xChannelHash );
	const Asura_Sound_Mixer_ChannelPreset* pxChannelPreset = pxPreset->FindChannel( m_xChannelHash );

	if( !pxChannel && !IsMaster() )
	{
		return;
	}

	const bool bIncludedInPreset = IsSelected() ? s_pxSelectedChannelData->m_bIncludeInPreset : m_bIncludedInPreset;

	Asura_Vector_2 xActualOrigin = xOrigin + m_xOffset;

	xActualOrigin.x *= ASURA_TEXT_OUTPUT_FONT_WIDTH;
	xActualOrigin.y *= ASURA_TEXT_OUTPUT_FONT_HEIGHT;

	u_int uCol = 0;

	// Shadow
	uCol = uMIXER_CHANNEL_DIALOG_SHADOW_COLOUR;
	if( !bIncludedInPreset )
	{
		uCol = ReduceOpacity( uCol );
	}

	Asura_Render::Overlay.Colour			= uCol;
	Asura_Render::Overlay.TextureIndex		= ASURA_TEXTURE_NONE;
	Asura_Render::Overlay.TranslucencyMode	= ASURA_TRANSLUCENCY_NORMAL;
	Asura_Render::Overlay.SetRectXY ( xActualOrigin.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE + 3.0f, xActualOrigin.y + 3.0f, fMIXER_CHANNEL_DIALOG_WIDTH - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 2.0f, fMIXER_CHANNEL_DIALOG_HEIGHT );
	Asura_Render::Overlay.Render();
	Asura_Render::FlushDisplay();

	// Background
	u_int uBackColour = uMIXER_CHANNEL_DIALOG_BASE_COLOUR;
	if( IsSelected() )
	{
		uBackColour = InterpolateColour( uMIXER_CHANNEL_DIALOG_BASE_PULSE1_COLOUR, uMIXER_CHANNEL_DIALOG_BASE_PULSE2_COLOUR, ( Asura_Maths::Sine( Asura_Timers::GetLocalUnscaledTime() * 5.0f ) + 1.0f ) * 0.5f );
	}
	else if( m_bHighlightBody )
	{
		uBackColour = uMIXER_CHANNEL_DIALOG_BASE_SEL_COLOUR;
	}

	uCol = uBackColour | Asura_OverlayBrightness;
	if( !bIncludedInPreset )
	{
		uCol = ReduceOpacity( uCol );
	}

	Asura_Render::Overlay.Colour			= uCol;
	Asura_Render::Overlay.TextureIndex		= ASURA_TEXTURE_NONE;
	Asura_Render::Overlay.TranslucencyMode	= ASURA_TRANSLUCENCY_NORMAL;
	Asura_Render::Overlay.SetRectXY ( xActualOrigin.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE, xActualOrigin.y, fMIXER_CHANNEL_DIALOG_WIDTH - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 2.0f, fMIXER_CHANNEL_DIALOG_HEIGHT );
	Asura_Render::Overlay.Render();
	Asura_Render::FlushDisplay();

	uCol = uMIXER_CHANNEL_DIALOG_BORDER_COLOUR | Asura_OverlayBrightness;
	
	// Border
	Asura_Render::Render2DLine( xActualOrigin.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE, xActualOrigin.y, xActualOrigin.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE, xActualOrigin.y + fMIXER_CHANNEL_DIALOG_HEIGHT, uCol );																	// Left
	Asura_Render::Render2DLine( xActualOrigin.x + fMIXER_CHANNEL_DIALOG_WIDTH - fMIXER_CHANNEL_DIALOG_NODE_SIZE, xActualOrigin.y, xActualOrigin.x + fMIXER_CHANNEL_DIALOG_WIDTH - fMIXER_CHANNEL_DIALOG_NODE_SIZE, xActualOrigin.y + fMIXER_CHANNEL_DIALOG_HEIGHT, uCol );		// Right
	Asura_Render::Render2DLine( xActualOrigin.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE, xActualOrigin.y, xActualOrigin.x + fMIXER_CHANNEL_DIALOG_WIDTH - fMIXER_CHANNEL_DIALOG_NODE_SIZE, xActualOrigin.y, uMIXER_CHANNEL_DIALOG_BORDER_COLOUR | Asura_OverlayBrightness );																	// Top
	Asura_Render::Render2DLine( xActualOrigin.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE, xActualOrigin.y + fMIXER_CHANNEL_DIALOG_HEIGHT, xActualOrigin.x + fMIXER_CHANNEL_DIALOG_WIDTH - fMIXER_CHANNEL_DIALOG_NODE_SIZE, xActualOrigin.y + fMIXER_CHANNEL_DIALOG_HEIGHT, uCol );	// Bottom

	uCol = ( m_bHighlightName ? uMIXER_CHANNEL_DIALOG_NAMETAG_SEL_COLOUR : uMIXER_CHANNEL_DIALOG_NAMETAG_COLOUR ) | Asura_OverlayBrightness;
	if( !bIncludedInPreset )
	{
		uCol = ReduceOpacity( uCol );
	}

	// Nametag
	Asura_Render::Overlay.Colour			= uCol;
	Asura_Render::Overlay.TextureIndex		= ASURA_TEXTURE_NONE;
	Asura_Render::Overlay.TranslucencyMode	= ASURA_TRANSLUCENCY_NORMAL;
	Asura_Render::Overlay.SetRectXY ( xActualOrigin.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE, xActualOrigin.y, fMIXER_CHANNEL_DIALOG_WIDTH - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 2.0f, fMIXER_CHANNEL_DIALOG_NAMETAG_HEIGHT );
	Asura_Render::Overlay.Render();
	Asura_Render::FlushDisplay();
	
	Asura_Render::Render2DLine( xActualOrigin.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE, xActualOrigin.y, xActualOrigin.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE, xActualOrigin.y + fMIXER_CHANNEL_DIALOG_NAMETAG_HEIGHT, uMIXER_CHANNEL_DIALOG_BORDER_COLOUR | Asura_OverlayBrightness );																// Left
	Asura_Render::Render2DLine( xActualOrigin.x + fMIXER_CHANNEL_DIALOG_WIDTH - fMIXER_CHANNEL_DIALOG_NODE_SIZE, xActualOrigin.y, xActualOrigin.x + fMIXER_CHANNEL_DIALOG_WIDTH - fMIXER_CHANNEL_DIALOG_NODE_SIZE, xActualOrigin.y + fMIXER_CHANNEL_DIALOG_NAMETAG_HEIGHT, uMIXER_CHANNEL_DIALOG_BORDER_COLOUR | Asura_OverlayBrightness );	// Right
	Asura_Render::Render2DLine( xActualOrigin.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE, xActualOrigin.y, xActualOrigin.x + fMIXER_CHANNEL_DIALOG_WIDTH - fMIXER_CHANNEL_DIALOG_NODE_SIZE, xActualOrigin.y, uMIXER_CHANNEL_DIALOG_BORDER_COLOUR | Asura_OverlayBrightness );													// Top
	Asura_Render::Render2DLine( xActualOrigin.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE, xActualOrigin.y + fMIXER_CHANNEL_DIALOG_NAMETAG_HEIGHT, xActualOrigin.x + fMIXER_CHANNEL_DIALOG_WIDTH - fMIXER_CHANNEL_DIALOG_NODE_SIZE, xActualOrigin.y + fMIXER_CHANNEL_DIALOG_NAMETAG_HEIGHT, uMIXER_CHANNEL_DIALOG_BORDER_COLOUR | Asura_OverlayBrightness );				// Bottom

	uCol = Asura_OverlayBrightness | 0x00FFFFFF;
	if( !bIncludedInPreset )
	{
		uCol = ReduceOpacity( uCol );
	}

	// Mixer Name
	if( IsMaster() )
	{
		Asura_Text_Output::Print ( ( xActualOrigin.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE + 4.0f ) / ASURA_TEXT_OUTPUT_FONT_WIDTH, ( xActualOrigin.y + 3.0f ) / ASURA_TEXT_OUTPUT_FONT_HEIGHT, uCol, ASURA_TEXT_FORMAT_SMALLFONT, "Master" );
	}
	else
	{
		Asura_Text_Output::Print ( ( xActualOrigin.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE + 4.0f ) / ASURA_TEXT_OUTPUT_FONT_WIDTH, ( xActualOrigin.y + 3.0f ) / ASURA_TEXT_OUTPUT_FONT_HEIGHT, uCol, ASURA_TEXT_FORMAT_SMALLFONT, pxChannel->GetName() );
	}

	if( !bIncludedInPreset )
	{
		const float fCrossPosX = xActualOrigin.x + fMIXER_CHANNEL_DIALOG_WIDTH * 0.5f;
		const float fCrossPosY = xActualOrigin.y + fMIXER_CHANNEL_DIALOG_HEIGHT * 0.65f;

		Asura_Render::Render2DLine( fCrossPosX - 10.0f, fCrossPosY - 10.0f, fCrossPosX + 10.0f, fCrossPosY + 10.0f, 0x00FF0000 | Asura_OverlayBrightness );
		Asura_Render::Render2DLine( fCrossPosX + 10.0f, fCrossPosY - 10.0f, fCrossPosX - 10.0f, fCrossPosY + 10.0f, 0x00FF0000 | Asura_OverlayBrightness );
	}
	else
	{
		// Solo?
		bool bSolo = false;
		if( !IsMaster() )
		{
			if( IsSelected() )
			{
				bSolo = s_pxSelectedChannelData->m_xSettings.m_bSolo;
			}
			else if( pxChannelPreset )
			{
				bSolo = pxChannelPreset->GetSettings().m_bSolo;
			}
			else
			{
				bSolo = pxChannel->IsSolo();
			}
		}
		
		if( bSolo  )
		{
			const float fIconRow	= 2.0f;
			const float fIconColumn	= 2.0f;

			Asura_Render::Overlay.TextureIndex = Tools_Dialog::GetIconTexture();
			Asura_Render::Overlay.TranslucencyMode = ASURA_TRANSLUCENCY_NORMAL;
			Asura_Render::Overlay.SetRectUV (0.0625f * fIconColumn, 0.0625f * fIconRow, 0.0625f, 0.0625f);
			Asura_Render::Overlay.SetRectXY ( ( xActualOrigin.x + fMIXER_CHANNEL_DIALOG_WIDTH ) - ( fMIXER_CHANNEL_DIALOG_NODE_SIZE + 16.0f ),  xActualOrigin.y + 1.0f, 16.0f, 16.0f);
			Asura_Render::Overlay.Colour = 0xFFFFFFFF;
			Asura_Render::Overlay.Render ();

			Asura_Render::FlushDisplay();
		}

		if( !IsMaster() )
		{
			// Draw the volume

			// Work out if we are muted
			bool bIsMuted = false;
			if( IsSelected() )
			{ 
				bIsMuted = s_pxSelectedChannelData->m_xSettings.m_bMute;
			}
			else if( pxChannelPreset )
			{
				bIsMuted = pxChannelPreset->GetSettings().m_bMute;
			}
			else
			{
				bIsMuted = pxChannel->IsMute();
			}

			// Work out where we should get the volume from
			float fVolume = 0.0f;
			if( IsSelected() )
			{
				fVolume = s_pxSelectedChannelData->m_xSettings.m_fVolume;
			}
			else if( pxChannelPreset )
			{
				fVolume = pxChannelPreset->GetSettings().m_fVolume;
			}
			else
			{
				fVolume = pxChannel->GetVolume();
			}

			const Asura_Vector_2 xVolumeOrigin ( xActualOrigin.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE + 5.0f, xActualOrigin.y + fMIXER_CHANNEL_DIALOG_NAMETAG_HEIGHT + 5.0f );
			const Asura_Vector_2 xVolumeDest   ( xActualOrigin.x + fMIXER_CHANNEL_DIALOG_WIDTH - ( fMIXER_CHANNEL_DIALOG_NODE_SIZE + 5.0f ), xActualOrigin.y + fMIXER_CHANNEL_DIALOG_NAMETAG_HEIGHT + 15.0f );

			if( bIsMuted )
			{
				Asura_Text_Output::Print ( xVolumeOrigin.x / ASURA_TEXT_OUTPUT_FONT_WIDTH, ( xVolumeOrigin.y - 1.0f ) / ASURA_TEXT_OUTPUT_FONT_HEIGHT, Asura_OverlayBrightness | 0x00FF1000, ASURA_TEXT_FORMAT_SMALLFONT, "Muted" );
			}
			else
			{
				Asura_Render::Overlay.Colour			= uMIXER_CHANNEL_DIALOG_VOLUME_BACK_COLOUR | Asura_OverlayBrightness;
				Asura_Render::Overlay.TextureIndex		= ASURA_TEXTURE_NONE;
				Asura_Render::Overlay.TranslucencyMode	= ASURA_TRANSLUCENCY_NORMAL;
				Asura_Render::Overlay.SetRectXY ( xVolumeOrigin.x, xVolumeOrigin.y, xVolumeDest.x - xVolumeOrigin.x, xVolumeDest.y - xVolumeOrigin.y );
				Asura_Render::Overlay.Render();
				Asura_Render::FlushDisplay();

				const float fVolPosX = ( xVolumeDest.x - xVolumeOrigin.x ) * fVolume * 0.75f;

				Asura_Render::Overlay.Colour			= uMIXER_CHANNEL_DIALOG_VOLUME_FILL_COLOUR | Asura_OverlayBrightness;
				Asura_Render::Overlay.TextureIndex		= ASURA_TEXTURE_NONE;
				Asura_Render::Overlay.TranslucencyMode	= ASURA_TRANSLUCENCY_NORMAL;
				Asura_Render::Overlay.SetRectXY ( xVolumeOrigin.x, xVolumeOrigin.y, fVolPosX, xVolumeDest.y - xVolumeOrigin.y );
				Asura_Render::Overlay.Render();
				Asura_Render::FlushDisplay();

				Asura_Render::Render2DLine( xVolumeOrigin.x + fVolPosX, xVolumeOrigin.y, xVolumeOrigin.x + fVolPosX, xVolumeDest.y, uMIXER_CHANNEL_DIALOG_VOLUME_LINE_COLOUR | Asura_OverlayBrightness ); // Volume Marker

				Asura_Render::Render2DLine( xVolumeOrigin.x, xVolumeOrigin.y, xVolumeOrigin.x, xVolumeDest.y, uMIXER_CHANNEL_DIALOG_BORDER_COLOUR | Asura_OverlayBrightness ); // Left
				Asura_Render::Render2DLine( xVolumeDest.x, xVolumeOrigin.y, xVolumeDest.x, xVolumeDest.y, uMIXER_CHANNEL_DIALOG_BORDER_COLOUR | Asura_OverlayBrightness ); // Right
				Asura_Render::Render2DLine( xVolumeOrigin.x, xVolumeOrigin.y, xVolumeDest.x, xVolumeOrigin.y, uMIXER_CHANNEL_DIALOG_BORDER_COLOUR | Asura_OverlayBrightness ); // Top
				Asura_Render::Render2DLine( xVolumeOrigin.x, xVolumeDest.y, xVolumeDest.x, xVolumeDest.y, uMIXER_CHANNEL_DIALOG_BORDER_COLOUR | Asura_OverlayBrightness ); // Bottom
			}

			// Draw the details text (These expressions got a bit long and ugly, sorry)
			char szDetails[32];

			bool bReverb		= ( IsSelected() ? s_pxSelectedChannelData->m_xSettings.m_bUseReverb			: ( pxChannelPreset ? pxChannelPreset->GetSettings().m_bUseReverb			: pxChannel->GetUseReverb() ) );
			bool bDistFilter	= ( IsSelected() ? s_pxSelectedChannelData->m_xSettings.m_bUseDistanceFilter	: ( pxChannelPreset ? pxChannelPreset->GetSettings().m_bUseDistanceFilter	: pxChannel->GetUseDistanceFilter() ) );
			bool bUseDoppler	= ( IsSelected() ? s_pxSelectedChannelData->m_xSettings.m_bUseDoppler			: ( pxChannelPreset ? pxChannelPreset->GetSettings().m_bUseDoppler			: pxChannel->GetUseDoppler() ) );
			bool bUseOcc		= ( IsSelected() ? s_pxSelectedChannelData->m_xSettings.m_bUseOcclusion			: ( pxChannelPreset ? pxChannelPreset->GetSettings().m_bUseOcclusion		: pxChannel->GetUseOcclusion() ) );
			bool bUseDSP		= ( IsSelected() ? s_pxSelectedChannelData->m_xSettings.m_bUseMasterDSP			: ( pxChannelPreset ? pxChannelPreset->GetSettings().m_bUseMasterDSP		: pxChannel->GetUseMasterDSP() ) );

			Asura_PrintfToBfr( szDetails, "%s %s %s %s %s", 32, true,
								bReverb		? "Rev" : "",
								bDistFilter	? "Dis" : "",
								bUseDoppler	? "Dop" : "",
								bUseOcc		? "Occ" : "",
								bUseDSP		? "DSP" : "" );

			Asura_Text_Output::Print ( ( xActualOrigin.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE + 5.0f ) / ASURA_TEXT_OUTPUT_FONT_WIDTH, ( xVolumeDest.y + 3.0f ) / ASURA_TEXT_OUTPUT_FONT_HEIGHT, Asura_OverlayBrightness | uMIXER_CHANNEL_DIALOG_DETAILS_TEXT_COLOUR, ASURA_TEXT_FORMAT_SMALLFONT, szDetails );
		}
	}

	// In/Out nodes
	Asura_Vector_2 xMyInputNodePos	= GetInputNodePos();
	Asura_Vector_2 xMyOutputNodePos = GetOutputNodePos();
	
	xMyInputNodePos.x *= ASURA_TEXT_OUTPUT_FONT_WIDTH;
	xMyInputNodePos.y *= ASURA_TEXT_OUTPUT_FONT_HEIGHT;
	xMyOutputNodePos.x *= ASURA_TEXT_OUTPUT_FONT_WIDTH;
	xMyOutputNodePos.y *= ASURA_TEXT_OUTPUT_FONT_HEIGHT;

	// Input node
	Asura_Render::Overlay.Colour			= ( m_bHighlightInNode ? uMIXER_CHANNEL_DIALOG_NODE_SEL_COLOUR : uMIXER_CHANNEL_DIALOG_NODE_COLOUR ) | Asura_OverlayBrightness;
	Asura_Render::Overlay.TextureIndex		= ASURA_TEXTURE_NONE;
	Asura_Render::Overlay.TranslucencyMode	= ASURA_TRANSLUCENCY_NORMAL;
	Asura_Render::Overlay.SetRectXY ( xMyInputNodePos.x - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyInputNodePos.y - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, fMIXER_CHANNEL_DIALOG_NODE_SIZE, fMIXER_CHANNEL_DIALOG_NODE_SIZE );
	Asura_Render::Overlay.Render();
	Asura_Render::FlushDisplay();

	Asura_Render::Render2DLine( xMyInputNodePos.x - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyInputNodePos.y - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyInputNodePos.x - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyInputNodePos.y + fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, uMIXER_CHANNEL_DIALOG_BORDER_COLOUR | Asura_OverlayBrightness );	// Left
	Asura_Render::Render2DLine( xMyInputNodePos.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyInputNodePos.y - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyInputNodePos.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyInputNodePos.y + fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, uMIXER_CHANNEL_DIALOG_BORDER_COLOUR | Asura_OverlayBrightness );	// Right
	Asura_Render::Render2DLine( xMyInputNodePos.x - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyInputNodePos.y - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyInputNodePos.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyInputNodePos.y - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, uMIXER_CHANNEL_DIALOG_BORDER_COLOUR | Asura_OverlayBrightness );	// Top
	Asura_Render::Render2DLine( xMyInputNodePos.x - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyInputNodePos.y + fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyInputNodePos.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyInputNodePos.y + fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, uMIXER_CHANNEL_DIALOG_BORDER_COLOUR | Asura_OverlayBrightness );	// Bottom

	// Render the connection to the next mixer
	if( !IsMaster() )
	{
		if( m_bSelectingOutput )
		{
			float fCursorX, fCursorY;
			Asura_Cursor::GetScreenCoords(fCursorX, fCursorY);

			fCursorX *= ASURA_TEXT_OUTPUT_FONT_WIDTH;
			fCursorY *= ASURA_TEXT_OUTPUT_FONT_HEIGHT;

			Asura_Render::Render2DLine( xMyOutputNodePos.x, xMyOutputNodePos.y, fCursorX, fCursorY, uMIXER_CHANNEL_DIALOG_CONNECTOR_COLOUR | Asura_OverlayBrightness );
		}
		else
		{
			Asura_Hash_ID xOutputMixerHash = ( IsSelected() ? s_pxSelectedChannelData->m_xOutputChannelHash : Asura_GetHashID( pxChannel->GetOutputMixerName() ) );

			Tools_MixerChannel_Dialog* pxOutput = FindMixerDialog( xOutputMixerHash );
			if( pxOutput )
			{
				Asura_Vector_2 xTheirInputNodePos	= pxOutput->GetInputNodePos();

				xTheirInputNodePos.x *= ASURA_TEXT_OUTPUT_FONT_WIDTH;
				xTheirInputNodePos.y *= ASURA_TEXT_OUTPUT_FONT_HEIGHT;

				Asura_Render::Render2DLine( xMyOutputNodePos.x, xMyOutputNodePos.y, xTheirInputNodePos.x, xTheirInputNodePos.y, uMIXER_CHANNEL_DIALOG_CONNECTOR_COLOUR | Asura_OverlayBrightness );
			}
		}

		Asura_Render::FlushDisplay();

		// Output node
		Asura_Render::Overlay.Colour			= ( m_bHighlightOutNode ? uMIXER_CHANNEL_DIALOG_NODE_SEL_COLOUR : uMIXER_CHANNEL_DIALOG_NODE_COLOUR ) | Asura_OverlayBrightness;
		Asura_Render::Overlay.TextureIndex		= ASURA_TEXTURE_NONE;
		Asura_Render::Overlay.TranslucencyMode	= ASURA_TRANSLUCENCY_NORMAL;
		Asura_Render::Overlay.SetRectXY ( xMyOutputNodePos.x - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyOutputNodePos.y - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, fMIXER_CHANNEL_DIALOG_NODE_SIZE, fMIXER_CHANNEL_DIALOG_NODE_SIZE );
		Asura_Render::Overlay.Render();
		Asura_Render::FlushDisplay();

		Asura_Render::Render2DLine( xMyOutputNodePos.x - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyOutputNodePos.y - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyOutputNodePos.x - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyOutputNodePos.y + fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, uMIXER_CHANNEL_DIALOG_BORDER_COLOUR | Asura_OverlayBrightness );	// Left
		Asura_Render::Render2DLine( xMyOutputNodePos.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyOutputNodePos.y - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyOutputNodePos.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyOutputNodePos.y + fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, uMIXER_CHANNEL_DIALOG_BORDER_COLOUR | Asura_OverlayBrightness );	// Right
		Asura_Render::Render2DLine( xMyOutputNodePos.x - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyOutputNodePos.y - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyOutputNodePos.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyOutputNodePos.y - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, uMIXER_CHANNEL_DIALOG_BORDER_COLOUR | Asura_OverlayBrightness );	// Top
		Asura_Render::Render2DLine( xMyOutputNodePos.x - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyOutputNodePos.y + fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyOutputNodePos.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, xMyOutputNodePos.y + fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f, uMIXER_CHANNEL_DIALOG_BORDER_COLOUR | Asura_OverlayBrightness );	// Bottom
	}
}


void Tools_MixerChannel_Dialog::Update ( const Asura_Vector_2& xOrigin )
{
	Asura_Dialog::Update( xOrigin );

	if( !s_bDialogsAcceptMouseInput )
	{
		return;
	}

	m_xOrigin = xOrigin;

	float fCursorX, fCursorY;
	Asura_Cursor::GetScreenCoords(fCursorX, fCursorY);

	fCursorX *= ASURA_TEXT_OUTPUT_FONT_WIDTH;
	fCursorY *= ASURA_TEXT_OUTPUT_FONT_HEIGHT;

	Asura_Vector_2 xActualOrigin = xOrigin + m_xOffset; 

	xActualOrigin.x *= ASURA_TEXT_OUTPUT_FONT_WIDTH;
	xActualOrigin.y *= ASURA_TEXT_OUTPUT_FONT_HEIGHT;

	m_bHighlightName	= false;
	m_bHighlightBody	= false;
	m_bHighlightInNode	= false;
	m_bHighlightOutNode = false;

	if( s_bAnyChannelBeingDragged && !m_bDragging )
	{
		// Some other channel is being dragged, we are done
		return;
	}

	if( s_bAnyChannelSelectingOutput && !m_bSelectingOutput )
	{
		// Some other channel is selecting their output see if the mouse is over our input node

		Asura_Vector_2 xInputNodePos = GetInputNodePos();

		xInputNodePos.x *= ASURA_TEXT_OUTPUT_FONT_WIDTH;
		xInputNodePos.y *= ASURA_TEXT_OUTPUT_FONT_HEIGHT;

		if	(	( fCursorX > xInputNodePos.x  - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.75f )
			&&	( fCursorX < xInputNodePos.x  + fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.75f )
			&&	( fCursorY > xInputNodePos.y  - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.75f )
			&&	( fCursorY < xInputNodePos.y  + fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.75f ) )
		{
			// The cursor is over my input node
			m_bHighlightInNode = true;
			s_xInputNodeSelected = GetChannelHash();
		}
		else if( s_xInputNodeSelected == GetChannelHash() )
		{
			s_xInputNodeSelected = ASURA_HASH_ID_UNSET;
		}

		return;
	}

	// Deal with highlighting, selecting, starting to drag, starting to pick output
	if( !m_bDragging && !m_bSelectingOutput )
	{
		Asura_Vector_2 xOutputNodePos = GetOutputNodePos();

		xOutputNodePos.x *= ASURA_TEXT_OUTPUT_FONT_WIDTH;
		xOutputNodePos.y *= ASURA_TEXT_OUTPUT_FONT_HEIGHT;

		if  (	( fCursorX > xActualOrigin.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE )
			&&	( fCursorX < xActualOrigin.x + fMIXER_CHANNEL_DIALOG_WIDTH - fMIXER_CHANNEL_DIALOG_NODE_SIZE )
			&&	( fCursorY > xActualOrigin.y )
			&&	( fCursorY < xActualOrigin.y + fMIXER_CHANNEL_DIALOG_NAMETAG_HEIGHT ) )
		{
			m_bHighlightName = true;
		}
		else if (	( fCursorX > xActualOrigin.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE )
				&&	( fCursorX < xActualOrigin.x + fMIXER_CHANNEL_DIALOG_WIDTH - fMIXER_CHANNEL_DIALOG_NODE_SIZE )
				&&	( fCursorY > xActualOrigin.y )
				&&	( fCursorY < xActualOrigin.y + fMIXER_CHANNEL_DIALOG_HEIGHT ) 
				&&	( !IsMaster() ) )
		{
			m_bHighlightBody = true;
		}
		else if (	( fCursorX > xOutputNodePos.x - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f )
				&&	( fCursorX < xOutputNodePos.x + fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f )
				&&	( fCursorY > xOutputNodePos.y - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f )
				&&	( fCursorY < xOutputNodePos.y + fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f ) 
				&&	IsSelected() )
		{
			m_bHighlightOutNode = true;
		}

		if( !IsSelected() && m_bHighlightBody && Asura_UserInput::DebouncedKeyPressed( ASURA_KEYID_MOUSEBUTTON1 ) )
		{
			// See if we can be selected
			if( s_pfnRequestSelection && s_pfnRequestSelection( GetChannelHash() ) )
			{
				// Copy this mixer's data into the selected mixer memory (this implicitly 'selects' this dialog)
				if( s_pxSelectedChannelData )
				{
					s_pxSelectedChannelData->CopyFromMixerChannel( GetChannelHash() );
				}
				return;
			}
		}

		if( m_bHighlightName && Asura_UserInput::KeyPressed( ASURA_KEYID_MOUSEBUTTON1 ) )
		{
			m_bDragging = true;
			s_bAnyChannelBeingDragged = true;
			m_xDragOrigin = Asura_Vector_2( fCursorX - xActualOrigin.x, fCursorY - xActualOrigin.y );
			
			return;
		}
		
		if( IsSelected() && m_bHighlightOutNode && Asura_UserInput::KeyPressed( ASURA_KEYID_MOUSEBUTTON1 ) )
		{
			m_bSelectingOutput = true;
			s_bAnyChannelSelectingOutput = true;
			s_xInputNodeSelected = ASURA_HASH_ID_UNSET;
		}
	}

	// Deal with dragging the dialog
	if( m_bDragging )
	{
		if( !Asura_UserInput::KeyPressed( ASURA_KEYID_MOUSEBUTTON1 ) )
		{
			m_bDragging = false;
			s_bAnyChannelBeingDragged = false;
			
			if( s_pfnPositionChanged )
			{
				s_pfnPositionChanged( GetChannelHash(), m_xOffset );
			}

			return;
		}

		m_xOffset = Asura_Vector_2( fCursorX / ASURA_TEXT_OUTPUT_FONT_WIDTH - ( xOrigin.x + m_xDragOrigin.x / ASURA_TEXT_OUTPUT_FONT_WIDTH ), fCursorY / ASURA_TEXT_OUTPUT_FONT_HEIGHT - ( xOrigin.y + m_xDragOrigin.y / ASURA_TEXT_OUTPUT_FONT_HEIGHT ) );
		
		m_xOffset.x = Asura_Maths::Max( 0.0f, m_xOffset.x );
		m_xOffset.y = Asura_Maths::Max( 0.0f, m_xOffset.y );

		m_xMaxCoord.x = m_xOffset.x + fMIXER_CHANNEL_DIALOG_WIDTH / ASURA_TEXT_OUTPUT_FONT_WIDTH;
		m_xMaxCoord.y = m_xOffset.y + fMIXER_CHANNEL_DIALOG_HEIGHT / ASURA_TEXT_OUTPUT_FONT_WIDTH;

		m_bHighlightName = true;
	}

	// Deal with picking an output channel
	if( m_bSelectingOutput )
	{
		if( !Asura_UserInput::KeyPressed( ASURA_KEYID_MOUSEBUTTON1 ) )
		{
			if( s_xInputNodeSelected != ASURA_HASH_ID_UNSET )
			{
				if( s_pxSelectedChannelData )
				{
					s_pxSelectedChannelData->m_xOutputChannelHash = s_xInputNodeSelected;
					s_pxSelectedChannelData->m_bModified = true;
					s_xInputNodeSelected = ASURA_HASH_ID_UNSET;
				}
			}
			m_bSelectingOutput = false;
			s_bAnyChannelSelectingOutput = false;
		}
	}
}


void Tools_MixerChannel_Dialog::MoveTo	( const Asura_Vector_2 xPosition )
{
	m_xOffset = xPosition;

	m_xMaxCoord.x = m_xOffset.x + fMIXER_CHANNEL_DIALOG_WIDTH / ASURA_TEXT_OUTPUT_FONT_WIDTH;
	m_xMaxCoord.y = m_xOffset.y + fMIXER_CHANNEL_DIALOG_HEIGHT / ASURA_TEXT_OUTPUT_FONT_WIDTH;
};

Asura_Vector_2 Tools_MixerChannel_Dialog::GetInputNodePos () const
{
	Asura_Vector_2 xInputNodePos = m_xOrigin + m_xOffset;

	xInputNodePos.x += ( fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f ) / ASURA_TEXT_OUTPUT_FONT_WIDTH;
	xInputNodePos.y += ( fMIXER_CHANNEL_DIALOG_HEIGHT / ASURA_TEXT_OUTPUT_FONT_HEIGHT * 0.75f );
	
	return xInputNodePos;
}


Asura_Vector_2 Tools_MixerChannel_Dialog::GetOutputNodePos () const
{
	Asura_Vector_2 xOutputNodePos = m_xOrigin + m_xOffset;

	xOutputNodePos.x += ( fMIXER_CHANNEL_DIALOG_WIDTH - fMIXER_CHANNEL_DIALOG_NODE_SIZE * 0.5f ) / ASURA_TEXT_OUTPUT_FONT_WIDTH;
	xOutputNodePos.y += ( fMIXER_CHANNEL_DIALOG_HEIGHT / ASURA_TEXT_OUTPUT_FONT_HEIGHT * 0.75f );

	return xOutputNodePos;
}


Tools_MixerChannel_Dialog* Tools_MixerChannel_Dialog::FindMixerDialog ( const Asura_Hash_ID xMixerHash )
{
	Asura_List_It<Tools_MixerChannel_Dialog> xIter( s_xMixerDialogList );

	for( 0; !xIter.Done(); xIter.Next() )
	{
		Tools_MixerChannel_Dialog* pxDialog = xIter.GetCurrent();
		if( pxDialog && ( pxDialog->GetChannelHash() == xMixerHash ) )
		{
			return pxDialog;
		}
	}

	return 0;
}

// _______________________________ Tools_MixerList_Dialog __________________________________

Tools_SoundMixerPresetList_Dialog::Tools_SoundMixerPresetList_Dialog ( const Asura_Vector_2& xOffset ) : Asura_List_Dialog ( xOffset, 0, &m_iSelectedIndex, 10, 0, 0, &m_bSelectionChanged, false )
{
	SetExpandWidthToFillOverlay(false);

	m_uMixerPresetListSize		= 0;
	m_pszMixerPresetList		= 0;
	m_pxMixerPresetNameTree		= 0;

	m_iSelectedIndex			= 0;
	m_bSelectionChanged			= false;

	m_xMaxCoord.x = xOffset.x + m_fTextWidth;
}

Tools_SoundMixerPresetList_Dialog::~Tools_SoundMixerPresetList_Dialog ()
{
	DeleteMixerPresetList();
}


void Tools_SoundMixerPresetList_Dialog::UpdateMixerPresetList( const Asura_Hash_ID xToSelect /* = ASURA_HASH_ID_UNSET */ )
{
	DeleteMixerPresetList();

	int iJunkIndex;

	m_pxMixerPresetNameTree = new Asura_NameTree;

	//add all the names of the existing mixer channels
	for(u_int uPreset = 0; uPreset < Asura_Sound_Mixer_System::GetNumberOfPresets(); ++uPreset )
	{
		const Asura_Sound_Mixer_Preset* pxPreset = Asura_Sound_Mixer_System::GetPresetByIndex( uPreset );
		m_pxMixerPresetNameTree->FindNodeIndexByName( pxPreset->GetName(), true, iJunkIndex );
	}

	//Fill array with these strings
	m_uMixerPresetListSize	= m_pxMixerPresetNameTree->GetNumberOfNodes();
	m_pszMixerPresetList	= new const char*[ m_uMixerPresetListSize ];
	m_pxMixerPresetNameTree->MakeSortedNameList( m_pszMixerPresetList );

	ReplaceStrings( m_uMixerPresetListSize, m_pszMixerPresetList );

	//find the index of the current string in the sorted list
	m_iSelectedIndex = 0;
	for( u_int uEntry = 0; uEntry < m_uMixerPresetListSize; ++ uEntry )
	{
		if( xToSelect == Asura_GetHashID( m_pszMixerPresetList[ uEntry ] ) )
		{
			m_iSelectedIndex = static_cast<int>( uEntry );
			break;
		}
	}

	m_bSelectionChanged = false;
}


void Tools_SoundMixerPresetList_Dialog::DeleteMixerPresetList()
{
	delete [] m_pszMixerPresetList;
	m_pszMixerPresetList = 0;
	m_uMixerPresetListSize = 0;
	delete m_pxMixerPresetNameTree;
	m_pxMixerPresetNameTree = 0;
}

// _______________________________ Tools_TempMixerChannel __________________________________

void Tools_TempMixerChannel::CopyFromMixerChannel ( const Asura_Hash_ID xChannelHash )
{
	m_bModified = false;

	m_xChannelHash = xChannelHash;

	const Asura_Sound_Mixer_Channel* pxChannel = Asura_Sound_Mixer_System::GetChannelByHash( m_xChannelHash );

	// Is this mixer included in the preset?

	m_bIncludeInPreset = false;

	const Asura_Sound_Mixer_Preset* pxPreset = Asura_Sound_Mixer_System::GetPresetByHash( m_xChannelPresetHash );
	Asura_Sound_Mixer_ChannelPreset* pxChannelPreset = 0;

	if( pxPreset )
	{
		pxChannelPreset = pxPreset->FindChannel( xChannelHash );

		if( pxChannelPreset )
		{
			m_bIncludeInPreset = true;
		}
	}
	
	m_xOutputChannelHash = Asura_GetHashID( pxChannel->GetOutputMixerName() );

	if( !m_bIncludeInPreset )
	{
		// If we are not included in the preset get our settings from the channel
		if( !pxChannel )
		{
			SetDefaults();
			return;
		}
		else
		{
			m_xSettings = pxChannel->GetSettings();
		}
	}
	else
	{
		m_xSettings = pxChannelPreset->GetSettings();
	}
}


void Tools_TempMixerChannel::CopyToMixerChannel ( const Asura_Hash_ID xChannelHash )
{
	// See if we need to add this channel to the preset
	Asura_Sound_Mixer_Preset* pxPreset = Asura_Sound_Mixer_System::GetPresetByHash( m_xChannelPresetHash );

	if( !pxPreset )
	{
		return;
	}

	Asura_Sound_Mixer_ChannelPreset* pxChannelPreset = pxPreset->FindChannel( xChannelHash );
	Asura_Sound_Mixer_Channel* pxChannel = Asura_Sound_Mixer_System::GetChannelByHash( xChannelHash );

	if( !pxChannel )
	{
		return;
	}

	// Set the output channel in case it was changed
	const Asura_Sound_Mixer_Channel* pxOutputChannel = Asura_Sound_Mixer_System::GetChannelByHash( m_xOutputChannelHash );
	if( pxOutputChannel )
	{
		pxChannel->SetOutputMixerName( pxOutputChannel->GetName() );
	}
	else
	{
		pxChannel->SetOutputMixerName( "Master" );
	}

	if( m_bIncludeInPreset && !pxChannelPreset )
	{
		// Add this channel to the preset
		pxPreset->AddChannel( m_xSettings, pxChannel->GetName() );
		pxChannelPreset = pxPreset->FindChannel( xChannelHash );

		Asura_Assert( pxChannelPreset, "Failed to find mixer channel I just added to preset" );
		if( !pxChannelPreset )
		{
			return;
		}
	}
	else if( !m_bIncludeInPreset && pxChannelPreset )
	{
		// Remove this channel from the preset
		pxPreset->RemoveChannel( xChannelHash );
		return;
	}
	
	if( pxChannelPreset )
	{
		pxChannelPreset->SetSettings( m_xSettings );
	}
}


void Tools_TempMixerChannel::SetDefaults ()
{
	m_xSettings.m_fVolume					= 1.0f;
	m_xSettings.m_xSurroundPan.m_fPan		= 0.5f;
	m_xSettings.m_xSurroundPan.m_fFade		= 1.0f;
	m_xSettings.m_bPanOverride				= false;
	m_xSettings.m_bMute						= false;
	m_xSettings.m_bSolo						= false;
	m_xSettings.m_bIgnoreGameSpeedPitch		= false;
	m_xSettings.m_bUseReverb				= true;
	m_xSettings.m_bUseDistanceFilter		= true;
	m_xSettings.m_bUseDoppler				= true;
	m_xSettings.m_bUseOcclusion				= true;

	m_bIncludeInPreset		= true;
	m_bModified				= false;
	m_xChannelHash			= ASURA_HASH_ID_UNSET;
	m_xChannelPresetHash	= ASURA_HASH_ID_UNSET;
	m_xOutputChannelHash	= ASURA_HASH_ID_UNSET;
}

// _______________________________ Tools_SoundMixer_Dialog __________________________________


Tools_SoundMixer_Dialog::Tools_SoundMixer_Dialog(const Asura_Vector_2& xOffset,Asura_String* pxMixer,bool* pbModified,const char* szLabel)
:Asura_DropDown_List_Dialog(xOffset)
{
	m_bInitialised = false;
	m_pszMixerList = 0;
	m_uMixerListSize = 0;
	m_pxMixerNameTree = 0;

	m_pbModified = pbModified;
	m_pxMixer = pxMixer;
	Asura_Assert(m_pxMixer,"");

	m_bSelectionChanged = false;
	m_xMixerBackup = *m_pxMixer;


	UpdateMixerList();
	Initialise(xOffset,m_pszMixerList,&m_iSelectedIndex,10,m_uMixerListSize,NULL,NULL,szLabel);
	m_bInitialised = true;

	SetOnSelectCallback(OnSelectionChanged,(int) this);
}

Tools_SoundMixer_Dialog::~Tools_SoundMixer_Dialog()
{
	DeleteMixerList();
}


void Tools_SoundMixer_Dialog::UpdateMixerList()
{
	DeleteMixerList();

	m_pxMixerNameTree = new Asura_NameTree;

	//make sure the mixer being edited isn't blank
	if(m_pxMixer->IsBlank())
	{
		*m_pxMixer = "Master";
		m_xMixerBackup = *m_pxMixer;
	}
	//and add it to the tree
	int iJunkIndex;
	m_pxMixerNameTree->FindNodeIndexByName(m_pxMixer->GetString(),true,iJunkIndex);

	//add all the names of the existing mixer channels
	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_pxMixerNameTree->FindNodeIndexByName(szName,true,iJunkIndex);
		}
	}
	//add the master mixer
	m_pxMixerNameTree->FindNodeIndexByName("Master",true,iJunkIndex);


	//Fill array with these strings
	m_uMixerListSize = m_pxMixerNameTree->GetNumberOfNodes();
	m_pszMixerList = new const char*[m_uMixerListSize];
	m_pxMixerNameTree->MakeSortedNameList(m_pszMixerList);

	//find the index of the current string in the sorted list
	m_iSelectedIndex = 0;
	for(u_int i=0;i<m_uMixerListSize;i++)
	{
		if(!m_pxMixer->Compare(m_pszMixerList[i]))
		{
			m_iSelectedIndex = (int) i;
			break;
		}
	}

	if(m_bInitialised)
	{
		ReplaceStrings(m_uMixerListSize,m_pszMixerList);
	}

	m_iListValidID = s_iListValidID;
}

void Tools_SoundMixer_Dialog::DeleteMixerList()
{
	delete [] m_pszMixerList;
	m_pszMixerList = 0;
	m_uMixerListSize = 0;
	delete m_pxMixerNameTree;
	m_pxMixerNameTree = 0;
}

void Tools_SoundMixer_Dialog::OnSelectionChanged()
{
	*m_pxMixer = m_pszMixerList[m_iSelectedIndex];
	if(m_pbModified) *m_pbModified = true;

}

void Tools_SoundMixer_Dialog::PerFrameUpdate()
{
	//check for external changes to the mixer
	if(!m_bSelectionChanged && m_xMixerBackup.Compare(*m_pxMixer))
	{
		m_xMixerBackup = *m_pxMixer;
		UpdateMixerList();
	}

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

	Asura_DropDown_List_Dialog::PerFrameUpdate();
}

// _______________________________ Tools_SoundMixer_PresetMessage __________________________________

void Tools_SoundMixer_PresetMessage::AddOverlayControls(struct Asura_StaticMessage* pxMessage,class Asura_Overlay* pxOverlay,class Asura_Vector_2& xOffset)
{
	Asura_Message_SoundMixerPreset* pxMessageData = reinterpret_cast< Asura_Message_SoundMixerPreset* >( pxMessage->m_pData );
	int iMaxNumberOfLines = 10;
	Asura_Dialog* pxNewDialogue = new Tools_SoundMixerPreset_DropDownList_Dialog( "Choose preset", xOffset, iMaxNumberOfLines, &pxMessageData->m_uHashID );
	pxNewDialogue->SetHelpText( "The mixer settings that will be interpolated towards." );
	pxOverlay->AddDialog( pxNewDialogue );
	xOffset.y += Asura_Dialog::DefaultSpacing();

	switch (pxMessage->m_usMsgID)
	{
	case ASURA_MSG_SOUND_MIXER_PRESET_QUEUE:
		pxNewDialogue = new Asura_Numerical_Dialog( "Fade-in Time Override", xOffset, &pxMessageData->m_fFadeTime, -1.0f, 60.0f, 0.1f, "%.2f" );
		pxNewDialogue->SetHelpText("If >= zero , this will override the fade time defined by the preset.");
		break;
	case ASURA_MSG_SOUND_MIXER_PRESET_UNQUEUE:
		pxNewDialogue = new Asura_Numerical_Dialog( "Fade-out Time Override", xOffset, &pxMessageData->m_fFadeTime, -1.0f, 60.0f, 0.1f, "%.2f" );
		pxNewDialogue->SetHelpText("If >= zero , this will override the fade time defined by the preset.");
		break;
	default:
		Asura_Assert(false,"Undefined message type");
	case ASURA_MSG_SOUND_MIXER_PRESET:
		pxNewDialogue = new Asura_Numerical_Dialog( "Fade Time", xOffset, &pxMessageData->m_fFadeTime, 0.0f, 60.0f, 0.1f, "%.2f" );
		break;
	}

	pxOverlay->AddDialog( pxNewDialogue );
	xOffset.y += Asura_Dialog::DefaultSpacing();
}

//eof
