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

#include "DmElementFramework.h"
#include "datamodel.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"


//-----------------------------------------------------------------------------
// Singleton instance
//-----------------------------------------------------------------------------
static CDmElementFramework g_DmElementFramework;
CDmElementFramework *g_pDmElementFrameworkImp = &g_DmElementFramework;
IDmElementFramework *g_pDmElementFramework = &g_DmElementFramework;


//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CDmElementFramework::CDmElementFramework() : m_phase( PH_EDIT ), m_dirtyElements( 128, 256 ) 
{
}

	
//-----------------------------------------------------------------------------
// Methods of IAppSystem
//-----------------------------------------------------------------------------
bool CDmElementFramework::Connect( CreateInterfaceFn factory )
{
	return true;
}

void CDmElementFramework::Disconnect()
{
}

void *CDmElementFramework::QueryInterface( const char *pInterfaceName )
{
	if ( !V_strcmp( pInterfaceName, VDMELEMENTFRAMEWORK_VERSION ) )
		return (IDmElementFramework*)this;

	return NULL;
}

InitReturnVal_t CDmElementFramework::Init( )
{
	return INIT_OK;
}

void CDmElementFramework::Shutdown()
{
	m_dependencyGraph.Cleanup();
}


//-----------------------------------------------------------------------------
// element framework phase transition methods
//-----------------------------------------------------------------------------
void CDmElementFramework::EditApply()
{
	g_pDataModelImp->RemoveUnreferencedElements();
}

void CDmElementFramework::Resolve( bool clearDirtyFlags )
{
	int nCount = m_dirtyElements.Count();
	for ( int ei = 0; ei < nCount; ++ei )
	{
		DmElementHandle_t h = m_dirtyElements[ ei ];
		CDmElement *pElement = g_pDataModel->GetElement( h );
		if ( !pElement )
			continue;

		pElement->Resolve();

		if ( clearDirtyFlags )
		{
			CDmeElementAccessor::MarkDirty( pElement, false ); // marks element clean
			CDmeElementAccessor::MarkAttributesClean( pElement ); // marks all attributes clean
		}
	}

	if ( clearDirtyFlags )
	{
		m_dirtyElements.RemoveAll();
	}
}


//-----------------------------------------------------------------------------
// Returns the current phase
//-----------------------------------------------------------------------------
DmPhase_t CDmElementFramework::GetPhase()
{
	return FastGetPhase();
}

void CDmElementFramework::SetOperators( const CUtlVector< IDmeOperator* > &operators )
{
	VPROF( "CDmElementFramework::SetOperators()" );
	m_dependencyGraph.Reset( operators );
}

void CDmElementFramework::BeginEdit()
{
	Assert( m_phase == PH_EDIT || m_phase == PH_OUTPUT );

	if ( m_phase == PH_EDIT )
	{
		m_phase = PH_EDIT_APPLY;
		EditApply();

		m_phase = PH_EDIT_RESOLVE;
		Resolve( false );
	}

	m_phase = PH_EDIT;
}

void CDmElementFramework::Operate( bool bResolve )
{
	VPROF( "CDmElementFramework::Operate" );

	Assert( m_phase == PH_EDIT || m_phase == PH_OUTPUT );

	if ( m_phase == PH_EDIT )
	{
		{
			VPROF( "CDmElementFramework::PH_EDIT_APPLY" );
			m_phase = PH_EDIT_APPLY;
			EditApply();
		}

		{
			VPROF( "CDmElementFramework::PH_EDIT_RESOLVE" );
			m_phase = PH_EDIT_RESOLVE;
			Resolve( false );
		}
	}

	{
		VPROF( "CDmElementFramework::PH_DEPENDENCY" );
		m_phase = PH_DEPENDENCY;
		bool cycle = m_dependencyGraph.CullAndSortOperators();
		if ( cycle )
		{
			Warning( "Operator cycle found during dependency graph traversal!\n" );
		}
	}

	{
		VPROF( "CDmElementFramework::PH_OPERATE" );
		m_phase = PH_OPERATE;
		const CUtlVector< IDmeOperator* > &operatorsToRun = m_dependencyGraph.GetSortedOperators();
		uint on = operatorsToRun.Count();
		for ( uint oi = 0; oi < on; ++oi )
		{
			operatorsToRun[ oi ]->Operate();
		}
	}

	if ( bResolve )
	{
		VPROF( "CDmElementFramework::PH_OPERATE_RESOLVE" );
		m_phase = PH_OPERATE_RESOLVE;
		Resolve( true );

		m_phase = PH_OUTPUT;
	}
}

void CDmElementFramework::Resolve()
{
	VPROF( "CDmElementFramework::Resolve" );

	Assert( m_phase == PH_OPERATE );

	m_phase = PH_OPERATE_RESOLVE;
	Resolve( true );

	m_phase = PH_OUTPUT;
}

void CDmElementFramework::AddElementToDirtyList( DmElementHandle_t hElement )
{
	m_dirtyElements.AddToTail( hElement );
}

void CDmElementFramework::RemoveCleanElementsFromDirtyList()
{
	int nCount = m_dirtyElements.Count();
	while ( --nCount >= 0 )
	{
		DmElementHandle_t h = m_dirtyElements[ nCount ];
		CDmElement *pElement = g_pDataModel->GetElement( h );
		if ( !pElement )
			continue;

		if ( !CDmeElementAccessor::IsDirty( pElement ) )
		{
			m_dirtyElements.FastRemove( nCount );
		}
	}
}