//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================

#include "dme_controls/dmepresetgroupeditorpanel.h"
#include "dme_controls/dmecontrols_utils.h"
#include "movieobjects/dmeanimationset.h"
#include "vgui_controls/ListPanel.h"
#include "vgui_controls/PropertySheet.h"
#include "vgui_controls/PropertyPage.h"
#include "vgui_controls/Button.h"
#include "vgui_controls/Menu.h"
#include "vgui_controls/Splitter.h"
#include "vgui_controls/MessageBox.h"
#include "vgui_controls/ComboBox.h"
#include "vgui_controls/InputDialog.h"
#include "vgui_controls/TextEntry.h"
#include "vgui/MouseCode.h"
#include "vgui/IInput.h"
#include "vgui/ISurface.h"
#include "tier1/KeyValues.h"
#include "tier1/utldict.h"
#include "dme_controls/presetpicker.h"
#include "vgui_controls/FileOpenDialog.h"
#include "tier2/fileutils.h"
#include "tier1/utlbuffer.h"
#include "dme_controls/inotifyui.h"
#include "../game/shared/iscenetokenprocessor.h"
#include "movieobjects/dmx_to_vcd.h"
#include "studio.h"
#include "phonemeconverter.h"

// Forward declaration
class CDmePresetGroupEditorPanel;


//-----------------------------------------------------------------------------
// Utility scope guards
//-----------------------------------------------------------------------------
DEFINE_SOURCE_UNDO_SCOPE_GUARD( PresetGroup, NOTIFY_SOURCE_PRESET_GROUP_EDITOR );
DEFINE_SOURCE_NOTIFY_SCOPE_GUARD( PresetGroup, NOTIFY_SOURCE_PRESET_GROUP_EDITOR );

#define PRESET_FILE_FORMAT "preset"

//-----------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//
// CDmePresetRemapPanel
//
// Implementation below because of scoping issues
//
//-----------------------------------------------------------------------------
class CDmePresetRemapPanel : public vgui::Frame
{
	DECLARE_CLASS_SIMPLE( CDmePresetRemapPanel, vgui::Frame );

public:
	CDmePresetRemapPanel( vgui::Panel *pParent, const char *pTitle );
	~CDmePresetRemapPanel();

	// Shows the modal dialog
	void DoModal( CDmeAnimationSet *pAnimationSet, CDmePresetGroup *pDestGroup );

	// Inherited from Frame
	virtual void OnCommand( const char *pCommand );

	virtual void OnKeyCodeTyped( KeyCode code );

private:
	MESSAGE_FUNC( OnTextChanged, "TextChanged" );
	MESSAGE_FUNC( OnSelectPreset, "SelectPreset" );
	MESSAGE_FUNC( OnRemovePreset, "RemovePreset" );
	MESSAGE_FUNC_PARAMS( OnPresetPicked, "PresetPicked", params );
	MESSAGE_FUNC_PARAMS( OnOpenContextMenu, "OpenContextMenu", kv );

	// Refreshes the list of presets
	void RefreshPresetList( );

	// Applies changes to the preset remap
	void ApplyChangesToPresetRemap();

	// Cleans up the context menu
	void CleanupContextMenu();

	vgui::ListPanel *m_pPresetRemapList;
	vgui::ComboBox	*m_pSourcePresetGroup;
	CDmeHandle< CDmePresetGroup > m_hSourceGroup;
	CDmeHandle< CDmePresetGroup > m_hDestGroup;
	vgui::DHANDLE< vgui::Menu > m_hContextMenu;
};


//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
static int __cdecl DestPresetNameSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 )
{
	const char *string1 = item1.kv->GetString( "dest" );
	const char *string2 = item2.kv->GetString( "dest" );
	return Q_stricmp( string1, string2 );
}

static int __cdecl SrcPresetNameSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 )
{
	const char *string1 = item1.kv->GetString( "src" );
	const char *string2 = item2.kv->GetString( "src" );
	return Q_stricmp( string1, string2 );
}

CDmePresetRemapPanel::CDmePresetRemapPanel( vgui::Panel *pParent, const char *pTitle ) : 
	BaseClass( pParent, "DmePresetRemapPanel" )
{
	m_pSourcePresetGroup = new vgui::ComboBox( this, "SourcePresetGroup", 8, true );
	SetDeleteSelfOnClose( true );

	m_pPresetRemapList = new vgui::ListPanel( this, "PresetRemapList" );
	m_pPresetRemapList->AddColumnHeader( 0, "dest", "Dest Preset", 100, 0 );
	m_pPresetRemapList->AddColumnHeader( 1, "src", "Source Preset", 100, 0 );
	m_pPresetRemapList->SetSelectIndividualCells( false );
	m_pPresetRemapList->SetMultiselectEnabled( true );
	m_pPresetRemapList->SetEmptyListText( "No presets" );
	m_pPresetRemapList->AddActionSignalTarget( this );
	m_pPresetRemapList->SetSortFunc( 0, DestPresetNameSortFunc );
	m_pPresetRemapList->SetSortFunc( 1, SrcPresetNameSortFunc );
	m_pPresetRemapList->SetSortColumn( 0 );

	SetBlockDragChaining( true );

	LoadControlSettingsAndUserConfig( "resource/presetremappanel.res" );

	SetTitle( pTitle, false );
}

CDmePresetRemapPanel::~CDmePresetRemapPanel()
{
	CleanupContextMenu();
}


//-----------------------------------------------------------------------------
// Cleans up the context menu
//-----------------------------------------------------------------------------
void CDmePresetRemapPanel::CleanupContextMenu()
{
	if ( m_hContextMenu.Get() )
	{
		m_hContextMenu->MarkForDeletion();
		m_hContextMenu = NULL;
	}
}


//-----------------------------------------------------------------------------
// Refreshes the list of presets
//-----------------------------------------------------------------------------
void CDmePresetRemapPanel::RefreshPresetList( )
{
	m_pPresetRemapList->RemoveAll();
							   
	CDmaElementArray< CDmePreset > *pPresetList = m_hDestGroup.Get() ? &m_hDestGroup->GetPresets() : NULL;
	if ( !pPresetList )
		return;

	int nCount = pPresetList->Count();
	if ( nCount == 0 )
		return;

	CDmePresetRemap *pRemap = m_hDestGroup->GetPresetRemap();
	bool bUseRemap = ( pRemap && m_hSourceGroup.Get() && !Q_stricmp( pRemap->m_SourcePresetGroup, m_hSourceGroup->GetName() ) );

	for ( int i = 0; i < nCount; ++i )
	{
		CDmePreset *pPreset = pPresetList->Get(i);

		const char *pName = pPreset->GetName();
		if ( !pName || !pName[0] )
		{
			pName = "<no name>";
		}

		KeyValues *kv = new KeyValues( "node" );
		kv->SetString( "dest", pName );
		SetElementKeyValue( kv, "destPreset", pPreset );
		if ( bUseRemap )
		{
			const char *pSource = pRemap->FindSourcePreset( pName );
			CDmePreset *pSrcPreset = pSource ? m_hSourceGroup->FindPreset( pSource ) : NULL;
			kv->SetString( "src", pSrcPreset ? pSrcPreset->GetName() : "" ); 
			SetElementKeyValue( kv, "srcPreset", pSrcPreset );
		}
		else
		{
			kv->SetString( "src", "" ); 
			SetElementKeyValue( kv, "srcPreset", NULL );
		}

		m_pPresetRemapList->AddItem( kv, 0, false, false );
	}
	m_pPresetRemapList->SortList();
}


//-----------------------------------------------------------------------------
// Called by the preset picker when a preset is picked
//-----------------------------------------------------------------------------
void CDmePresetRemapPanel::OnPresetPicked( KeyValues *pParams )
{
	int nSelectedItemCount = m_pPresetRemapList->GetSelectedItemsCount();
	if ( nSelectedItemCount != 1 )
		return;

	CDmePreset *pPreset = GetElementKeyValue< CDmePreset >( pParams, "preset" );
	int nItemID = m_pPresetRemapList->GetSelectedItem( 0 );
	KeyValues *kv = m_pPresetRemapList->GetItem( nItemID );
	kv->SetString( "src", pPreset ? pPreset->GetName() : "" ); 
	SetElementKeyValue( kv, "srcPreset", pPreset );
	m_pPresetRemapList->ApplyItemChanges( nItemID );
}


//-----------------------------------------------------------------------------
// Called when double-clicking on a list entry
//-----------------------------------------------------------------------------
void CDmePresetRemapPanel::OnKeyCodeTyped( KeyCode code )
{
	if ( code == KEY_ENTER )
	{
		OnSelectPreset();
		return;
	}

	if ( code == KEY_DELETE || code == KEY_BACKSPACE )
	{
		OnRemovePreset();
		return;
	}

	BaseClass::OnKeyCodeTyped( code );
}


//-----------------------------------------------------------------------------
// Called by the context menu
//-----------------------------------------------------------------------------
void CDmePresetRemapPanel::OnSelectPreset()
{
	int nSelectedItemCount = m_pPresetRemapList->GetSelectedItemsCount();
	if ( nSelectedItemCount != 1 )
		return;

	CPresetPickerFrame *pPresetPicker = new CPresetPickerFrame( this, "Select Source Preset", false );
	pPresetPicker->AddActionSignalTarget( this );
	pPresetPicker->DoModal( m_hSourceGroup, false, NULL );
}

void CDmePresetRemapPanel::OnRemovePreset()
{
	int nSelectedItemCount = m_pPresetRemapList->GetSelectedItemsCount();
	if ( nSelectedItemCount != 1 )
		return;

	int nItemID = m_pPresetRemapList->GetSelectedItem( 0 );
	KeyValues *kv = m_pPresetRemapList->GetItem( nItemID );
	kv->SetString( "src", "" ); 
	SetElementKeyValue( kv, "srcPreset", NULL );
	m_pPresetRemapList->ApplyItemChanges( nItemID );
}


//-----------------------------------------------------------------------------
// Called to open a context-sensitive menu for a particular menu item
//-----------------------------------------------------------------------------
void CDmePresetRemapPanel::OnOpenContextMenu( KeyValues *kv )
{
	CleanupContextMenu();

	int nSelectedItemCount = m_pPresetRemapList->GetSelectedItemsCount();
	if ( nSelectedItemCount != 1 )
		return;

	m_hContextMenu = new vgui::Menu( this, "ActionMenu" );
	m_hContextMenu->AddMenuItem( "#DmePresetRemapPanel_SelectPreset", new KeyValues( "SelectPreset" ), this );
	m_hContextMenu->AddMenuItem( "#DmePresetRemapPanel_RemovePreset", new KeyValues( "RemovePreset" ), this );

	vgui::Menu::PlaceContextMenu( this, m_hContextMenu.Get() );
}


//-----------------------------------------------------------------------------
// Called when the dest combo list changes
//-----------------------------------------------------------------------------
void CDmePresetRemapPanel::OnTextChanged()
{
	KeyValues *pCurrentGroup = m_pSourcePresetGroup->GetActiveItemUserData();
	m_hSourceGroup = pCurrentGroup ? GetElementKeyValue<CDmePresetGroup>( pCurrentGroup, "presetGroup" ) : NULL;
	RefreshPresetList();
}


void CDmePresetRemapPanel::DoModal( CDmeAnimationSet *pAnimationSet, CDmePresetGroup *pDestGroup )
{
	m_hDestGroup = pDestGroup;

	m_pSourcePresetGroup->DeleteAllItems();

	bool bSelected = false;

	CDmePresetRemap* pRemap = m_hDestGroup->GetPresetRemap();

	// Populate the combo box with preset group names
	const CDmaElementArray< CDmePresetGroup > &presetGroupList = pAnimationSet->GetPresetGroups();
	int nCount = presetGroupList.Count();
	for ( int i = 0; i < nCount; ++i )
	{
		CDmePresetGroup *pPresetGroup = presetGroupList[i];
		if ( pPresetGroup == m_hDestGroup.Get() )
			continue;
		 
		KeyValues *kv = new KeyValues( "entry" );
		SetElementKeyValue( kv, "presetGroup", pPresetGroup );
		int nItemID = m_pSourcePresetGroup->AddItem( pPresetGroup->GetName(), kv );
		if ( !bSelected || ( pRemap && !Q_stricmp( pRemap->m_SourcePresetGroup, pPresetGroup->GetName() ) ) )
		{
			m_pSourcePresetGroup->ActivateItem( nItemID );
			bSelected = true;
		}
	}

	BaseClass::DoModal( );

	m_pSourcePresetGroup->RequestFocus();
}


//-----------------------------------------------------------------------------
// Applies changes to the preset remap
//-----------------------------------------------------------------------------
void CDmePresetRemapPanel::ApplyChangesToPresetRemap()
{
	int nTextLength = m_pSourcePresetGroup->GetTextLength() + 1;
	char* pSourceName = (char*)_alloca( nTextLength * sizeof(char) );
	m_pSourcePresetGroup->GetText( pSourceName, nTextLength );

	CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Change Preset Remap" );
	CDmePresetRemap *pPresetRemap = m_hDestGroup->GetOrAddPresetRemap();
	pPresetRemap->m_SourcePresetGroup = pSourceName;
	pPresetRemap->RemoveAll();
	for ( int nItemID = m_pPresetRemapList->FirstItem(); 
		nItemID != m_pPresetRemapList->InvalidItemID(); 
		nItemID = m_pPresetRemapList->NextItem( nItemID ) )
	{
		KeyValues* pKeyValues = m_pPresetRemapList->GetItem( nItemID );
		CDmePreset *pSrcPreset = GetElementKeyValue< CDmePreset >( pKeyValues, "srcPreset" );
		CDmePreset *pDestPreset = GetElementKeyValue< CDmePreset >( pKeyValues, "destPreset" );
		if ( pSrcPreset && pDestPreset )
		{
			pPresetRemap->AddRemap( pSrcPreset->GetName(), pDestPreset->GetName() );
		}
	}
}


//-----------------------------------------------------------------------------
// command handler
//-----------------------------------------------------------------------------
void CDmePresetRemapPanel::OnCommand( const char *command )
{
	if ( !Q_stricmp( command, "Ok") )
	{
		ApplyChangesToPresetRemap();
		CloseModal();
		return;
	}

	if ( !Q_stricmp( command, "Cancel") )
	{
		CloseModal();
		return;
	}

	BaseClass::OnCommand( command );
}


//-----------------------------------------------------------------------------
//
// CDmePresetGroupListPanel
//
// Implementation below because of scoping issues
//
//-----------------------------------------------------------------------------
class CDmePresetGroupListPanel : public vgui::ListPanel
{
	DECLARE_CLASS_SIMPLE( CDmePresetGroupListPanel, vgui::ListPanel );

public:
	// constructor, destructor
	CDmePresetGroupListPanel( vgui::Panel *pParent, const char *pName, CDmePresetGroupEditorPanel *pComboPanel );

	virtual void OnCreateDragData( KeyValues *msg );
	virtual bool IsDroppable( CUtlVector< KeyValues * >& msgList );
	virtual void OnPanelDropped( CUtlVector< KeyValues * >& msgList );
	virtual void OnKeyCodeTyped( vgui::KeyCode code );
	virtual void OnMouseDoublePressed( vgui::MouseCode code );
	virtual void OnDroppablePanelPaint( CUtlVector< KeyValues * >& msglist, CUtlVector< Panel * >& dragPanels );

private:
	CDmePresetGroupEditorPanel *m_pPresetGroupPanel;
};


//-----------------------------------------------------------------------------
//
// CDmePresetListPanel
//
// Implementation below because of scoping issues
//
//-----------------------------------------------------------------------------
class CDmePresetListPanel : public vgui::ListPanel
{
	DECLARE_CLASS_SIMPLE( CDmePresetListPanel, vgui::ListPanel );

public:
	// constructor, destructor
	CDmePresetListPanel( vgui::Panel *pParent, const char *pName, CDmePresetGroupEditorPanel *pComboPanel );

	virtual void OnKeyCodeTyped( vgui::KeyCode code );
	virtual void OnCreateDragData( KeyValues *msg );
	virtual bool IsDroppable( CUtlVector< KeyValues * >& msgList );
	virtual void OnPanelDropped( CUtlVector< KeyValues * >& msgList );
	virtual void OnDroppablePanelPaint( CUtlVector< KeyValues * >& msglist, CUtlVector< Panel * >& dragPanels );

private:

	CDmePresetGroupEditorPanel *m_pPresetGroupPanel;
};


//-----------------------------------------------------------------------------
// Sort functions for list panel
//-----------------------------------------------------------------------------
static int __cdecl IndexSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 )
{
	int nIndex1 = item1.kv->GetInt("index");
	int nIndex2 = item2.kv->GetInt("index");
	return nIndex1 - nIndex2;
}


//-----------------------------------------------------------------------------
// constructor, destructor
//-----------------------------------------------------------------------------
CDmePresetGroupEditorPanel::CDmePresetGroupEditorPanel( vgui::Panel *pParent, const char *pName ) :
	BaseClass( pParent, pName )
{
	m_pSplitter = new vgui::Splitter( this, "PresetGroupSplitter", vgui::SPLITTER_MODE_VERTICAL, 1 );
	vgui::Panel *pSplitterLeftSide = m_pSplitter->GetChild( 0 );
	vgui::Panel *pSplitterRightSide = m_pSplitter->GetChild( 1 );

	m_pPresetGroupList = new CDmePresetGroupListPanel( pSplitterLeftSide, "PresetGroupList", this );
	m_pPresetGroupList->AddColumnHeader( 0, "name", "Preset Group Name", 150, 0 );
	m_pPresetGroupList->AddColumnHeader( 1, "visible", "Visible", 70, 0 );
	m_pPresetGroupList->AddColumnHeader( 2, "shared", "Shared", 52, 0 );
	m_pPresetGroupList->AddColumnHeader( 3, "readonly", "Read Only", 52, 0 );
	m_pPresetGroupList->SetSelectIndividualCells( false );
	m_pPresetGroupList->SetMultiselectEnabled( false );
	m_pPresetGroupList->SetEmptyListText( "No preset groups" );
	m_pPresetGroupList->AddActionSignalTarget( this );
	m_pPresetGroupList->SetSortFunc( 0, IndexSortFunc );
	m_pPresetGroupList->SetSortFunc( 1, NULL );
	m_pPresetGroupList->SetColumnSortable( 1, false );
	m_pPresetGroupList->SetSortFunc( 2, NULL );
	m_pPresetGroupList->SetColumnSortable( 2, false );
	m_pPresetGroupList->SetSortFunc( 3, NULL );
	m_pPresetGroupList->SetColumnSortable( 3, false );
	m_pPresetGroupList->SetDropEnabled( true );
	m_pPresetGroupList->SetSortColumn( 0 );
	m_pPresetGroupList->SetDragEnabled( true );
	m_pPresetGroupList->SetDropEnabled( true );
	m_pPresetGroupList->SetIgnoreDoubleClick( true );

	m_pPresetList = new CDmePresetListPanel( pSplitterRightSide, "PresetList", this );
	m_pPresetList->AddColumnHeader( 0, "name", "Preset Name", 150, 0 );
	m_pPresetList->SetSelectIndividualCells( false );
	m_pPresetList->SetEmptyListText( "No presets" );
	m_pPresetList->AddActionSignalTarget( this );
	m_pPresetList->SetSortFunc( 0, IndexSortFunc );
	m_pPresetList->SetSortColumn( 0 );
	m_pPresetList->SetDragEnabled( true );
	m_pPresetList->SetDropEnabled( true );
	m_pPresetList->SetIgnoreDoubleClick( true );

	LoadControlSettingsAndUserConfig( "resource/dmepresetgroupeditorpanel.res" );

	m_hFileOpenStateMachine = new vgui::FileOpenStateMachine( this, this );
	m_hFileOpenStateMachine->AddActionSignalTarget( this );
}


CDmePresetGroupEditorPanel::~CDmePresetGroupEditorPanel()
{
	CleanupContextMenu();
	SaveUserConfig();
}


//-----------------------------------------------------------------------------
// Cleans up the context menu
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::CleanupContextMenu()
{
	if ( m_hContextMenu.Get() )
	{
		m_hContextMenu->MarkForDeletion();
		m_hContextMenu = NULL;
	}
}


//-----------------------------------------------------------------------------
// Sets the combination operator
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::SetAnimationSet( CDmeAnimationSet *pAnimationSet )
{
	m_hAnimationSet = pAnimationSet;
	RefreshAnimationSet();
}

CDmeAnimationSet* CDmePresetGroupEditorPanel::GetAnimationSet()
{
	return m_hAnimationSet;
}


//-----------------------------------------------------------------------------
// Builds the preset group list for the animation set
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::RefreshAnimationSet()
{
	CDmePresetGroup *pSelectedPresetGroup = GetSelectedPresetGroup();

	m_pPresetGroupList->RemoveAll();	
	if ( !m_hAnimationSet.Get() )
		return;
	
	const CDmaElementArray< CDmePresetGroup > &presetGroupList = m_hAnimationSet->GetPresetGroups();
	int nCount = presetGroupList.Count();
	for ( int i = 0; i < nCount; ++i )
	{ 
		CDmePresetGroup *pPresetGroup = presetGroupList[i];
		Assert( pPresetGroup );
		if ( !pPresetGroup )
			continue;

		bool bIsVisible = pPresetGroup->m_bIsVisible;
		KeyValues *kv = new KeyValues( "node", "name", pPresetGroup->GetName() );
		kv->SetString( "visible", bIsVisible ? "Yes" : "No" ); 
		kv->SetString( "shared", pPresetGroup->IsShared() ? "Yes" : "No" ); 
		kv->SetString( "readonly", pPresetGroup->m_bIsReadOnly ? "Yes" : "No" ); 
		SetElementKeyValue( kv, "presetGroup", pPresetGroup );
		kv->SetColor( "cellcolor", pPresetGroup->m_bIsReadOnly ? Color( 255, 0, 0, 255 ) : Color( 255, 255, 255, 255 ) ); 
		kv->SetInt( "index", i );
		int nItemID = m_pPresetGroupList->AddItem( kv, 0, false, false );

		if ( pSelectedPresetGroup == pPresetGroup )
		{
			m_pPresetGroupList->AddSelectedItem( nItemID );
		}
	}

	m_pPresetGroupList->SortList();

	RefreshPresetNames();
}


//-----------------------------------------------------------------------------
// Tells any class that cares that the data in this thing has changed
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::NotifyDataChanged()
{
	PostActionSignal( new KeyValues( "PresetsChanged" ) );
}


//-----------------------------------------------------------------------------
// Refreshes the list of presets in the selected preset group
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::RefreshPresetNames()
{
	CDmePreset *pSelectedPreset = GetSelectedPreset();

	m_pPresetList->RemoveAll();	
	if ( !m_hAnimationSet.Get() )
		return;

	CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup();
	if ( !pPresetGroup )
		return;

	const CDmaElementArray< CDmePreset > &presetList = pPresetGroup->GetPresets();
	int nCount = presetList.Count( );
	for ( int i = 0; i < nCount; ++i )
	{
		CDmePreset *pPreset = presetList[i];
		KeyValues *kv = new KeyValues( "node", "name", pPreset->GetName() );
		SetElementKeyValue( kv, "preset", pPreset );
		kv->SetInt( "index", i );
		int nItemID = m_pPresetList->AddItem( kv, 0, false, false );
		if ( pSelectedPreset == pPreset )
		{
			m_pPresetList->AddSelectedItem( nItemID );
		}
	}

	m_pPresetList->SortList();
}


//-----------------------------------------------------------------------------
// Called to open a context-sensitive menu for a particular menu item
//-----------------------------------------------------------------------------
CDmePreset* CDmePresetGroupEditorPanel::GetSelectedPreset()
{
	if ( !m_hAnimationSet.Get() )
		return NULL;

	int nSelectedPresetCount = m_pPresetList->GetSelectedItemsCount();
	if ( nSelectedPresetCount != 1 )
		return NULL;

	int nItemID = m_pPresetList->GetSelectedItem( 0 );
	KeyValues *pKeyValues = m_pPresetList->GetItem( nItemID );

	CDmePreset *pPreset = GetElementKeyValue< CDmePreset >( pKeyValues, "preset" );
	return pPreset;
}


//-----------------------------------------------------------------------------
// Selects a particular preset 
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::SetSelectedPreset( CDmePreset* pPreset )
{
	m_pPresetList->ClearSelectedItems();
	for ( int nItemID = m_pPresetList->FirstItem(); 
		nItemID != m_pPresetList->InvalidItemID(); 
		nItemID = m_pPresetList->NextItem( nItemID ) )
	{
		KeyValues* pKeyValues = m_pPresetList->GetItem( nItemID );
		CDmePreset *pItemPreset = GetElementKeyValue< CDmePreset >( pKeyValues, "preset" );
		if ( pItemPreset == pPreset )
		{
			m_pPresetList->AddSelectedItem( nItemID );
		}
	}
}


//-----------------------------------------------------------------------------
// Called to open a context-sensitive menu for a particular menu item
//-----------------------------------------------------------------------------
CDmePresetGroup* CDmePresetGroupEditorPanel::GetSelectedPresetGroup()
{
	if ( !m_hAnimationSet.Get() )
		return NULL;

	int nSelectedItemCount = m_pPresetGroupList->GetSelectedItemsCount();
	if ( nSelectedItemCount != 1 )
		return NULL;

	int nItemID = m_pPresetGroupList->GetSelectedItem( 0 );
	KeyValues *pKeyValues = m_pPresetGroupList->GetItem( nItemID );
	CDmePresetGroup *pPresetGroup = GetElementKeyValue<CDmePresetGroup>( pKeyValues, "presetGroup" );
	return pPresetGroup;
}


//-----------------------------------------------------------------------------
// Selects a particular preset group
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::SetSelectedPresetGroup( CDmePresetGroup* pPresetGroup )
{
	m_pPresetGroupList->ClearSelectedItems();
	for ( int nItemID = m_pPresetGroupList->FirstItem(); 
		nItemID != m_pPresetGroupList->InvalidItemID(); 
		nItemID = m_pPresetGroupList->NextItem( nItemID ) )
	{
		KeyValues* pKeyValues = m_pPresetGroupList->GetItem( nItemID );
		CDmePresetGroup *pItemPresetGroup = GetElementKeyValue< CDmePresetGroup >( pKeyValues, "presetGroup" );
		if ( pItemPresetGroup == pPresetGroup )
		{
			m_pPresetGroupList->AddSelectedItem( nItemID );
		}
	}
}


//-----------------------------------------------------------------------------
// If it finds a duplicate preset name, reports an error message and returns it found one
//-----------------------------------------------------------------------------
bool CDmePresetGroupEditorPanel::HasDuplicatePresetName( const char *pPresetName, CDmePreset *pIgnorePreset )
{
	CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup();
	if ( !pPresetGroup )
		return false;

	CDmePreset *pMatch = pPresetGroup->FindPreset( pPresetName );
	if ( pMatch && pMatch != pIgnorePreset )
	{
		vgui::MessageBox *pError = new vgui::MessageBox( "#DmePresetGroupEditor_DuplicatePresetNameTitle", "#DmePresetGroupEditor_DuplicatePresetNameText", this );
		pError->DoModal();
		return true;
	}

	return false;
}


//-----------------------------------------------------------------------------
// Called by OnInputCompleted after we get a new group name
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::PerformAddPreset( const char *pNewPresetName )
{
	CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup();
	if ( !pPresetGroup )
		return;

	if ( HasDuplicatePresetName( pNewPresetName ) ) 
		return;

	CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Add Preset" );
	CDmePreset *pPreset = pPresetGroup->FindOrAddPreset( pNewPresetName );
	sg.Release();

	RefreshPresetNames();
	SetSelectedPreset( pPreset );
	NotifyDataChanged();

	KeyValues *pKeyValues = new KeyValues( "AddNewPreset" );
	SetElementKeyValue( pKeyValues, "preset", pPreset );
	PostActionSignal( pKeyValues );
}


//-----------------------------------------------------------------------------
// Called by OnInputCompleted after we get a new group name
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::PerformRenamePreset( const char *pNewPresetName )
{
	CDmePreset *pPreset = GetSelectedPreset();
	if ( !pPreset )
		return;

	if ( HasDuplicatePresetName( pNewPresetName, pPreset ) ) 
		return;

	CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Rename Preset" );
	pPreset->SetName( pNewPresetName );
	sg.Release();

	RefreshPresetNames();
	NotifyDataChanged();
}


//-----------------------------------------------------------------------------
// Add a preset 
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnAddPreset()
{
	vgui::InputDialog *pInput = new vgui::InputDialog( this, "Add Preset", "Enter name of new preset" );
	pInput->SetMultiline( false );
	pInput->DoModal( new KeyValues( "OnAddPreset" ) );
}


//-----------------------------------------------------------------------------
// Rename a preset 
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnRenamePreset()
{
	CDmePreset *pPreset = GetSelectedPreset();
	if ( !pPreset )
		return;

	vgui::InputDialog *pInput = new vgui::InputDialog( this, "Rename Preset", "Enter new name of preset" );
	pInput->SetMultiline( false );
	pInput->DoModal( new KeyValues( "OnRenamePreset" ) );
}


//-----------------------------------------------------------------------------
// Remove a preset
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnRemovePreset()
{
	CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup();
	if ( !pPresetGroup )
		return;

	CDmePreset *pPreset = GetSelectedPreset();
	if ( !pPreset )
		return;

	int nItemID = m_pPresetList->GetSelectedItem( 0 );
	int nCurrentRow = m_pPresetList->GetItemCurrentRow( nItemID );

	CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Remove Preset" );
	pPresetGroup->RemovePreset( pPreset );
	sg.Release();

	RefreshPresetNames();
	if ( nCurrentRow >= m_pPresetList->GetItemCount() )
	{
		--nCurrentRow;
	}
	if ( nCurrentRow >= 0 )
	{
		nItemID = m_pPresetList->GetItemIDFromRow( nCurrentRow );
		m_pPresetList->ClearSelectedItems();
		m_pPresetList->AddSelectedItem( nItemID );
	}
	NotifyDataChanged();
}


//-----------------------------------------------------------------------------
// Called to open a context-sensitive menu for a particular menu item
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnMovePresetUp()
{
	CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup();
	CDmePreset *pPreset = GetSelectedPreset();
	if ( !pPresetGroup || !pPreset )
		return;

	CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Reorder Presets" );
	pPresetGroup->MovePresetUp( pPreset );
	sg.Release();

	RefreshPresetNames();
	SetSelectedPreset( pPreset );
	NotifyDataChanged();
}


//-----------------------------------------------------------------------------
// Called to open a context-sensitive menu for a particular menu item
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnMovePresetDown()
{
	CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup();
	CDmePreset *pPreset = GetSelectedPreset();
	if ( !pPresetGroup || !pPreset )
		return;

	CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Reorder Presets" );
	pPresetGroup->MovePresetDown( pPreset );
	sg.Release();

	RefreshPresetNames();
	SetSelectedPreset( pPreset );
	NotifyDataChanged();
}


//-----------------------------------------------------------------------------
// Drag/drop reordering of presets
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::MovePresetInFrontOf( CDmePreset *pDragPreset, CDmePreset *pDropPreset )
{
	CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup();
	if ( !pPresetGroup )
		return;

	CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Reorder Presets" );
	pPresetGroup->MovePresetInFrontOf( pDragPreset, pDropPreset );
	sg.Release();

	RefreshPresetNames();
	SetSelectedPreset( pDragPreset );
	NotifyDataChanged();
}


//-----------------------------------------------------------------------------
// Fileopen state machine
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnFileStateMachineFinished( KeyValues *pParams )
{
	KeyValues *pContextKeyValues = pParams->GetFirstTrueSubKey();
	if ( Q_stricmp( pContextKeyValues->GetName(), "ImportPresets" ) )
		return;

	CDmElement *pRoot = GetElementKeyValue<CDmElement>( pContextKeyValues, "presets" );
	if ( !pRoot )
		return;

	if ( pParams->GetInt( "completionState", 0 ) != 0 )
	{
		CPresetPickerFrame *pPresetPicker = new CPresetPickerFrame( this, "Select Preset(s) to Import" );
		pPresetPicker->AddActionSignalTarget( this );
		KeyValues *pContextKeyValuesImport = new KeyValues( "ImportPicked" );
		SetElementKeyValue( pContextKeyValuesImport, "presets", pRoot );
		pPresetPicker->DoModal( pRoot, true, pContextKeyValuesImport );
	}
	else
	{
		// Clean up the read-in file
		CDisableUndoScopeGuard sg;
		g_pDataModel->RemoveFileId( pRoot->GetFileId() );
	}
}


//-----------------------------------------------------------------------------
// Finds a control index
//-----------------------------------------------------------------------------
struct ExportedControl_t
{
	CUtlString m_Name;
	bool m_bIsStereo;
	bool m_bIsMulti;
	int m_nFirstIndex;
};

static int FindExportedControlIndex( const char *pControlName, CUtlVector< ExportedControl_t > &uniqueControls )
{
	int nCount = uniqueControls.Count();
	for ( int i = 0; i < nCount; ++i )
	{
		if ( !Q_stricmp( pControlName, uniqueControls[i].m_Name ) )
			return i;
	}
	return -1;
}


//-----------------------------------------------------------------------------
// Builds a unique list of controls found in the presets
//-----------------------------------------------------------------------------
static int BuildExportedControlList( CDmeAnimationSet *pAnimationSet, CDmePresetGroup *pPresetGroup, CUtlVector< ExportedControl_t > &uniqueControls )
{
	int nGlobalIndex = 0;
	const CDmrElementArray< CDmePreset > &presets = pPresetGroup->GetPresets();
	int nPresetCount = presets.Count();
	for ( int iPreset = 0; iPreset < nPresetCount; ++iPreset )
	{
		CDmePreset *pPreset = presets[iPreset];
		const CDmrElementArray< CDmElement > &controls = pPreset->GetControlValues();

		int nControlCount = controls.Count();
		for ( int i = 0; i < nControlCount; ++i )
		{
			const char *pControlName = controls[i]->GetName();
			int nIndex = FindExportedControlIndex( pControlName, uniqueControls );
			if ( nIndex >= 0 )
				continue;
			CDmAttribute *pValueAttribute = controls[i]->GetAttribute( "value" );
			if ( !pValueAttribute || pValueAttribute->GetType() != AT_FLOAT )
				continue;

			CDmElement *pControl = pAnimationSet->FindControl( pControlName );
			if ( !pControl )
				continue;

			int j = uniqueControls.AddToTail();
			ExportedControl_t &control = uniqueControls[j];
			control.m_Name = pControlName;
			control.m_bIsStereo = pControl->GetValue<bool>( "combo" );
			control.m_bIsMulti = pControl->GetValue<bool>( "multi" );
			control.m_nFirstIndex = nGlobalIndex;
			nGlobalIndex += 1 + control.m_bIsStereo + control.m_bIsMulti;
		}
	}
	return nGlobalIndex;
}


//-----------------------------------------------------------------------------
// Fileopen state machine
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::SetupFileOpenDialog( vgui::FileOpenDialog *pDialog, bool bOpenFile, const char *pFileFormat, KeyValues *pContextKeyValues )
{
	if ( bOpenFile )
	{
		pDialog->SetTitle( "Import Preset File", true );
	}
	else
	{
		pDialog->SetTitle( "Export Preset File", true );
	}

	char pPresetPath[MAX_PATH];
	if ( !Q_stricmp( pFileFormat, PRESET_FILE_FORMAT ) )
	{
		GetModSubdirectory( "models", pPresetPath, sizeof(pPresetPath) );
		pDialog->SetStartDirectoryContext( "preset_importexport", pPresetPath );
		pDialog->AddFilter( "*.*", "All Files (*.*)", false );
		pDialog->AddFilter( "*.pre", "Preset File (*.pre)", true, PRESET_FILE_FORMAT );
	}
	else if ( !Q_stricmp( pFileFormat, "vfe" ) )
	{
		GetModSubdirectory( "expressions", pPresetPath, sizeof(pPresetPath) );
		pDialog->SetStartDirectoryContext( "preset_exportvfe", pPresetPath );
		pDialog->AddFilter( "*.*", "All Files (*.*)", false );
		pDialog->AddFilter( "*.vfe", "Expression File (*.vfe)", true, "vfe" );
	}
	else if ( !Q_stricmp( pFileFormat, "txt" ) )
	{
		GetModSubdirectory( "expressions", pPresetPath, sizeof(pPresetPath) );
		pDialog->SetStartDirectoryContext( "preset_exportvfe", pPresetPath );
		pDialog->AddFilter( "*.*", "All Files (*.*)", false );
		pDialog->AddFilter( "*.txt", "Faceposer Expression File (*.txt)", true, "txt" );
	}
}

bool CDmePresetGroupEditorPanel::OnReadFileFromDisk( const char *pFileName, const char *pFileFormat, KeyValues *pContextKeyValues )
{
	CDmElement *pRoot;
	CDisableUndoScopeGuard sgRestore;
	DmFileId_t fileId = g_pDataModel->RestoreFromFile( pFileName, NULL, pFileFormat, &pRoot, CR_FORCE_COPY );
	sgRestore.Release();

	if ( fileId == DMFILEID_INVALID )
		return false;

	// When importing an entire group, we can do it all right here
	if ( !Q_stricmp( pContextKeyValues->GetName(), "ImportPresetGroup" ) )
	{
		CDmePresetGroup *pPresetGroup = CastElement< CDmePresetGroup >( pRoot );
		if ( !pPresetGroup )
			return false;

		CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Import Preset Group" );
		pPresetGroup->SetFileId( m_hAnimationSet->GetFileId(), TD_DEEP );
		m_hAnimationSet->GetPresetGroups( ).AddToTail( pPresetGroup );
		sg.Release();

		// Warn if we import a remap which doesn't exist
		CDmePresetRemap *pPresetRemap = pPresetGroup->GetPresetRemap();
		if ( pPresetRemap )
		{
			if ( m_hAnimationSet->FindPresetGroup( pPresetRemap->m_SourcePresetGroup ) == NULL )
			{
				char pBuf[512];
				Q_snprintf( pBuf, sizeof(pBuf), 
					"Import contains a remap which refers to an unknown preset group \"%s\"!\n", 
					pPresetRemap->m_SourcePresetGroup.Get() );
				vgui::MessageBox *pError = new vgui::MessageBox( "Bad source remap name!", pBuf, this );
				pError->DoModal();
			}
		}

		RefreshAnimationSet();
		NotifyDataChanged();
		return true;
	}

	CDmAttribute* pPresets = pRoot->GetAttribute( "presets", AT_ELEMENT_ARRAY );
	if ( !pPresets )
		return false;

	SetElementKeyValue( pContextKeyValues, "presets", pRoot );
	return true;
}

bool CDmePresetGroupEditorPanel::OnWriteFileToDisk( const char *pFileName, const char *pFileFormat, KeyValues *pContextKeyValues )
{
	// Used when exporting an entire preset group
	if ( !Q_stricmp( pContextKeyValues->GetName(), "ExportPresetGroup" ) )
	{
		CDmePresetGroup *pPresetGroup = GetElementKeyValue<CDmePresetGroup>( pContextKeyValues, "presetGroup" );
		if ( !pPresetGroup )
			return false;

		bool bOk = g_pDataModel->SaveToFile( pFileName, NULL, g_pDataModel->GetDefaultEncoding( pFileFormat ), pFileFormat, pPresetGroup );
		return bOk;
	}

	// Used when exporting an entire preset group
	if ( !Q_stricmp( pContextKeyValues->GetName(), "ExportPresetGroupToVFE" ) )
	{
		CDmePresetGroup *pPresetGroup = GetElementKeyValue<CDmePresetGroup>( pContextKeyValues, "presetGroup" );
		if ( !pPresetGroup )
			return false;

		bool bOk = pPresetGroup->ExportToVFE( pFileName, m_hAnimationSet );
		return bOk;
	}

	// Used when exporting an entire preset group
	if ( !Q_stricmp( pContextKeyValues->GetName(), "ExportPresetGroupToTXT" ) )
	{
		CDmePresetGroup *pPresetGroup = GetElementKeyValue<CDmePresetGroup>( pContextKeyValues, "presetGroup" );
		if ( !pPresetGroup )
			return false;

		bool bOk = pPresetGroup->ExportToTXT( pFileName, m_hAnimationSet );
		return bOk;
	}

	// Used when exporting a subset of a preset group
	int nCount = pContextKeyValues->GetInt( "count" );
	if ( nCount == 0 )
		return true;

	CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup();
	const char *pPresetGroupName = pPresetGroup ? pPresetGroup->GetName() : "root";

	CDisableUndoScopeGuard sg;
	CDmePresetGroup *pRoot = CreateElement< CDmePresetGroup >( pPresetGroupName, DMFILEID_INVALID );
	CDmaElementArray< CDmePreset >& presets = pRoot->GetPresets( );

	// Build list of selected presets 
	for ( int i = 0; i < nCount; ++i )
	{
		char pBuf[32];
		Q_snprintf( pBuf, sizeof(pBuf), "%d", i );
		CDmePreset *pPreset = GetElementKeyValue<CDmePreset>( pContextKeyValues, pBuf );
		presets.AddToTail( pPreset );
	}

	bool bOk = g_pDataModel->SaveToFile( pFileName, NULL, g_pDataModel->GetDefaultEncoding( pFileFormat ), pFileFormat, pRoot );
	g_pDataModel->DestroyElement( pRoot->GetHandle() );
	return bOk;
}


//-----------------------------------------------------------------------------
// Called when preset picking is cancelled
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnPresetPickCancelled( KeyValues *pParams )
{
	KeyValues *pContextKeyValues = pParams->FindKey( "ImportPicked" );
	if ( pContextKeyValues )
	{
		// Clean up the read-in file
		CDisableUndoScopeGuard sg;
		CDmElement *pRoot = GetElementKeyValue<CDmElement>( pContextKeyValues, "presets" );
		g_pDataModel->RemoveFileId( pRoot->GetFileId() );
		return;
	}
}


//-----------------------------------------------------------------------------
// Actually imports the presets from a file
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::ImportPresets( const CUtlVector< CDmePreset * >& presets )
{
	CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup();
	if ( !pPresetGroup )
		return;

	CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Import Presets" );

	int nPresetCount = presets.Count();
	for ( int i = 0; i < nPresetCount; ++i )
	{
		CDmePreset *pPreset = pPresetGroup->FindOrAddPreset( presets[i]->GetName() );
		const CDmaElementArray< CDmElement > &srcValues = presets[i]->GetControlValues( );
		CDmaElementArray< CDmElement > &values = pPreset->GetControlValues( );
		values.RemoveAll();

		int nValueCount = srcValues.Count();
		for ( int j = 0; j < nValueCount; ++j )
		{
			CDmElement *pSrcControlValue = srcValues[j];
			CDmElement *pControlValue = pSrcControlValue->Copy( );
			pControlValue->SetFileId( pPresetGroup->GetFileId(), TD_DEEP );
			values.AddToTail( pControlValue );
		}
	}

	RefreshAnimationSet();
	NotifyDataChanged();
}


//-----------------------------------------------------------------------------
// The 'export presets' context menu option
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnPresetPicked( KeyValues *pParams )
{
	CUtlVector< CDmePreset * > presets;
	int nCount = pParams->GetInt( "count" );
	if ( nCount == 0 )
		return;

	// Build list of selected presets 
	for ( int i = 0; i < nCount; ++i )
	{
		char pBuf[32];
		Q_snprintf( pBuf, sizeof(pBuf), "%d", i );
		CDmePreset *pPreset = GetElementKeyValue<CDmePreset>( pParams, pBuf );
		presets.AddToTail( pPreset );
	}

	if ( pParams->FindKey( "ExportPicked" ) )
	{
		KeyValues *pContextKeyValues = new KeyValues( "ExportPresets" );
		pContextKeyValues->SetInt( "count", nCount );
		for ( int i = 0; i < nCount; ++i )
		{
			char pBuf[32];
			Q_snprintf( pBuf, sizeof(pBuf), "%d", i );
			SetElementKeyValue( pContextKeyValues, pBuf, presets[i] );
		}

		m_hFileOpenStateMachine->SaveFile( pContextKeyValues, NULL, PRESET_FILE_FORMAT, vgui::FOSM_SHOW_PERFORCE_DIALOGS );
		return;
	}

	KeyValues *pContextKeyValues = pParams->FindKey( "ImportPicked" );
	if ( pContextKeyValues )
	{
		ImportPresets( presets );

		// Clean up the read-in file
		{
			CDisableUndoScopeGuard sg;
			CDmElement *pRoot = GetElementKeyValue<CDmElement>( pContextKeyValues, "presets" );
			g_pDataModel->RemoveFileId( pRoot->GetFileId() );
			return;
		}
	}
}


//-----------------------------------------------------------------------------
// The 'export presets' context menu option
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnExportPresets()
{
	CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup();
	if ( !pPresetGroup )
		return;

	CPresetPickerFrame *pPresetPicker = new CPresetPickerFrame( this, "Select Preset(s) to Export" );
	pPresetPicker->AddActionSignalTarget( this );
	pPresetPicker->DoModal( pPresetGroup, true, new KeyValues( "ExportPicked" ) );
}


//-----------------------------------------------------------------------------
// The 'import presets' context menu option
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnImportPresets()
{
	KeyValues *pContextKeyValues = new KeyValues( "ImportPresets" );
	m_hFileOpenStateMachine->OpenFile( PRESET_FILE_FORMAT, pContextKeyValues );
}


//-----------------------------------------------------------------------------
// The 'export preset groups to VFE' context menu option
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnExportPresetGroupToVFE()
{
	CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup();
	if ( !pPresetGroup )
		return;

	KeyValues *pContextKeyValues = new KeyValues( "ExportPresetGroupToVFE" );
	SetElementKeyValue( pContextKeyValues, "presetGroup", pPresetGroup );
	m_hFileOpenStateMachine->SaveFile( pContextKeyValues, NULL, "vfe", vgui::FOSM_SHOW_PERFORCE_DIALOGS );
}


//-----------------------------------------------------------------------------
// The 'export preset groups to TXT' context menu option
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnExportPresetGroupToTXT()
{
	CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup();
	if ( !pPresetGroup )
		return;

	KeyValues *pContextKeyValues = new KeyValues( "ExportPresetGroupToTXT" );
	SetElementKeyValue( pContextKeyValues, "presetGroup", pPresetGroup );
	m_hFileOpenStateMachine->SaveFile( pContextKeyValues, NULL, "txt", vgui::FOSM_SHOW_PERFORCE_DIALOGS );
}


//-----------------------------------------------------------------------------
// The 'export preset groups' context menu option
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnExportPresetGroups()
{
	CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup();
	if ( !pPresetGroup )
		return;

	KeyValues *pContextKeyValues = new KeyValues( "ExportPresetGroup" );
	SetElementKeyValue( pContextKeyValues, "presetGroup", pPresetGroup );
	m_hFileOpenStateMachine->SaveFile( pContextKeyValues, NULL, PRESET_FILE_FORMAT, vgui::FOSM_SHOW_PERFORCE_DIALOGS );
}


//-----------------------------------------------------------------------------
// The 'import preset groups' context menu option
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnImportPresetGroups()
{
	KeyValues *pContextKeyValues = new KeyValues( "ImportPresetGroup" );
	m_hFileOpenStateMachine->OpenFile( PRESET_FILE_FORMAT, pContextKeyValues );
}


//-----------------------------------------------------------------------------
// Preset remap editor
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnRemoveDefaultControls()
{
	CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup();
	if ( !pPresetGroup )
		return;

	CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Remove Default Controls" );
	CDmrElementArray< CDmePreset > presets = pPresetGroup->GetPresets();
	int nPresetCount = presets.Count();
	for ( int i = 0; i < nPresetCount; ++i )
	{
		CDmePreset *pPreset = presets[i];
		CDmrElementArray< CDmElement > controls = pPreset->GetControlValues();	
		int nControlCount = controls.Count();
		for ( int j = nControlCount; --j >= 0; )
		{
			CDmElement *pControlValue = controls[j];
			CDmElement *pControl = m_hAnimationSet->FindControl( pControlValue->GetName() );
			if ( !pControl )
			{
				controls.Remove( j );
				continue;
			}

			bool bIsDefault = true;
			if ( pControl->GetValue<float>( "defaultValue" ) != pControlValue->GetValue<float>( "value" ) )
			{
				bIsDefault = false;
			}

			bool bIsStereo = pControl->GetValue<bool>( "combo" );
			if ( bIsStereo )
			{
				if ( pControl->GetValue<float>( "defaultBalance" ) != pControlValue->GetValue<float>( "balance" ) )
				{
					bIsDefault = false;
				}
			}

			bool bIsMulti = pControl->GetValue<bool>( "multi" );
			if ( bIsMulti )
			{
				if ( pControl->GetValue<float>( "defaultMultilevel" ) != pControlValue->GetValue<float>( "multilevel" ) )
				{
					bIsDefault = false;
				}
			}

			if ( bIsDefault )
			{
				controls.Remove( j );
			}
		}
	}
}


//-----------------------------------------------------------------------------
// Preset remap editor
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnEditPresetRemapping()
{
	CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup();
	if ( !pPresetGroup )
		return;

	CDmePresetRemapPanel *pPresetRemapPanel = new CDmePresetRemapPanel( this, "Manage Preset Remapping" );
	pPresetRemapPanel->DoModal( m_hAnimationSet, pPresetGroup );
}


//-----------------------------------------------------------------------------
// Perform preset remap
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnRemapPresets()
{
	CDmePresetGroup *pDestPresetGroup = GetSelectedPresetGroup();
	if ( !pDestPresetGroup || pDestPresetGroup->m_bIsReadOnly )
		return;

	CDmePresetRemap *pPresetRemap = pDestPresetGroup->GetPresetRemap();
	if ( !pPresetRemap )
		return;

	CDmePresetGroup *pSourcePresetGroup = m_hAnimationSet->FindPresetGroup( pPresetRemap->m_SourcePresetGroup );
	if ( !pSourcePresetGroup )
	{
		char pBuf[512];
		Q_snprintf( pBuf, sizeof(pBuf), "Unable to find preset group name %s in animation set %s!\n", 
			pPresetRemap->m_SourcePresetGroup.Get(), m_hAnimationSet->GetName() );
		vgui::MessageBox *pError = new vgui::MessageBox( "Bad source remap name!", pBuf, this );
		pError->DoModal();
		return;
	}

	CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Remap Presets" );

	int nCount = pPresetRemap->GetRemapCount();
	for ( int i = 0; i < nCount; ++i )
	{
		const char *pSourceName = pPresetRemap->GetRemapSource( i );
		CDmePreset *pSourcePreset = pSourcePresetGroup->FindPreset( pSourceName );

		const char *pDestName = pPresetRemap->GetRemapDest( i );
		CDmePreset *pDestPreset = pDestPresetGroup->FindPreset( pDestName );

		if ( !pSourcePreset || !pDestPreset )
			continue;

		pDestPreset->CopyControlValuesFrom( pSourcePreset );
	}

	sg.Release();
	NotifyDataChanged();
}


//-----------------------------------------------------------------------------
// Called to open a context-sensitive menu for a particular preset
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnOpenPresetContextMenu( )
{
	if ( !m_hAnimationSet.Get() )
		return;

	CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup();
	if ( !pPresetGroup )
		return;

	CDmePreset *pPreset = GetSelectedPreset();

	m_hContextMenu = new vgui::Menu( this, "ActionMenu" );

	// Can only export from read-only groups
	if ( pPresetGroup->m_bIsReadOnly )
	{
		m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_ExportPresets", new KeyValues( "ExportPresets" ), this );
		vgui::Menu::PlaceContextMenu( this, m_hContextMenu.Get() );
		return;
	}

	m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_AddPreset", new KeyValues( "AddPreset" ), this );
	if ( pPreset )
	{
		m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_RenamePreset", new KeyValues( "RenamePreset" ), this );
		m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_RemovePreset", new KeyValues( "RemovePreset" ), this );
		m_hContextMenu->AddSeparator();
		m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_MoveUp", new KeyValues( "MovePresetUp" ), this );
		m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_MoveDown", new KeyValues( "MovePresetDown" ), this );
	}

	m_hContextMenu->AddSeparator();
	m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_ImportPresets", new KeyValues( "ImportPresets" ), this );
	m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_ExportPresets", new KeyValues( "ExportPresets" ), this );

	vgui::Menu::PlaceContextMenu( this, m_hContextMenu.Get() );
}


//-----------------------------------------------------------------------------
// Called to open a context-sensitive menu for a particular menu item
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnOpenContextMenu( KeyValues *kv )
{
	CleanupContextMenu();
	if ( !m_hAnimationSet.Get() )
		return;

	Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL );
	if ( pPanel == m_pPresetList )
	{
		OnOpenPresetContextMenu();
		return;
	}

	if ( pPanel != m_pPresetGroupList )
		return;
    
	m_hContextMenu = new vgui::Menu( this, "ActionMenu" );
	m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_AddGroup", new KeyValues( "AddGroup" ), this );
	m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_AddPhonemeGroup", new KeyValues( "AddPhonemeGroup" ), this );

	CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup();
	if ( pPresetGroup )
	{
		m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_RenameGroup", new KeyValues( "RenameGroup" ), this );
		m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_RemoveGroup", new KeyValues( "RemoveGroup" ), this );
		m_hContextMenu->AddSeparator();
		m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_ToggleVisibility", new KeyValues( "ToggleGroupVisibility" ), this );
		m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_ToggleSharing", new KeyValues( "ToggleGroupSharing" ), this );
		m_hContextMenu->AddSeparator();
		m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_MoveUp", new KeyValues( "MoveGroupUp" ), this );
		m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_MoveDown", new KeyValues( "MoveGroupDown" ), this );

		CDmePresetRemap *pPresetRemap = pPresetGroup->GetPresetRemap();
		bool bUseSeparator = !pPresetGroup->m_bIsReadOnly || pPresetRemap;
		if ( bUseSeparator )
		{
			m_hContextMenu->AddSeparator();
		}
		if ( !pPresetGroup->m_bIsReadOnly )
		{
			m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_RemoveDefaultControls", new KeyValues( "RemoveDefaultControls" ), this );
			m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_EditPresetRemapping", new KeyValues( "EditPresetRemapping" ), this );
		}
		if ( pPresetRemap )
		{
			m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_RemapPresets", new KeyValues( "RemapPresets" ), this );
		}
	}
	m_hContextMenu->AddSeparator();
	m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_ImportPresets", new KeyValues( "ImportPresetGroups" ), this );
	if ( pPresetGroup )
	{
		m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_ExportPresets", new KeyValues( "ExportPresetGroups" ), this );
		m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_ExportPresetsToFaceposer", new KeyValues( "ExportPresetGroupsToTXT" ), this );
		m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_ExportPresetsToExpression", new KeyValues( "ExportPresetGroupsToVFE" ), this );
	}

	vgui::Menu::PlaceContextMenu( this, m_hContextMenu.Get() );
}


//-----------------------------------------------------------------------------
// Called when a list panel's selection changes
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnItemSelected( KeyValues *kv )
{
	Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL );
	if ( pPanel == m_pPresetGroupList )
	{
		RefreshPresetNames();
		return;
	}
}


//-----------------------------------------------------------------------------
// Called when a list panel's selection changes
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnItemDeselected( KeyValues *kv )
{
	Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL );
	if ( pPanel == m_pPresetGroupList )
	{
		RefreshPresetNames();
		return;
	}
}


//-----------------------------------------------------------------------------
// If it finds a duplicate control name, reports an error message and returns it found one
//-----------------------------------------------------------------------------
bool CDmePresetGroupEditorPanel::HasDuplicateGroupName( const char *pGroupName, CDmePresetGroup *pIgnorePreset )
{
	if ( !m_hAnimationSet )
		return false;

	CDmePresetGroup *pMatch = m_hAnimationSet->FindPresetGroup( pGroupName );
	if ( pMatch && pMatch != pIgnorePreset )
	{
		vgui::MessageBox *pError = new vgui::MessageBox( "#DmePresetGroupEditor_DuplicateNameTitle", "#DmePresetGroupEditor_DuplicateNameText", this );
		pError->DoModal();
		return true;
	}

	return false;
}


//-----------------------------------------------------------------------------
// Called by OnInputCompleted after we get a new group name
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::PerformAddGroup( const char *pNewGroupName )
{
	if ( !m_hAnimationSet )
		return;

	if ( HasDuplicateGroupName( pNewGroupName ) ) 
		return;

	CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Add Preset Group" );
	CDmePresetGroup *pPresetGroup = m_hAnimationSet->FindOrAddPresetGroup( pNewGroupName );
	sg.Release();

	RefreshAnimationSet();
	SetSelectedPresetGroup( pPresetGroup );
	NotifyDataChanged();
}


//-----------------------------------------------------------------------------
// Called by OnInputCompleted after we get a new group name
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::PerformAddPhonemeGroup( const char *pNewGroupName )
{
	if ( !m_hAnimationSet )
		return;

	if ( HasDuplicateGroupName( pNewGroupName ) ) 
		return;

	CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Add Phoneme Preset Group" );
	CDmePresetGroup *pPresetGroup = m_hAnimationSet->FindOrAddPresetGroup( pNewGroupName );

	int nPhonemeCount = NumPhonemes();
	for ( int i = 0; i < nPhonemeCount; ++i )
	{
		if ( !IsStandardPhoneme( i ) )
			continue;

		char pTempBuf[256];
		const char *pPhonemeName = NameForPhonemeByIndex( i );
		if ( !Q_stricmp( pPhonemeName, "<sil>" ) )
		{
			pPhonemeName = "silence";
		}
		Q_snprintf( pTempBuf, sizeof(pTempBuf), "p_%s", pPhonemeName );

		pPresetGroup->FindOrAddPreset( pTempBuf );
	}

	sg.Release();

	RefreshAnimationSet();
	SetSelectedPresetGroup( pPresetGroup );
	NotifyDataChanged();
}


//-----------------------------------------------------------------------------
// Called by OnInputCompleted after we get a new group name
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::PerformRenameGroup( const char *pNewGroupName )
{
	CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup();
	if ( !pPresetGroup )
		return;

	if ( HasDuplicateGroupName( pNewGroupName, pPresetGroup ) ) 
		return;

	CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Rename Preset Group" );
	pPresetGroup->SetName( pNewGroupName );
	sg.Release();

	RefreshAnimationSet();
	NotifyDataChanged();
}


//-----------------------------------------------------------------------------
// Called by OnGroupControls after we get a new group name
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnInputCompleted( KeyValues *pKeyValues )
{
	const char *pName = pKeyValues->GetString( "text", NULL );
	if ( !pName || !pName[0] )						  
		return;

	if ( pKeyValues->FindKey( "OnAddGroup" ) )
	{
		PerformAddGroup( pName );
		return;
	}

	if ( pKeyValues->FindKey( "OnAddPhonemeGroup" ) )
	{
		PerformAddPhonemeGroup( pName );
		return;
	}

	if ( pKeyValues->FindKey( "OnRenameGroup" ) )
	{
		PerformRenameGroup( pName );
		return;
	}

	if ( pKeyValues->FindKey( "OnAddPreset" ) )
	{
		PerformAddPreset( pName );
		return;
	}

	if ( pKeyValues->FindKey( "OnRenamePreset" ) )
	{
		PerformRenamePreset( pName );
		return;
	}
}


//-----------------------------------------------------------------------------
// Toggle group visibility
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::ToggleGroupVisibility( CDmePresetGroup *pPresetGroup )
{
	if ( pPresetGroup )
	{
		CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Toggle Preset Group Visibility" );

		pPresetGroup->m_bIsVisible = !pPresetGroup->m_bIsVisible;
	}

	RefreshAnimationSet();
	NotifyDataChanged();
}


//-----------------------------------------------------------------------------
// Ungroup controls from each other
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnToggleGroupVisibility( )
{
	CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup();
	ToggleGroupVisibility( pPresetGroup );
}


//-----------------------------------------------------------------------------
// Ungroup controls from each other
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnToggleGroupSharing( )
{
	CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup();
	if ( pPresetGroup )
	{
		CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Toggle Preset Group Sharing" );

		pPresetGroup->SetShared( !pPresetGroup->IsShared() );
	}

	RefreshAnimationSet();
}


//-----------------------------------------------------------------------------
// Add a preset group
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnAddGroup()
{
	vgui::InputDialog *pInput = new vgui::InputDialog( this, "Add Preset Group", "Enter name of new preset group" );
	pInput->SetMultiline( false );
	pInput->DoModal( new KeyValues( "OnAddGroup" ) );
}


//-----------------------------------------------------------------------------
// Add a preset group
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnAddPhonemeGroup()
{
	vgui::InputDialog *pInput = new vgui::InputDialog( this, "Add Phoneme Preset Group", "Enter name of new preset group", "phoneme" );
	pInput->SetMultiline( false );
	pInput->DoModal( new KeyValues( "OnAddPhonemeGroup" ) );
}


//-----------------------------------------------------------------------------
// Rename a preset group
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnRenameGroup()
{
	CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup();
	if ( !pPresetGroup )
		return;

	vgui::InputDialog *pInput = new vgui::InputDialog( this, "Rename Preset Group", "Enter new name of preset group" );
	pInput->SetMultiline( false );
	pInput->DoModal( new KeyValues( "OnRenameGroup" ) );
}


//-----------------------------------------------------------------------------
// Remove a preset group
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnRemoveGroup()
{
	if ( !m_hAnimationSet.Get() )
		return;

	CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup();
	if ( !pPresetGroup )
		return;

	if ( !Q_stricmp( pPresetGroup->GetName(), "procedural" ) )
	{
		vgui::MessageBox *pError = new vgui::MessageBox( "#DmePresetGroupEditor_CannotRemovePresetGroupTitle", "#DmePresetGroupEditor_CannotRemovePresetGroupText", this );
		pError->DoModal();
		return;
	}

	int nItemID = m_pPresetGroupList->GetSelectedItem( 0 );
	int nCurrentRow = m_pPresetGroupList->GetItemCurrentRow( nItemID );

	CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Remove Preset Group" );
	m_hAnimationSet->RemovePresetGroup( pPresetGroup );
	sg.Release();

	RefreshAnimationSet();
	if ( nCurrentRow >= m_pPresetGroupList->GetItemCount() )
	{
		--nCurrentRow;
	}
	if ( nCurrentRow >= 0 )
	{
		nItemID = m_pPresetGroupList->GetItemIDFromRow( nCurrentRow );
		m_pPresetGroupList->ClearSelectedItems();
		m_pPresetGroupList->AddSelectedItem( nItemID );
	}
	NotifyDataChanged();
}


//-----------------------------------------------------------------------------
// Called to open a context-sensitive menu for a particular menu item
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnMoveGroupUp()
{
	CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup();
	if ( !pPresetGroup || !m_hAnimationSet.Get() )
		return;

	CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Reorder Preset Groups" );
	m_hAnimationSet->MovePresetGroupUp( pPresetGroup );
	sg.Release();

	RefreshAnimationSet();
	SetSelectedPresetGroup( pPresetGroup );
	NotifyDataChanged();
}


//-----------------------------------------------------------------------------
// Called to open a context-sensitive menu for a particular menu item
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::OnMoveGroupDown()
{
	CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup();
	if ( !pPresetGroup || !m_hAnimationSet.Get() )
		return;

	CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Reorder Preset Groups" );
	m_hAnimationSet->MovePresetGroupDown( pPresetGroup );
	sg.Release();

	RefreshAnimationSet();
	SetSelectedPresetGroup( pPresetGroup );
	NotifyDataChanged();
}


//-----------------------------------------------------------------------------
// Drag/drop reordering of preset groups
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::MovePresetGroupInFrontOf( CDmePresetGroup *pDragGroup, CDmePresetGroup *pDropGroup )
{
	if ( !m_hAnimationSet.Get() )
		return;

	CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Reorder Preset Groups" );
	m_hAnimationSet->MovePresetGroupInFrontOf( pDragGroup, pDropGroup );
	sg.Release();

	RefreshAnimationSet();
	SetSelectedPresetGroup( pDragGroup );
	NotifyDataChanged();
}


//-----------------------------------------------------------------------------
// Drag/drop preset moving
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorPanel::MovePresetIntoGroup( CDmePreset *pPreset, CDmePresetGroup *pGroup )
{
	if ( !m_hAnimationSet.Get() || !pPreset || !pGroup )
		return;

	CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Change Preset Group" );

	m_hAnimationSet->RemovePreset( pPreset );
	pGroup->GetPresets().AddToTail( pPreset );
	sg.Release();

	RefreshPresetNames();
	NotifyDataChanged();
}


//-----------------------------------------------------------------------------
//
//
// CDmePresetGroupListPanel
//
// Declaration above because of scoping issues
//
//
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
CDmePresetGroupListPanel::CDmePresetGroupListPanel( vgui::Panel *pParent, const char *pName, CDmePresetGroupEditorPanel *pComboPanel ) : 
	BaseClass( pParent, pName ), m_pPresetGroupPanel( pComboPanel )
{
}


//-----------------------------------------------------------------------------
// Handle keypresses
//-----------------------------------------------------------------------------
void CDmePresetGroupListPanel::OnMouseDoublePressed( vgui::MouseCode code )
{
	if ( code == MOUSE_LEFT )
	{
		int x, y, row, column;
		vgui::input()->GetCursorPos( x, y );
		GetCellAtPos( x, y, row, column );
		int itemId = GetItemIDFromRow( row );
		KeyValues *pKeyValues = GetItem( itemId );
		CDmePresetGroup *pPresetGroup = GetElementKeyValue< CDmePresetGroup >( pKeyValues, "presetGroup" );
		m_pPresetGroupPanel->ToggleGroupVisibility( pPresetGroup );
		return;
	}

	BaseClass::OnMouseDoublePressed( code );
}


//-----------------------------------------------------------------------------
// Handle keypresses
//-----------------------------------------------------------------------------
void CDmePresetGroupListPanel::OnKeyCodeTyped( vgui::KeyCode code )
{
	if ( code == KEY_DELETE || code == KEY_BACKSPACE )
	{
		m_pPresetGroupPanel->OnRemoveGroup();
		return;
	}

	if ( vgui::input()->IsKeyDown( KEY_LSHIFT ) || vgui::input()->IsKeyDown( KEY_RSHIFT ) )
	{
		if ( code == KEY_UP )
		{
			m_pPresetGroupPanel->OnMoveGroupUp();
			return;
		}
		
		if ( code == KEY_DOWN )
		{
			m_pPresetGroupPanel->OnMoveGroupDown();
			return;
		}
	}

	vgui::ListPanel::OnKeyCodeTyped( code );
}


//-----------------------------------------------------------------------------
// Called when a list panel's selection changes
//-----------------------------------------------------------------------------
void CDmePresetGroupListPanel::OnCreateDragData( KeyValues *msg )
{
	CDmePresetGroup *pPresetGroup = m_pPresetGroupPanel->GetSelectedPresetGroup();
	if ( !pPresetGroup )
		return;

	SetElementKeyValue( msg, "presetGroup", pPresetGroup );
	msg->SetInt( "selfDroppable", 1 );
}


//-----------------------------------------------------------------------------
// Called when a list panel's selection changes
//-----------------------------------------------------------------------------
bool CDmePresetGroupListPanel::IsDroppable( CUtlVector< KeyValues * >& msgList )
{
	if ( msgList.Count() > 0 )
	{
		KeyValues *pData( msgList[ 0 ] );
		if ( m_pPresetGroupPanel )
		{
			CDmePresetGroup *pPresetGroup = GetElementKeyValue< CDmePresetGroup >( pData, "presetGroup" );
			if ( pPresetGroup )
				return true;

			CDmePreset *pPreset = GetElementKeyValue< CDmePreset >( pData, "preset" );
			if ( pPreset )
			{
				// Can't drop presets onto read-only preset groups
				int x, y, row, column;
				vgui::input()->GetCursorPos( x, y );
				GetCellAtPos( x, y, row, column );
				KeyValues *pKeyValues = GetItem( row );
				CDmePresetGroup *pDropGroup = pKeyValues ? GetElementKeyValue<CDmePresetGroup>( pKeyValues, "presetGroup" ) : NULL;

				if ( pDropGroup && !pDropGroup->m_bIsReadOnly )
					return true;
			}
		}
	}

	return false;
}


//-----------------------------------------------------------------------------
// Called when a list panel's selection changes
//-----------------------------------------------------------------------------
void CDmePresetGroupListPanel::OnPanelDropped( CUtlVector< KeyValues * >& msgList )
{
	if ( msgList.Count() == 0 )
		return;

	KeyValues *pData = msgList[ 0 ];
	if ( !m_pPresetGroupPanel )
		return;

	// Discover the cell the panel is over
	int x, y, row, column;
	vgui::input()->GetCursorPos( x, y );
	GetCellAtPos( x, y, row, column );
	KeyValues *pKeyValues = GetItem( row );

	CDmePresetGroup *pDragGroup = GetElementKeyValue<CDmePresetGroup>( pData, "presetGroup" );
	if ( pDragGroup )
	{
		CDmePresetGroup *pDropGroup = pKeyValues ? GetElementKeyValue<CDmePresetGroup>( pKeyValues, "presetGroup" ) : NULL;
		m_pPresetGroupPanel->MovePresetGroupInFrontOf( pDragGroup, pDropGroup );
		return;
	}

	CDmePreset *pDragPreset = GetElementKeyValue<CDmePreset>( pData, "preset" );
	if ( pDragPreset )
	{
		CDmePresetGroup *pDropGroup = pKeyValues ? GetElementKeyValue<CDmePresetGroup>( pKeyValues, "presetGroup" ) : NULL;
		if ( pDropGroup )
		{
			m_pPresetGroupPanel->MovePresetIntoGroup( pDragPreset, pDropGroup );
		}
		return;
	}
}


//-----------------------------------------------------------------------------
// Mouse is now over a droppable panel
//-----------------------------------------------------------------------------
void CDmePresetGroupListPanel::OnDroppablePanelPaint( CUtlVector< KeyValues * >& msglist, CUtlVector< Panel * >& dragPanels )
{ 
	// Discover the cell the panel is over
	int x, y, w, h, row, column;
	vgui::input()->GetCursorPos( x, y );
	GetCellAtPos( x, y, row, column );
	GetCellBounds( row, 0, x, y, w, h );

	int x2, y2, w2, h2;
	GetCellBounds( row, 3, x2, y2, w2, h2 );
	w = x2 + w2 - x;

	LocalToScreen( x, y );

	surface()->DrawSetColor( GetDropFrameColor() );

	// Draw insertion point
	surface()->DrawFilledRect( x,			y, x + w, y + 2 );
	surface()->DrawFilledRect( x,	y + h - 2, x + w, y + h );
	surface()->DrawFilledRect( x,			y, x + 2, y + h );
	surface()->DrawFilledRect( x + w - 2,	y, x + w, y + h );
}


//-----------------------------------------------------------------------------
//
//
// CDmePresetListPanel
//
// Declaration above because of scoping issues
//
//
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
CDmePresetListPanel::CDmePresetListPanel( vgui::Panel *pParent, const char *pName, CDmePresetGroupEditorPanel *pComboPanel ) :
	BaseClass( pParent, pName ), m_pPresetGroupPanel( pComboPanel )
{
}

void CDmePresetListPanel::OnKeyCodeTyped( vgui::KeyCode code )
{
	CDmePresetGroup *pPresetGroup = m_pPresetGroupPanel->GetSelectedPresetGroup();
	if ( pPresetGroup && !pPresetGroup->m_bIsReadOnly )
	{
		if ( code == KEY_DELETE || code == KEY_BACKSPACE )
		{
			m_pPresetGroupPanel->OnRemovePreset();
			return;
		}

		// Not sure how to handle 'edit' mode... the relevant stuff is private
		if ( vgui::input()->IsKeyDown( KEY_LSHIFT ) || vgui::input()->IsKeyDown( KEY_RSHIFT ) )
		{
			if ( code == KEY_UP )
			{
				m_pPresetGroupPanel->OnMovePresetUp();
				return;
			}

			if ( code == KEY_DOWN )
			{
				m_pPresetGroupPanel->OnMovePresetDown();
				return;
			}
		}
	}	

	vgui::ListPanel::OnKeyCodeTyped( code );
}


//-----------------------------------------------------------------------------
// Called when a list panel's selection changes
//-----------------------------------------------------------------------------
void CDmePresetListPanel::OnCreateDragData( KeyValues *msg )
{
	CDmePresetGroup *pPresetGroup = m_pPresetGroupPanel->GetSelectedPresetGroup();
	if ( pPresetGroup->m_bIsReadOnly )
		return;

	CDmePreset *pPreset = m_pPresetGroupPanel->GetSelectedPreset();
	if ( !pPreset )
		return;

	SetElementKeyValue( msg, "preset", pPreset );
	msg->SetInt( "selfDroppable", 1 );
}


//-----------------------------------------------------------------------------
// Called when a list panel's selection changes
//-----------------------------------------------------------------------------
bool CDmePresetListPanel::IsDroppable( CUtlVector< KeyValues * >& msgList )
{
	if ( msgList.Count() > 0 )
	{
		KeyValues *pData( msgList[ 0 ] );
		if ( pData->GetPtr( "panel", NULL ) == this && m_pPresetGroupPanel )
		{
			CDmePreset *pPreset = GetElementKeyValue< CDmePreset >( pData, "preset" );
			if ( pPreset )
				return true;
		}
	}

	return false;
}

//-----------------------------------------------------------------------------
// Called when a list panel's selection changes
//-----------------------------------------------------------------------------
void CDmePresetListPanel::OnPanelDropped( CUtlVector< KeyValues * >& msgList )
{
	if ( msgList.Count() == 0 )
		return;

	KeyValues *pData = msgList[ 0 ];
	if ( pData->GetPtr( "panel", NULL ) != this || !m_pPresetGroupPanel )
		return;

	// Discover the cell the panel is over
	int x, y, row, column;
	vgui::input()->GetCursorPos( x, y );
	GetCellAtPos( x, y, row, column );

	KeyValues *pKeyValues = GetItem( row );

	CDmePreset *pDragPreset = GetElementKeyValue<CDmePreset>( pData, "preset" );
	if ( pDragPreset )
	{
		CDmePreset *pDropPreset = pKeyValues ? GetElementKeyValue<CDmePreset>( pKeyValues, "preset" ) : NULL;
		m_pPresetGroupPanel->MovePresetInFrontOf( pDragPreset, pDropPreset );
		return;
	}
}


//-----------------------------------------------------------------------------
// Mouse is now over a droppable panel
//-----------------------------------------------------------------------------
void CDmePresetListPanel::OnDroppablePanelPaint( CUtlVector< KeyValues * >& msglist, CUtlVector< Panel * >& dragPanels )
{
	// Discover the cell the panel is over
	int x, y, w, h, row, column;
	vgui::input()->GetCursorPos( x, y );
	GetCellAtPos( x, y, row, column );
	GetCellBounds( row, column, x, y, w, h );
	LocalToScreen( x, y );

	surface()->DrawSetColor( GetDropFrameColor() );

	// Draw insertion point
	surface()->DrawFilledRect( x,			y, x + w, y + 2 );
	surface()->DrawFilledRect( x,	y + h - 2, x + w, y + h );
	surface()->DrawFilledRect( x,			y, x + 2, y + h );
	surface()->DrawFilledRect( x + w - 2,	y, x + w, y + h );
}



//-----------------------------------------------------------------------------
//
// Purpose: Combination system editor frame
//
//-----------------------------------------------------------------------------
CDmePresetGroupEditorFrame::CDmePresetGroupEditorFrame( vgui::Panel *pParent, const char *pTitle ) : 
	BaseClass( pParent, "DmePresetGroupEditorFrame" )
{
	SetDeleteSelfOnClose( true );
	m_pEditor = new CDmePresetGroupEditorPanel( this, "DmePresetGroupEditorPanel" );
	m_pEditor->AddActionSignalTarget( this );
	m_pOkButton = new vgui::Button( this, "OkButton", "#VGui_OK", this, "Ok" );
	SetBlockDragChaining( true );

	LoadControlSettingsAndUserConfig( "resource/dmepresetgroupeditorframe.res" );

	SetTitle( pTitle, false );
	g_pDataModel->InstallNotificationCallback( this );
}

CDmePresetGroupEditorFrame::~CDmePresetGroupEditorFrame()
{
	g_pDataModel->RemoveNotificationCallback( this );
}


//-----------------------------------------------------------------------------
// Sets the current scene + animation list
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorFrame::SetAnimationSet( CDmeAnimationSet *pComboSystem )
{
	m_pEditor->SetAnimationSet( pComboSystem );
}


//-----------------------------------------------------------------------------
// On command
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorFrame::OnCommand( const char *pCommand )
{
	if ( !Q_stricmp( pCommand, "Ok" ) )
	{
		CloseModal();
		return;
	}

	BaseClass::OnCommand( pCommand );
}


//-----------------------------------------------------------------------------
// Inherited from IDmNotify
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorFrame::NotifyDataChanged( const char *pReason, int nNotifySource, int nNotifyFlags )
{
	if ( !IsVisible() )
		return;

	if ( nNotifySource == NOTIFY_SOURCE_PRESET_GROUP_EDITOR )
		return;

	m_pEditor->RefreshAnimationSet();
}


//-----------------------------------------------------------------------------
// Chains notification messages from the contained panel to external clients
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorFrame::OnPresetsChanged()
{
	PostActionSignal( new KeyValues( "PresetsChanged" ) );
}

void CDmePresetGroupEditorFrame::OnAddNewPreset( KeyValues *pKeyValues )
{
	PostActionSignal( pKeyValues->MakeCopy() );
}


//-----------------------------------------------------------------------------
// Various command handlers related to the Edit menu
//-----------------------------------------------------------------------------
void CDmePresetGroupEditorFrame::OnUndo()
{
	if ( g_pDataModel->CanUndo() )
	{
		CDisableUndoScopeGuard guard;
		g_pDataModel->Undo();
	}
}

void CDmePresetGroupEditorFrame::OnRedo()
{
	if ( g_pDataModel->CanRedo() )
	{
		CDisableUndoScopeGuard guard;
		g_pDataModel->Redo();
	}
}