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

#include "vstdlib/random.h"
#include "dme_controls/dmedageditpanel.h"
#include "dme_controls/dmedagrenderpanel.h"
#include "dme_controls/dmepanel.h"
#include "vgui_controls/Button.h"
#include "vgui_controls/Splitter.h"
#include "vgui_controls/PropertySheet.h"
#include "vgui_controls/PropertyPage.h"
#include "vgui_controls/Label.h"
#include "vgui_controls/ScrollBar.h"
#include "vgui_controls/Slider.h"
#include "vgui_controls/ScrollableEditablePanel.h"
#include "vgui_controls/ListPanel.h"
#include "vgui_controls/Image.h"
#include "vgui_controls/TextImage.h"
#include "vgui/ISurface.h"
#include "vgui/ischeme.h"
#include "vgui/iinput.h"
#include "vgui/IVGui.h"
#include "vgui/cursor.h"
#include "movieobjects/dmemakefile.h"
#include "movieobjects/dmemdlmakefile.h"
#include "movieobjects/dmedccmakefile.h"
#include "movieobjects/dmedag.h"
#include "movieobjects/dmeclip.h"
#include "movieobjects/dmeanimationlist.h"
#include "movieobjects/dmecombinationoperator.h"
#include "movieobjects/dmeanimationset.h"

#include "tier1/KeyValues.h"
#include "materialsystem/imesh.h"
#include "dme_controls/BaseAnimationSetEditor.h"
#include "dme_controls/BaseAnimSetAttributeSliderPanel.h"
//-----------------------------------------------------------------------------
//
// Hook into the dme panel editor system
//
//-----------------------------------------------------------------------------
IMPLEMENT_DMEPANEL_FACTORY( CDmeDagEditPanel, DmeDag, "DmeDagPreview2", "DmeDag Previewer 2", false );
IMPLEMENT_DMEPANEL_FACTORY( CDmeDagEditPanel, DmeSourceSkin, "DmeSourceSkinPreview2", "MDL Skin Previewer 2", false );
IMPLEMENT_DMEPANEL_FACTORY( CDmeDagEditPanel, DmeSourceAnimation, "DmeSourceAnimationPreview2", "MDL Animation Previewer 2", false );
IMPLEMENT_DMEPANEL_FACTORY( CDmeDagEditPanel, DmeDCCMakefile, "DmeMakeFileOutputPreview2", "DCC MakeFile Output Previewer 2", false );


//-----------------------------------------------------------------------------
// Slider panel
//-----------------------------------------------------------------------------
class CDmeAnimationListPanel : public vgui::EditablePanel
{
	DECLARE_CLASS_SIMPLE( CDmeAnimationListPanel, vgui::EditablePanel );

public:
	// constructor, destructor
	CDmeAnimationListPanel( vgui::Panel *pParent, const char *pName );
	virtual ~CDmeAnimationListPanel();

	void SetAnimationList( CDmeAnimationList *pList );
	void RefreshAnimationList();
	const char *GetSelectedAnimation() const;

private:
	// Called when the selection changes moves
	MESSAGE_FUNC( OnItemSelected, "ItemSelected" );
	MESSAGE_FUNC( OnItemDeselected, "ItemDeselected" );

	vgui::ListPanel *m_pAnimationList;
	CDmeHandle< CDmeAnimationList > m_hAnimationList;
};


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


//-----------------------------------------------------------------------------
// constructor, destructor
//-----------------------------------------------------------------------------
CDmeAnimationListPanel::CDmeAnimationListPanel( vgui::Panel *pParent, const char *pName ) :
	BaseClass( pParent, pName )
{
	m_pAnimationList = new vgui::ListPanel( this, "AnimationList" );
	m_pAnimationList->AddColumnHeader( 0, "name", "name", 100, 0 );
	m_pAnimationList->AddActionSignalTarget( this );
	m_pAnimationList->SetSortFunc( 0, AnimationNameSortFunc );
	m_pAnimationList->SetSortColumn( 0 );
	m_pAnimationList->SetEmptyListText("No animations");
	m_pAnimationList->SetMultiselectEnabled( false );

	LoadControlSettingsAndUserConfig( "resource/dmeanimationlistpanel.res" );
}

CDmeAnimationListPanel::~CDmeAnimationListPanel()
{
}


//-----------------------------------------------------------------------------
// Sets the combination operator
//-----------------------------------------------------------------------------
void CDmeAnimationListPanel::SetAnimationList( CDmeAnimationList *pList )
{
	if ( pList != m_hAnimationList.Get() )
	{
		m_hAnimationList = pList;
		RefreshAnimationList();
	}
}


//-----------------------------------------------------------------------------
// Builds the list of animations
//-----------------------------------------------------------------------------
void CDmeAnimationListPanel::RefreshAnimationList()
{
	m_pAnimationList->RemoveAll();
	if ( !m_hAnimationList.Get() )
		return;

	int nCount = m_hAnimationList->GetAnimationCount();
	for ( int i = 0; i < nCount; ++i )
	{
		CDmeChannelsClip *pAnimation = m_hAnimationList->GetAnimation( i );
		KeyValues *pItemKeys = new KeyValues( "node", "name", pAnimation->GetName() );
		m_pAnimationList->AddItem( pItemKeys, 0, false, false );
	}

	m_pAnimationList->SortList();
}


//-----------------------------------------------------------------------------
// Returns the selected animation
//-----------------------------------------------------------------------------
const char *CDmeAnimationListPanel::GetSelectedAnimation() const
{
	if ( m_pAnimationList->GetSelectedItemsCount() == 0 )
		return "";

	int nIndex = m_pAnimationList->GetSelectedItem( 0 );
	KeyValues *pItemKeyValues = m_pAnimationList->GetItem( nIndex );
	return pItemKeyValues->GetString( "name" );
}


//-----------------------------------------------------------------------------
// Called when an animation is selected or deselected
//-----------------------------------------------------------------------------
void CDmeAnimationListPanel::OnItemSelected( )
{
	const char *pAnimationName = GetSelectedAnimation();
	PostActionSignal( new KeyValues( "AnimationSelected", "animationName", pAnimationName ) );
}

void CDmeAnimationListPanel::OnItemDeselected( )
{
	PostActionSignal( new KeyValues( "AnimationDeselected" ) );
}

class CDmeCombinationOperatorPanel : public CBaseAnimationSetEditor
{	
	DECLARE_CLASS_SIMPLE( CDmeCombinationOperatorPanel, CBaseAnimationSetEditor );
public:
	CDmeCombinationOperatorPanel( vgui::Panel *parent, const char *panelName );
	virtual ~CDmeCombinationOperatorPanel();

	virtual void OnTick();

	void SetCombinationOperator( CDmeCombinationOperator *pOp );
	void RefreshCombinationOperator();

private:
	void CreateFakeAnimationSet( );
	void DestroyFakeAnimationSet( );

	// Adds new controls to the animation set
	void AddNewAnimationSetControls();

	// Removes a controls from all presets
	void RemoveAnimationControlFromPresets( const char *pControlName );

	// Removes a controls from all presets
	void RemoveAnimationControlFromSelectionGroups( const char *pControlName );

	// Removes controls from the animation set that aren't used
	void RemoveUnusedAnimationSetControls();

	// Modify controls in the presets which have had stereo or multilevel settings changed
	void ModifyExistingAnimationSetControls();

	// Modify controls in the presets which have had stereo or multilevel settings changed
	void ModifyExistingAnimationSetPresets( CDmElement *pControlElement );

	// Modify controls which have had stereo or multilevel settings changed
	void ModifyExistingAnimationSetControl( CDmElement *pControlElement );

	// Creates the procedural presets
	void ComputeProceduralPresets();

	void RefreshAnimationSet();

	// Sort control names to match the combination controls
	void SortAnimationSetControls();

	// Sets preset values
	void GenerateProceduralPresetValues( CDmElement *pPreset, CDmElement *pControl, bool bIdentity, float flForceValue );
	void GenerateProceduralPresetValues( CDmElement *pPreset, const CDmrElementArray< CDmElement > &controls, bool bIdentity, float flForceValue = -1.0f );

	CDmeHandle< CDmeCombinationOperator > m_hCombinationOperator;
};


//-----------------------------------------------------------------------------
// Constructor/destructor
//-----------------------------------------------------------------------------
CDmeCombinationOperatorPanel::CDmeCombinationOperatorPanel( vgui::Panel *parent, const char *panelName ) :
	BaseClass( parent, panelName, false )
{
	CreateFakeAnimationSet();
	vgui::ivgui()->AddTickSignal( GetVPanel(), 0 );
}

CDmeCombinationOperatorPanel::~CDmeCombinationOperatorPanel()
{
	DestroyFakeAnimationSet();
}


//-----------------------------------------------------------------------------
// Create, destroy fake animation sets
//-----------------------------------------------------------------------------
void CDmeCombinationOperatorPanel::CreateFakeAnimationSet( )
{
	m_AnimSet = CreateElement< CDmeAnimationSet >( "fakeSet", DMFILEID_INVALID );
	m_AnimSet->SetValue( "gameModel", DMELEMENT_HANDLE_INVALID );
	g_pDataModel->DontAutoDelete( m_AnimSet->GetHandle() );
}

void CDmeCombinationOperatorPanel::DestroyFakeAnimationSet( )
{
	DestroyElement( m_AnimSet, TD_DEEP );
}


//-----------------------------------------------------------------------------
// Sets combination ops + animation sets
//-----------------------------------------------------------------------------
void CDmeCombinationOperatorPanel::SetCombinationOperator( CDmeCombinationOperator *pOp )
{
	// The Animation Set Editor Doesn't Handle Passing The Same Animation Set With
	// Different Data In It Very Well
	DestroyFakeAnimationSet();
	CreateFakeAnimationSet();

	if ( pOp != m_hCombinationOperator.Get() )
	{
		m_hCombinationOperator = pOp;
		RefreshCombinationOperator();
	}
}


//-----------------------------------------------------------------------------
// Sets preset values
//-----------------------------------------------------------------------------
void CDmeCombinationOperatorPanel::GenerateProceduralPresetValues( CDmElement *pPreset, CDmElement *ctrl, bool bForceValue, float flForceValue )
{
	bool combo = ctrl->GetValue< bool >( "combo" );
	bool multi = ctrl->GetValue< bool >( "multi" );

	float flValue, flBalance, flMultilevel;
	if ( !bForceValue )
	{
		flValue = RandomFloat( 0.0f, 1.0f );
		flBalance = RandomFloat( 0.25f, 0.75f );
		flMultilevel = RandomFloat( 0.0f, 1.0f );
	}
	else
	{
		flValue = flBalance = flMultilevel = flForceValue;
	}

	pPreset->SetValue< float >( "value", flValue );
	if ( combo )
	{
		pPreset->SetValue< float >( "balance", flBalance );
	}
	else
	{
		pPreset->RemoveAttribute( "balance" );
	}

	if ( multi )
	{
		pPreset->SetValue< float >( "multilevel", flMultilevel );
	}
	else
	{
		pPreset->RemoveAttribute( "multilevel" );
	}
}


void CDmeCombinationOperatorPanel::GenerateProceduralPresetValues( CDmElement *pPreset, const CDmrElementArray< CDmElement > &controls, bool bIdentity, float flForceValue /*= -1.0f*/ )
{
	CDmrElementArray<> values( pPreset, "controlValues" );
	Assert( values.IsValid() );
	values.RemoveAll();
	int c = controls.Count();
	for ( int i = 0; i < c ; ++i )
	{
		CDmElement *pControl = controls[ i ];
		CDmElement *pControlValue = CreateElement< CDmElement >( pControl->GetName(), pPreset->GetFileId() );
		GenerateProceduralPresetValues( pControlValue, pControl, bIdentity, flForceValue );
		values.AddToTail( pControlValue );
	}
}


//-----------------------------------------------------------------------------
// Removes a controls from all presets
//-----------------------------------------------------------------------------
void CDmeCombinationOperatorPanel::RemoveAnimationControlFromPresets( const char *pControlName )
{
	CDmeAnimationSet *pAnimationSet = m_AnimSet.Get();
	CDmrElementArray< CDmePresetGroup > presetGroupList = pAnimationSet->GetPresetGroups();

	int nPresetGroupCount = presetGroupList.Count();
	for ( int i = 0; i < nPresetGroupCount; ++i )
	{
		CDmePresetGroup *pPresetGroup = presetGroupList[ i ];

		CDmrElementArray< CDmePreset > presetList = pPresetGroup->GetPresets();
		int nPresetCount = presetList.Count();
		for ( int j = 0; j < nPresetCount; ++j )
		{
			CDmePreset *pPreset = presetList[ j ];
			CDmrElementArray<> controlValues = pPreset->GetControlValues();

			int nControlCount = controlValues.Count();
			for ( int k = 0; k < nControlCount; ++k )
			{
				CDmElement *v = controlValues[ k ];
				if ( !Q_stricmp( v->GetName(), pControlName ) )
				{
					controlValues.FastRemove( k );
					break;
				}
			}
		}
	}
}


//-----------------------------------------------------------------------------
// Removes a controls from all presets
//-----------------------------------------------------------------------------
void CDmeCombinationOperatorPanel::RemoveAnimationControlFromSelectionGroups( const char *pControlName )
{
	CDmeAnimationSet *pAnimationSet = m_AnimSet.Get();
	CDmrElementArray< > selectionGroupList = pAnimationSet->GetSelectionGroups();

	int nGroupCount = selectionGroupList.Count();
	for ( int i = 0; i < nGroupCount; ++i )
	{
		CDmElement *pSelectionGroup = selectionGroupList[ i ];
		CDmrStringArray selectedControls( pSelectionGroup, "selectedControls" );
		int nControlCount = selectedControls.Count();
		for ( int j = 0; j < nControlCount; ++j )
		{
			if ( !Q_stricmp( selectedControls[ j ], pControlName ) )
			{
				selectedControls.FastRemove( j );
				break;
			}
		}
	}
}


//-----------------------------------------------------------------------------
// Removes controls from the animation set that aren't used
//-----------------------------------------------------------------------------
void CDmeCombinationOperatorPanel::RemoveUnusedAnimationSetControls()
{
	CDmeAnimationSet *pAnimationSet = m_AnimSet.Get();
	CDmrElementArray< > controls = pAnimationSet->GetControls();

	// Remove all controls in the animation set and in presets that don't exist in the combination system
	int nAnimSetCount = controls.Count();
	for ( int i = nAnimSetCount; --i >= 0; )
	{
		CDmElement *pControlElement = controls[i];
		if ( !pControlElement )
			continue;

		// Don't do any of this work for transforms
		if ( pControlElement->GetValue< bool >( "transform" ) )
			continue;

		const char *pControlName = pControlElement->GetName();

		// Look for a match
		if ( m_hCombinationOperator.Get() && m_hCombinationOperator->FindControlIndex( pControlName ) >= 0 )
			continue;
	
		// No match, blow the control away.
		RemoveAnimationControlFromPresets( pControlName );
		RemoveAnimationControlFromSelectionGroups( pControlName );
		controls.FastRemove( i );
	}
}


//-----------------------------------------------------------------------------
// Modify controls in the presets which have had stereo or multilevel settings changed
//-----------------------------------------------------------------------------
void CDmeCombinationOperatorPanel::ModifyExistingAnimationSetControl( CDmElement *pControlElement )
{
	const char *pControlName = pControlElement->GetName();

	// Look for a match
	int nControlIndex = m_hCombinationOperator->FindControlIndex( pControlName );
	Assert( nControlIndex >= 0 );

	bool bIsStereoControl = m_hCombinationOperator->IsStereoControl( nControlIndex );
	bool bIsAnimControlStereo = pControlElement->GetValue< bool >( "combo" );
	if ( bIsAnimControlStereo != bIsStereoControl )
	{
		pControlElement->SetValue< bool >( "combo", bIsStereoControl );
		if ( !bIsStereoControl )
		{
			pControlElement->RemoveAttribute( "balance" );
		}
		else
		{
			const Vector2D &value = m_hCombinationOperator->GetStereoControlValue( nControlIndex );
			pControlElement->SetValue< float >( "balance", value.y );
		}
	}

	bool bIsMultiControl = m_hCombinationOperator->IsMultiControl( nControlIndex );
	bool bIsAnimMultiControl = pControlElement->GetValue< bool >( "multi" );
	if ( bIsAnimMultiControl != bIsMultiControl )
	{
		pControlElement->SetValue< bool >( "multi", bIsMultiControl );
		if ( !bIsMultiControl )
		{
			pControlElement->RemoveAttribute( "multilevel" );
		}
		else
		{
			pControlElement->SetValue< float >( "multilevel", m_hCombinationOperator->GetMultiControlLevel( nControlIndex ) );
		}
	}

	float flDefaultValue = m_hCombinationOperator->GetRawControlCount( nControlIndex ) == 2 ? 0.5f : 0.0f;
	pControlElement->SetValue( "defaultValue", flDefaultValue );
	pControlElement->SetValue( "defaultBalance", 0.5f );
	pControlElement->SetValue( "defaultMultilevel", 0.5f );
}


//-----------------------------------------------------------------------------
// Modify controls in the presets which have had stereo or multilevel settings changed
//-----------------------------------------------------------------------------
void CDmeCombinationOperatorPanel::ModifyExistingAnimationSetPresets( CDmElement *pControlElement )
{
	const char *pControlName = pControlElement->GetName();

	CDmeAnimationSet *pAnimationSet = m_AnimSet.Get();
	const CDmrElementArray< CDmePresetGroup > &presetGroupList = pAnimationSet->GetPresetGroups();

	int nPresetGroupCount = presetGroupList.Count();
	for ( int g = 0; g < nPresetGroupCount; ++g )
	{
		CDmePresetGroup *pPresetGroup = presetGroupList[ g ];
		const CDmrElementArray< CDmePreset > &presetList = pPresetGroup->GetPresets();

		int nPresetCount = presetList.Count();
		for ( int i = 0; i < nPresetCount; ++i )
		{
			CDmePreset *pPreset = presetList[ i ];
			const CDmrElementArray< CDmElement > &controlValues = pPreset->GetControlValues( );

			int nControlCount = controlValues.Count();
			for ( int j = 0; j < nControlCount; ++j )
			{
				CDmElement *v = controlValues[ j ];
				if ( Q_stricmp( v->GetName(), pControlName ) )
					continue;

				bool bIsAnimControlStereo = pControlElement->GetValue< bool >( "combo" );
				if ( bIsAnimControlStereo )
				{
					if ( !v->HasAttribute( "balance" ) )
					{
						v->SetValue( "balance", 0.5f );
					}
				}
				else
				{
					v->RemoveAttribute( "balance" );
				}

				bool bIsAnimMultiControl = pControlElement->GetValue< bool >( "multi" );
				if ( bIsAnimMultiControl )
				{
					if ( !v->HasAttribute( "multilevel" ) )
					{
						v->SetValue( "multilevel", 0.5f );
					}
				}
				else
				{
					v->RemoveAttribute( "multilevel" );
				}
			}
		}
	}
}


//-----------------------------------------------------------------------------
// Modify controls which have had stereo or multilevel settings changed
//-----------------------------------------------------------------------------
void CDmeCombinationOperatorPanel::ModifyExistingAnimationSetControls()
{
	CDmeAnimationSet *pAnimationSet = m_AnimSet.Get();
	const CDmrElementArray< CDmElement > &controls = pAnimationSet->GetControls();

	// Update the main controls; update defaults and add or remove combo + multi data
	int nAnimSetCount = controls.Count();
	for ( int i = nAnimSetCount; --i >= 0; )
	{
		CDmElement *pControlElement = controls[i];
		if ( !pControlElement )
			continue;

		// Don't do any of this work for transforms
		if ( pControlElement->GetValue< bool >( "transform" ) )
			continue;

		ModifyExistingAnimationSetControl( pControlElement );
		ModifyExistingAnimationSetPresets( pControlElement );
	}
}


//-----------------------------------------------------------------------------
// Adds new controls to the animation set
//-----------------------------------------------------------------------------
void CDmeCombinationOperatorPanel::AddNewAnimationSetControls()
{
	if ( !m_hCombinationOperator.Get() )
		return;

	CDmeAnimationSet *pAnimationSet = m_AnimSet.Get();
	CDmaElementArray< > &controls = pAnimationSet->GetControls();

	// Remove all controls in the animation set and in presets that don't exist in the combination system
	int nFirstControl = controls.Count();
	int nCombinationControlCount = m_hCombinationOperator->GetControlCount();
	for ( int i = 0; i < nCombinationControlCount; ++i )
	{
		const char *pControlName = m_hCombinationOperator->GetControlName( i );
		if ( pAnimationSet->FindControl( pControlName ) )
			continue;

		bool bIsStereoControl = m_hCombinationOperator->IsStereoControl(i);
		bool bIsMultiControl = m_hCombinationOperator->IsMultiControl(i);
		float flDefaultValue = m_hCombinationOperator->GetRawControlCount(i) == 2 ? 0.5f : 0.0f;

		// Add the control to the controls group
		CDmElement *pControl = CreateElement< CDmElement >( pControlName, pAnimationSet->GetFileId() );
		Assert( pControl );
		controls.InsertBefore( i, pControl );

		pControl->SetValue( "combo", bIsStereoControl );
		pControl->SetValue( "multi", bIsMultiControl );

		if ( bIsStereoControl )
		{ 
			const Vector2D &value = m_hCombinationOperator->GetStereoControlValue(i);

			pControl->SetValue( "value", value.x );
			pControl->SetValue( "balance", value.y );
		}
		else
		{
			pControl->SetValue( "value", m_hCombinationOperator->GetControlValue(i) );
		}

		if ( bIsMultiControl )
		{
			pControl->SetValue( "multilevel", m_hCombinationOperator->GetMultiControlLevel(i) );
		}

		pControl->SetValue( "defaultValue", flDefaultValue );
		pControl->SetValue( "defaultBalance", 0.5f );
		pControl->SetValue( "defaultMultilevel", 0.5f );
	}

	int nLastControl = controls.Count();
	if ( nLastControl == nFirstControl )
		return;

	// Add new controls to the root group
	CDmElement *pGroup = pAnimationSet->FindOrAddSelectionGroup( "Root" );

	// Fill in members
	CDmrStringArray groups( pGroup, "selectedControls" );
	for ( int i = nFirstControl; i < nLastControl; ++i )
	{
		groups.AddToTail( controls[ i ]->GetName() );
	}
}


//-----------------------------------------------------------------------------
// Creates the procedural presets
//-----------------------------------------------------------------------------
void CDmeCombinationOperatorPanel::ComputeProceduralPresets()
{
	CDmeAnimationSet *pAnimationSet = m_AnimSet.Get();

	// Now create some presets
	CDmePresetGroup* pPresetGroup = pAnimationSet->FindOrAddPresetGroup( "procedural" );
	pPresetGroup->m_bIsReadOnly = true;

	// NOTE: Default needs no values set into it since it's the default
	CDmElement *pPreset = pPresetGroup->FindOrAddPreset( "Default" );

	pPreset = pPresetGroup->FindOrAddPreset( "Zero" );
	GenerateProceduralPresetValues( pPreset, pAnimationSet->GetControls(), true, 0.0f );

	pPreset = pPresetGroup->FindOrAddPreset( "Half" );
	GenerateProceduralPresetValues( pPreset, pAnimationSet->GetControls(), true, 0.5f );

	pPreset = pPresetGroup->FindOrAddPreset( "One" );
	GenerateProceduralPresetValues( pPreset, pAnimationSet->GetControls(), true, 1.0f );

	pPreset = pPresetGroup->FindOrAddPreset( "Random" );
	GenerateProceduralPresetValues( pPreset, pAnimationSet->GetControls(), false );

	pAnimationSet->EnsureProceduralPresets();
}


//-----------------------------------------------------------------------------
// Sort control names to match the combination controls
//-----------------------------------------------------------------------------
void CDmeCombinationOperatorPanel::SortAnimationSetControls()
{
	CDmaElementArray<> &controls = m_AnimSet->GetControls();
	int nControlCount = controls.Count();
	if ( nControlCount == 0 )
		return;

	int nCombinationControlCount = m_hCombinationOperator->GetControlCount();
	Assert( nControlCount == nCombinationControlCount );

	DmElementHandle_t *pElements = (DmElementHandle_t*)_alloca( nControlCount * sizeof(DmElementHandle_t) );
	for ( int i = 0; i < nCombinationControlCount; ++i )
	{
		const char *pControlName = m_hCombinationOperator->GetControlName( i );
		CDmElement *pControl = m_AnimSet->FindControl( pControlName );
		pElements[i] = pControl->GetHandle();
	}

	controls.SetMultiple( 0, nControlCount, pElements );

#ifdef _DEBUG
	for ( int i = 0; i < nCombinationControlCount; ++i )
	{
		const char *pControlName = controls[i]->GetName();
		const char *pComboName = m_hCombinationOperator->GetControlName( i );
		Assert( !Q_stricmp( pControlName, pComboName ) );
	}
#endif
}


//-----------------------------------------------------------------------------
// Called when the animation set has to be made to match the combination operator
//-----------------------------------------------------------------------------
void CDmeCombinationOperatorPanel::RefreshAnimationSet()
{
	if ( !m_AnimSet.Get() )
		return;

	CDisableUndoScopeGuard sg;

	// Remove all controls in the animation set and in presets that don't exist in the combination system
	RemoveUnusedAnimationSetControls();

	// Modify controls in the presets which have had stereo or multilevel settings changed
	ModifyExistingAnimationSetControls();
	
	// Add all controls not in the animation set but which do exist in the combination system
	AddNewAnimationSetControls();

	// Resets the procedural presets
	ComputeProceduralPresets();

	// Sort control names to match the combination controls
	SortAnimationSetControls();

	// Set that as the current set
	BaseClass::ChangeAnimationSet( m_AnimSet );
}

void CDmeCombinationOperatorPanel::RefreshCombinationOperator()
{
	RefreshAnimationSet();
}


//-----------------------------------------------------------------------------
// Simulate
//-----------------------------------------------------------------------------
void CDmeCombinationOperatorPanel::OnTick()
{
	BaseClass::OnTick();

	if ( m_hCombinationOperator )
	{
		{
			CDisableUndoScopeGuard sg;

			int c = m_hCombinationOperator->GetControlCount();
			for ( int i = 0; i < c; ++i )
			{
				AttributeValue_t value;

				bool bVisible = GetAttributeSlider()->GetSliderValues( &value, i );
				if ( !bVisible )
					continue;

				if ( m_hCombinationOperator->IsStereoControl( i ) )
				{
					m_hCombinationOperator->SetControlValue( i, value.m_pValue[ANIM_CONTROL_VALUE], value.m_pValue[ANIM_CONTROL_BALANCE] );
				}
				else
				{
					m_hCombinationOperator->SetControlValue( i, value.m_pValue[ANIM_CONTROL_VALUE] );
				}
				if ( m_hCombinationOperator->IsMultiControl( i ) )
				{
					m_hCombinationOperator->SetMultiControlLevel( i, value.m_pValue[ANIM_CONTROL_MULTILEVEL] ); 
				}
			}
		}
    
		// FIXME: Shouldn't this happen at the application level?
		// run the machinery - apply, resolve, dependencies, operate, resolve
		CUtlVector< IDmeOperator* > operators;
		operators.AddToTail( m_hCombinationOperator );

		CDisableUndoScopeGuard guard;
		g_pDmElementFramework->SetOperators( operators );
		g_pDmElementFramework->Operate( true );
	}

	// allow elements and attributes to be edited again
	g_pDmElementFramework->BeginEdit();
}

//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
CDmeDagEditPanel::CDmeDagEditPanel( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName )
{
	m_pPropertiesSplitter = new vgui::Splitter( this, "PropertiesSplitter", vgui::SPLITTER_MODE_VERTICAL, 1 );
	vgui::Panel *pSplitterLeftSide = m_pPropertiesSplitter->GetChild( 0 );
	vgui::Panel *pSplitterRightSide = m_pPropertiesSplitter->GetChild( 1 );

	m_pDagRenderPanel = new CDmeDagRenderPanel( pSplitterRightSide, "DagRenderPanel" );

	m_pEditorSheet = new vgui::PropertySheet( pSplitterLeftSide, "EditorSheet" );
	m_pEditorSheet->AddActionSignalTarget( this );

	m_pAnimationPage = new vgui::PropertyPage( m_pEditorSheet, "AnimationPage" );
	m_pCombinationPage = new vgui::PropertyPage( m_pEditorSheet, "AnimationSetEditor" );
	m_pVertexAnimationPage = new vgui::PropertyPage( m_pEditorSheet, "VertexAnimationPage" );

	m_pCombinationPanel = new CDmeCombinationOperatorPanel( m_pCombinationPage, "AnimationSetEditorPanel" );
	m_pCombinationPanel->CreateToolsSubPanels();

	m_pAnimationListPanel = new CDmeAnimationListPanel( m_pAnimationPage, "AnimationListPanel" );
	m_pAnimationListPanel->AddActionSignalTarget( this );
	m_pVertexAnimationListPanel = new CDmeAnimationListPanel( m_pVertexAnimationPage, "VertexAnimationListPanel" );
	m_pVertexAnimationListPanel->AddActionSignalTarget( this );

	m_pCombinationPage->LoadControlSettingsAndUserConfig( "resource/dmedageditpanel_animationseteditorpage.res" );
	m_pAnimationPage->LoadControlSettingsAndUserConfig( "resource/dmedageditpanel_animationpage.res" );
	m_pVertexAnimationPage->LoadControlSettingsAndUserConfig( "resource/dmedageditpanel_vertexanimationpage.res" );

	// Load layout settings; has to happen before pinning occurs in code
	LoadControlSettingsAndUserConfig( "resource/dmedageditpanel.res" );

	// NOTE: Page adding happens *after* LoadControlSettingsAndUserConfig
	// because the layout of the sheet is correct at this point.
	m_pEditorSheet->AddPage( m_pAnimationPage, "Animation" );
	m_pEditorSheet->AddPage( m_pCombinationPage, "Combination" );
	m_pEditorSheet->AddPage( m_pVertexAnimationPage, "Vertex Animation" );

}

CDmeDagEditPanel::~CDmeDagEditPanel()
{
}


//-----------------------------------------------------------------------------
// Set the scene

//-----------------------------------------------------------------------------
void CDmeDagEditPanel::SetDmeElement( CDmeDag *pScene )
{
	m_pDagRenderPanel->SetDmeElement( pScene );
}


//-----------------------------------------------------------------------------
// Sets up the various panels in the dag editor
//-----------------------------------------------------------------------------
void CDmeDagEditPanel::SetMakefileRootElement( CDmElement *pRoot )
{
	CDmeAnimationList *pAnimationList = pRoot->GetValueElement< CDmeAnimationList >( "animationList" );
	SetAnimationList( pAnimationList );

	CDmeAnimationList *pVertexAnimationList = pRoot->GetValueElement< CDmeAnimationList >( "vertexAnimationList" );
	SetVertexAnimationList( pVertexAnimationList );

	CDmeCombinationOperator *pComboOp = pRoot->GetValueElement< CDmeCombinationOperator >( "combinationOperator" );
	SetCombinationOperator( pComboOp );
}


//-----------------------------------------------------------------------------
// Other methods which hook into DmePanel
//-----------------------------------------------------------------------------
void CDmeDagEditPanel::SetDmeElement( CDmeSourceSkin *pSkin )
{
	m_pDagRenderPanel->SetDmeElement( pSkin );

	// First, try to grab the dependent makefile
	CDmeMakefile *pSourceMakefile = pSkin->GetDependentMakefile();
	if ( !pSourceMakefile )
		return;

	// Next, try to grab the output of that makefile
	CDmElement *pOutput = pSourceMakefile->GetOutputElement( true );
	if ( !pOutput )
		return;

	SetMakefileRootElement( pOutput );
}

void CDmeDagEditPanel::SetDmeElement( CDmeSourceAnimation *pAnimation )
{
	m_pDagRenderPanel->SetDmeElement( pAnimation );

	// First, try to grab the dependent makefile
	CDmeMakefile *pSourceMakefile = pAnimation->GetDependentMakefile();
	if ( !pSourceMakefile )
		return;

	// Next, try to grab the output of that makefile
	CDmElement *pOutput = pSourceMakefile->GetOutputElement( true );
	if ( !pOutput )
		return;

	SetMakefileRootElement( pOutput );

//	if ( pAnimationList->FindAnimation( pAnimation->m_SourceAnimationName ) < 0 )
//		return;
}

void CDmeDagEditPanel::SetDmeElement( CDmeDCCMakefile *pDCCMakefile )
{
	m_pDagRenderPanel->SetDmeElement( pDCCMakefile );

	// First, try to grab the dependent makefile
	CDmElement *pOutput = pDCCMakefile->GetOutputElement( true );
	if ( !pOutput )
		return;

	SetMakefileRootElement( pOutput );
}

CDmeDag *CDmeDagEditPanel::GetDmeElement()
{
	return m_pDagRenderPanel->GetDmeElement();
}


//-----------------------------------------------------------------------------
// paint it!
//-----------------------------------------------------------------------------
void CDmeDagEditPanel::Paint()
{
	BaseClass::Paint();
}


//-----------------------------------------------------------------------------
// Sets animation
//-----------------------------------------------------------------------------
void CDmeDagEditPanel::SetAnimationList( CDmeAnimationList *pAnimationList )
{
	m_pDagRenderPanel->SetAnimationList( pAnimationList );
	m_pDagRenderPanel->SelectAnimation( "" );
	m_pAnimationListPanel->SetAnimationList( pAnimationList );
}


void CDmeDagEditPanel::SetVertexAnimationList( CDmeAnimationList *pAnimationList )
{
	m_pDagRenderPanel->SetVertexAnimationList( pAnimationList );
	m_pDagRenderPanel->SelectVertexAnimation( "" );
	m_pVertexAnimationListPanel->SetAnimationList( pAnimationList );
}

void CDmeDagEditPanel::SetCombinationOperator( CDmeCombinationOperator *pComboOp )
{
	m_pCombinationPanel->SetCombinationOperator( pComboOp );
}

void CDmeDagEditPanel::RefreshCombinationOperator()
{
	m_pCombinationPanel->RefreshCombinationOperator();
}


//-----------------------------------------------------------------------------
// Called when the page changes
//-----------------------------------------------------------------------------
void CDmeDagEditPanel::OnPageChanged( )
{
	if ( m_pEditorSheet->GetActivePage() == m_pCombinationPage )
	{
		m_pDagRenderPanel->SelectAnimation( "" );
		m_pDagRenderPanel->SelectVertexAnimation( "" );
		return;
	}

	if ( m_pEditorSheet->GetActivePage() == m_pAnimationPage )
	{
		m_pDagRenderPanel->SelectAnimation( m_pAnimationListPanel->GetSelectedAnimation() );
		m_pDagRenderPanel->SelectVertexAnimation( "" );
		return;
	}

	if ( m_pEditorSheet->GetActivePage() == m_pVertexAnimationPage )
	{
		m_pDagRenderPanel->SelectAnimation( "" );
		m_pDagRenderPanel->SelectVertexAnimation( m_pVertexAnimationListPanel->GetSelectedAnimation() );
		return;
	}
}


//-----------------------------------------------------------------------------
// Called when new animations are selected
//-----------------------------------------------------------------------------
void CDmeDagEditPanel::OnAnimationSelected( KeyValues *pKeyValues )
{
	vgui::Panel *pPanel = (vgui::Panel *)pKeyValues->GetPtr( "panel", NULL );
	const char *pAnimationName = pKeyValues->GetString( "animationName", "" );

	if ( pPanel == m_pAnimationListPanel )
	{
		m_pDagRenderPanel->SelectAnimation( pAnimationName );
		return;
	}

	if ( pPanel == m_pVertexAnimationListPanel )
	{
		m_pDagRenderPanel->SelectVertexAnimation( pAnimationName );
		return;
	}
}


void CDmeDagEditPanel::OnAnimationDeselected( KeyValues *pKeyValues )
{
	vgui::Panel *pPanel = (vgui::Panel *)pKeyValues->GetPtr( "panel", NULL );
	if ( pPanel == m_pAnimationListPanel )
	{
		m_pDagRenderPanel->SelectAnimation( "" );
		return;
	}

	if ( pPanel == m_pVertexAnimationListPanel )
	{
		m_pDagRenderPanel->SelectVertexAnimation( "" );
		return;
	}
}