//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Material Modify control entity.
//
//=============================================================================//

#include "cbase.h"
#include "proxyentity.h"
#include "materialsystem/imaterial.h"
#include "materialsystem/imaterialvar.h"
#include "materialsystem/itexture.h"
#include "iviewrender.h"
#include "texture_group_names.h"
#include "baseanimatedtextureproxy.h"
#include "toolframework_client.h"

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

#define MATERIAL_MODIFY_STRING_SIZE			255
#define MATERIAL_MODIFY_ANIMATION_UNSET		-1

// Must match MaterialModifyControl.cpp
enum MaterialModifyMode_t
{
	MATERIAL_MODIFY_MODE_NONE = 0,
	MATERIAL_MODIFY_MODE_SETVAR = 1,
	MATERIAL_MODIFY_MODE_ANIM_SEQUENCE = 2,
	MATERIAL_MODIFY_MODE_FLOAT_LERP = 3,
};

// forward declarations
void ToolFramework_RecordMaterialParams( IMaterial *pMaterial );

ConVar debug_materialmodifycontrol_client( "debug_materialmodifycontrol_client", "0" );

struct materialanimcommands_t
{
	int iFrameStart;
	int iFrameEnd;
	bool bWrap;
	float flFrameRate;
};

struct materialfloatlerpcommands_t
{
	int flStartValue;
	int flEndValue;
	float flTransitionTime;
};

//------------------------------------------------------------------------------
// FIXME: This really should inherit from something	more lightweight
//------------------------------------------------------------------------------

class C_MaterialModifyControl : public C_BaseEntity
{
public:

	DECLARE_CLASS( C_MaterialModifyControl, C_BaseEntity );

	C_MaterialModifyControl();

	void OnPreDataChanged( DataUpdateType_t updateType );
	void OnDataChanged( DataUpdateType_t updateType );
	bool ShouldDraw();

	IMaterial *GetMaterial( void )					{ return m_pMaterial; }
	const char *GetMaterialVariableName( void )		{ return m_szMaterialVar; }
	const char *GetMaterialVariableValue( void )	{ return m_szMaterialVarValue; }

	DECLARE_CLIENTCLASS();

	// Animated texture and Float Lerp usage
	bool	HasNewAnimationCommands( void )			{ return m_bHasNewAnimationCommands; }
	void	ClearAnimationCommands( void )			{ m_bHasNewAnimationCommands = false; }

	// Animated texture usage
	void	GetAnimationCommands( materialanimcommands_t *pCommands );

	// FloatLerp usage
	void	GetFloatLerpCommands( materialfloatlerpcommands_t *pCommands );

	void	SetAnimationStartTime( float flTime )
	{
		m_flAnimationStartTime = flTime;
	}
	float	GetAnimationStartTime( void ) const
	{
		return m_flAnimationStartTime;
	}

	MaterialModifyMode_t GetModifyMode( void ) const
	{
		return ( MaterialModifyMode_t)m_nModifyMode;
	}
private:

	char m_szMaterialName[MATERIAL_MODIFY_STRING_SIZE];
	char m_szMaterialVar[MATERIAL_MODIFY_STRING_SIZE];
	char m_szMaterialVarValue[MATERIAL_MODIFY_STRING_SIZE];
	IMaterial		*m_pMaterial;

	bool	m_bHasNewAnimationCommands;

	// Animation commands from the server
	int		m_iFrameStart;
	int		m_iFrameEnd;
	bool	m_bWrap;
	float	m_flFramerate;
	bool	m_bNewAnimCommandsSemaphore;
	bool	m_bOldAnimCommandsSemaphore;

	// Float lerp commands from the server
	float	m_flFloatLerpStartValue;
	float	m_flFloatLerpEndValue;
	float	m_flFloatLerpTransitionTime;
	bool	m_bFloatLerpWrap;
	float	m_flAnimationStartTime;

	int		m_nModifyMode;
};

IMPLEMENT_CLIENTCLASS_DT(C_MaterialModifyControl, DT_MaterialModifyControl, CMaterialModifyControl)
	RecvPropString( RECVINFO( m_szMaterialName ) ),
	RecvPropString( RECVINFO( m_szMaterialVar ) ),
	RecvPropString( RECVINFO( m_szMaterialVarValue ) ),
	RecvPropInt( RECVINFO(m_iFrameStart) ),
	RecvPropInt( RECVINFO(m_iFrameEnd) ),
	RecvPropInt( RECVINFO(m_bWrap) ),
	RecvPropFloat( RECVINFO(m_flFramerate) ),
	RecvPropInt( RECVINFO(m_bNewAnimCommandsSemaphore) ),
	RecvPropFloat( RECVINFO(m_flFloatLerpStartValue) ),
	RecvPropFloat( RECVINFO(m_flFloatLerpEndValue) ),
	RecvPropFloat( RECVINFO(m_flFloatLerpTransitionTime) ),
	RecvPropInt( RECVINFO(m_bFloatLerpWrap) ),
	RecvPropInt( RECVINFO(m_nModifyMode) ),
END_RECV_TABLE()

//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
C_MaterialModifyControl::C_MaterialModifyControl()
{
	m_pMaterial = NULL;
	m_bOldAnimCommandsSemaphore = false;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void C_MaterialModifyControl::OnPreDataChanged( DataUpdateType_t updateType )
{
	BaseClass::OnPreDataChanged( updateType );

	m_bOldAnimCommandsSemaphore = m_bNewAnimCommandsSemaphore;
}

//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
void C_MaterialModifyControl::OnDataChanged( DataUpdateType_t updateType )
{
	if( updateType == DATA_UPDATE_CREATED )
	{
		m_pMaterial = materials->FindMaterial( m_szMaterialName, TEXTURE_GROUP_OTHER );

		// Clear out our variables
		m_bHasNewAnimationCommands = true;
	}

	// Detect changes in the anim commands
	if ( m_bNewAnimCommandsSemaphore != m_bOldAnimCommandsSemaphore )
	{
		m_bHasNewAnimationCommands = true;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void C_MaterialModifyControl::GetAnimationCommands( materialanimcommands_t *pCommands )
{
	pCommands->iFrameStart = m_iFrameStart;
	pCommands->iFrameEnd = m_iFrameEnd;
	pCommands->bWrap = m_bWrap;
	pCommands->flFrameRate = m_flFramerate;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void C_MaterialModifyControl::GetFloatLerpCommands( materialfloatlerpcommands_t *pCommands )
{
	pCommands->flStartValue = m_flFloatLerpStartValue;
	pCommands->flEndValue = m_flFloatLerpEndValue;
	pCommands->flTransitionTime = m_flFloatLerpTransitionTime;
}

//------------------------------------------------------------------------------
// Purpose: We don't draw.
//------------------------------------------------------------------------------
bool C_MaterialModifyControl::ShouldDraw()
{
	return false;
}

//=============================================================================
//
// THE MATERIALMODIFYPROXY ITSELF
//
class CMaterialModifyProxy : public CBaseAnimatedTextureProxy
{
public:
	CMaterialModifyProxy();
	virtual ~CMaterialModifyProxy();
	virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
	virtual void OnBind( void *pEntity );
	virtual IMaterial *GetMaterial();

private:
	void OnBindSetVar( C_MaterialModifyControl *pControl );
	void OnBindAnimatedTexture( C_MaterialModifyControl *pControl );
	void OnBindFloatLerp( C_MaterialModifyControl *pControl );
	float GetAnimationStartTime( void* pArg );
	void AnimationWrapped( void* pArg );

	IMaterial	*m_pMaterial;
	
	// texture animation stuff
	int			m_iFrameStart;
	int			m_iFrameEnd;
	bool		m_bReachedEnd;
	bool		m_bCustomWrap;
	float		m_flCustomFramerate;

	// float lerp stuff
	IMaterialVar *m_pMaterialVar;
	int			m_flStartValue;
	int			m_flEndValue;
	float		m_flStartTime;
	float		m_flTransitionTime;
};

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CMaterialModifyProxy::CMaterialModifyProxy()
{
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CMaterialModifyProxy::~CMaterialModifyProxy()
{
}

bool CMaterialModifyProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
{
	// set var stuff
	m_pMaterial = pMaterial;

	// float lerp stuff
	m_flStartValue = MATERIAL_MODIFY_ANIMATION_UNSET;
	m_flEndValue = MATERIAL_MODIFY_ANIMATION_UNSET;

	// animated stuff
//	m_pMaterial = pMaterial;
//	m_iFrameStart = MATERIAL_MODIFY_ANIMATION_UNSET;
//	m_iFrameEnd = MATERIAL_MODIFY_ANIMATION_UNSET;
//	m_bReachedEnd = false;
//	return CBaseAnimatedTextureProxy::Init( pMaterial, pKeyValues );

	return true;
}

void CMaterialModifyProxy::OnBind( void *pEntity )
{
	// Get the modified material vars from the entity input
	IClientRenderable *pRend = (IClientRenderable *)pEntity;
	if ( pRend )
	{
		C_BaseEntity *pBaseEntity = pRend->GetIClientUnknown()->GetBaseEntity();
		
		if ( pBaseEntity )
		{
			if( debug_materialmodifycontrol_client.GetBool() )
			{
//				DevMsg( 1, "%s\n", pBaseEntity->GetDebugName() );
			}
			int numChildren = 0;
			bool gotOne = false;
			for ( C_BaseEntity *pChild = pBaseEntity->FirstMoveChild(); pChild; pChild = pChild->NextMovePeer() )
			{
				numChildren++;
				C_MaterialModifyControl *pControl = dynamic_cast<C_MaterialModifyControl*>( pChild );
				if ( !pControl )
					continue;

				if( debug_materialmodifycontrol_client.GetBool() )
				{
//					DevMsg( 1, "pControl: 0x%p\n", pControl );
				}
				
				switch( pControl->GetModifyMode() )
				{
				case MATERIAL_MODIFY_MODE_NONE:
					break;
				case MATERIAL_MODIFY_MODE_SETVAR:
					gotOne = true;
					OnBindSetVar( pControl );
					break;
				case MATERIAL_MODIFY_MODE_ANIM_SEQUENCE:
					OnBindAnimatedTexture( pControl );
					break;
				case MATERIAL_MODIFY_MODE_FLOAT_LERP:
					OnBindFloatLerp( pControl );
					break;
				default:
					Assert( 0 );
					break;
				}
			}
			if( gotOne )
			{
//				DevMsg( 1, "numChildren: %d\n", numChildren );
			}
		}
	}

	if ( ToolsEnabled() )
	{
		ToolFramework_RecordMaterialParams( GetMaterial() );
	}
}

IMaterial *CMaterialModifyProxy::GetMaterial()
{
	return m_pMaterial;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMaterialModifyProxy::OnBindSetVar( C_MaterialModifyControl *pControl )
{
	IMaterial *pMaterial = pControl->GetMaterial();
	if( !pMaterial )
	{
		Assert( 0 );
		return;
	}

	if ( pMaterial != m_pMaterial )
	{
//		Warning( "\t%s!=%s\n", pMaterial->GetName(), m_pMaterial->GetName() );
		return;
	}

	bool bFound;
	IMaterialVar *pMaterialVar = pMaterial->FindVar( pControl->GetMaterialVariableName(), &bFound, false );
	if ( !bFound )
		return;

	if( Q_strcmp( pControl->GetMaterialVariableValue(), "" ) )
	{
//		const char *pMaterialName = m_pMaterial->GetName();
//		const char *pMaterialVarName = pMaterialVar->GetName();
//		const char *pMaterialVarValue = pControl->GetMaterialVariableValue();
//		if( debug_materialmodifycontrol_client.GetBool() 
//			&& Q_stristr( m_pMaterial->GetName(), "faceandhair" )
//			&& Q_stristr( pMaterialVar->GetName(), "self" )
//			)
//		{
//			static int count = 0;
//			DevMsg( 1, "CMaterialModifyProxy::OnBindSetVar \"%s\" %s=%s %d pControl=0x%p\n", 
//				m_pMaterial->GetName(), pMaterialVar->GetName(), pControl->GetMaterialVariableValue(), count++, pControl );
//		}
		pMaterialVar->SetValueAutodetectType( pControl->GetMaterialVariableValue() );
	}
}


//-----------------------------------------------------------------------------
// Does the dirty deed
//-----------------------------------------------------------------------------
void CMaterialModifyProxy::OnBindAnimatedTexture( C_MaterialModifyControl *pControl )
{
	assert ( m_AnimatedTextureVar );
	if( m_AnimatedTextureVar->GetType() != MATERIAL_VAR_TYPE_TEXTURE )
		return;

	ITexture *pTexture;
	pTexture = m_AnimatedTextureVar->GetTextureValue();

	if ( !pControl )
		return;

	if ( pControl->HasNewAnimationCommands() )
	{
		// Read the data from the modify entity
		materialanimcommands_t sCommands;
		pControl->GetAnimationCommands( &sCommands );

		m_iFrameStart = sCommands.iFrameStart;
		m_iFrameEnd = sCommands.iFrameEnd;
		m_bCustomWrap = sCommands.bWrap;
		m_flCustomFramerate = sCommands.flFrameRate;
		m_bReachedEnd = false;

		m_flStartTime = gpGlobals->curtime;

		pControl->ClearAnimationCommands();
	}

	// Init all the vars based on whether we're using the base material settings, 
	// or the custom ones from the entity input.
	int numFrames;
	bool bWrapAnimation;
	float flFrameRate;
	int iLastFrame;

	// Do we have a custom frame section from the server?
	if ( m_iFrameStart != MATERIAL_MODIFY_ANIMATION_UNSET )
	{
		if ( m_iFrameEnd == MATERIAL_MODIFY_ANIMATION_UNSET )
		{
			m_iFrameEnd = pTexture->GetNumAnimationFrames();
		}

		numFrames = (m_iFrameEnd - m_iFrameStart) + 1;
		bWrapAnimation = m_bCustomWrap;
		flFrameRate = m_flCustomFramerate;
		iLastFrame = (m_iFrameEnd - 1);
	}
	else
	{
		numFrames = pTexture->GetNumAnimationFrames();
		bWrapAnimation = m_WrapAnimation;
		flFrameRate = m_FrameRate;
		iLastFrame = (numFrames - 1);
	}

	// Have we already reached the end? If so, stay there.
	if ( m_bReachedEnd && !bWrapAnimation )
	{
		m_AnimatedTextureFrameNumVar->SetIntValue( iLastFrame );
		return;
	}

	// NOTE: Must not use relative time based methods here
	// because the bind proxy can be called many times per frame.
	// Prevent multiple Wrap callbacks to be sent for no wrap mode
	float startTime;
	if ( m_iFrameStart != MATERIAL_MODIFY_ANIMATION_UNSET )
	{
		startTime = m_flStartTime;
	}
	else
	{
		startTime = GetAnimationStartTime(pControl);
	}
	float deltaTime = gpGlobals->curtime - startTime;
	float prevTime = deltaTime - gpGlobals->frametime;

	// Clamp..
	if (deltaTime < 0.0f)
		deltaTime = 0.0f;
	if (prevTime < 0.0f)
		prevTime = 0.0f;

	float frame = flFrameRate * deltaTime;	
	float prevFrame = flFrameRate * prevTime;

	int intFrame = ((int)frame) % numFrames; 
	int intPrevFrame = ((int)prevFrame) % numFrames;

	if ( m_iFrameStart != MATERIAL_MODIFY_ANIMATION_UNSET )
	{
		intFrame += m_iFrameStart;
		intPrevFrame += m_iFrameStart;
	}

	// Report wrap situation...
	if (intPrevFrame > intFrame)
	{
		m_bReachedEnd = true;

		if (bWrapAnimation)
		{
			AnimationWrapped( pControl );
		}
		else
		{
			// Only sent the wrapped message once.
			// when we're in non-wrapping mode
			if (prevFrame < numFrames)
				AnimationWrapped( pControl );
			intFrame = numFrames - 1;
		}
	}

	m_AnimatedTextureFrameNumVar->SetIntValue( intFrame );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
float CMaterialModifyProxy::GetAnimationStartTime( void* pArg )
{
	IClientRenderable *pRend = (IClientRenderable *)pArg;
	if (!pRend)
		return 0.0f;

	C_BaseEntity* pEntity = pRend->GetIClientUnknown()->GetBaseEntity();
	if (pEntity)
	{
		return pEntity->GetTextureAnimationStartTime();
	}
	return 0.0f;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CMaterialModifyProxy::AnimationWrapped( void* pArg )
{
	IClientRenderable *pRend = (IClientRenderable *)pArg;
	if (!pRend)
		return;

	C_BaseEntity* pEntity = pRend->GetIClientUnknown()->GetBaseEntity();
	if (pEntity)
	{
		pEntity->TextureAnimationWrapped();
	}
}

//-----------------------------------------------------------------------------
// Does the dirty deed
//-----------------------------------------------------------------------------
void CMaterialModifyProxy::OnBindFloatLerp( C_MaterialModifyControl *pControl )
{
	if ( !pControl )
		return;

	if ( pControl->HasNewAnimationCommands() )
	{
		pControl->SetAnimationStartTime( gpGlobals->curtime );
		pControl->ClearAnimationCommands();
	}

	// Read the data from the modify entity
	materialfloatlerpcommands_t sCommands;
	pControl->GetFloatLerpCommands( &sCommands );

	m_flStartValue = sCommands.flStartValue;
	m_flEndValue = sCommands.flEndValue;
	m_flTransitionTime = sCommands.flTransitionTime;
	m_flStartTime = pControl->GetAnimationStartTime();
	bool bFound;
	m_pMaterialVar = m_pMaterial->FindVar( pControl->GetMaterialVariableName(), &bFound, false );

	if( bFound )
	{
		float currentValue;
		if( m_flTransitionTime > 0.0f )
		{
			currentValue = m_flStartValue + ( m_flEndValue - m_flStartValue ) * clamp( ( ( gpGlobals->curtime - m_flStartTime ) / m_flTransitionTime ), 0.0f, 1.0f );
		}
		else
		{
			currentValue = m_flEndValue;
		}

		if( debug_materialmodifycontrol_client.GetBool() && Q_stristr( m_pMaterial->GetName(), "faceandhair" ) && Q_stristr( m_pMaterialVar->GetName(), "warp" ) )
		{
			static int count = 0;
			DevMsg( 1, "CMaterialFloatLerpProxy::OnBind \"%s\" %s=%f %d\n", m_pMaterial->GetName(), m_pMaterialVar->GetName(), currentValue, count++ );
		}
		m_pMaterialVar->SetFloatValue( currentValue );
	}
}

//=============================================================================
//
// MATERIALMODIFYANIMATED PROXY 
//
class CMaterialModifyAnimatedProxy : public CBaseAnimatedTextureProxy
{
public:
	CMaterialModifyAnimatedProxy() {};
	virtual ~CMaterialModifyAnimatedProxy() {};
	virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
	virtual void OnBind( void *pEntity );

	virtual float GetAnimationStartTime( void* pBaseEntity );
	virtual void AnimationWrapped( void* pC_BaseEntity );

private:
	IMaterial	*m_pMaterial;
	int			m_iFrameStart;
	int			m_iFrameEnd;
	bool		m_bReachedEnd;
	float		m_flStartTime;
	bool		m_bCustomWrap;
	float		m_flCustomFramerate;
};

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CMaterialModifyAnimatedProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
{
	m_pMaterial = pMaterial;
	m_iFrameStart = MATERIAL_MODIFY_ANIMATION_UNSET;
	m_iFrameEnd = MATERIAL_MODIFY_ANIMATION_UNSET;
	m_bReachedEnd = false;
	return CBaseAnimatedTextureProxy::Init( pMaterial, pKeyValues );
}

//-----------------------------------------------------------------------------
// Does the dirty deed
//-----------------------------------------------------------------------------
void CMaterialModifyAnimatedProxy::OnBind( void *pEntity )
{
	assert ( m_AnimatedTextureVar );
	if( m_AnimatedTextureVar->GetType() != MATERIAL_VAR_TYPE_TEXTURE )
		return;

	ITexture *pTexture;
	pTexture = m_AnimatedTextureVar->GetTextureValue();

	// Get the modified material vars from the entity input
	IClientRenderable *pRend = (IClientRenderable *)pEntity;
	if ( pRend )
	{
		C_BaseEntity *pBaseEntity = pRend->GetIClientUnknown()->GetBaseEntity();
		if ( pBaseEntity )
		{
			for ( C_BaseEntity *pChild = pBaseEntity->FirstMoveChild(); pChild; pChild = pChild->NextMovePeer() )
			{
				C_MaterialModifyControl *pControl = dynamic_cast<C_MaterialModifyControl*>( pChild );
				if ( !pControl )
					continue;

				if ( !pControl->HasNewAnimationCommands() )
					continue;

				// Read the data from the modify entity
				materialanimcommands_t sCommands;
				pControl->GetAnimationCommands( &sCommands );

				m_iFrameStart = sCommands.iFrameStart;
				m_iFrameEnd = sCommands.iFrameEnd;
				m_bCustomWrap = sCommands.bWrap;
				m_flCustomFramerate = sCommands.flFrameRate;
				m_bReachedEnd = false;

				m_flStartTime = gpGlobals->curtime;

				pControl->ClearAnimationCommands();
			}
		}
	}

	// Init all the vars based on whether we're using the base material settings, 
	// or the custom ones from the entity input.
	int numFrames;
	bool bWrapAnimation;
	float flFrameRate;
	int iLastFrame;

	// Do we have a custom frame section from the server?
	if ( m_iFrameStart != MATERIAL_MODIFY_ANIMATION_UNSET )
	{
		if ( m_iFrameEnd == MATERIAL_MODIFY_ANIMATION_UNSET )
		{
			m_iFrameEnd = pTexture->GetNumAnimationFrames();
		}

		numFrames = (m_iFrameEnd - m_iFrameStart) + 1;
		bWrapAnimation = m_bCustomWrap;
		flFrameRate = m_flCustomFramerate;
		iLastFrame = (m_iFrameEnd - 1);
	}
	else
	{
		numFrames = pTexture->GetNumAnimationFrames();
		bWrapAnimation = m_WrapAnimation;
		flFrameRate = m_FrameRate;
		iLastFrame = (numFrames - 1);
	}

	// Have we already reached the end? If so, stay there.
	if ( m_bReachedEnd && !bWrapAnimation )
	{
		m_AnimatedTextureFrameNumVar->SetIntValue( iLastFrame );
		return;
	}

	// NOTE: Must not use relative time based methods here
	// because the bind proxy can be called many times per frame.
	// Prevent multiple Wrap callbacks to be sent for no wrap mode
	float startTime;
	if ( m_iFrameStart != MATERIAL_MODIFY_ANIMATION_UNSET )
	{
		startTime = m_flStartTime;
	}
	else
	{
		startTime = GetAnimationStartTime(pEntity);
	}
	float deltaTime = gpGlobals->curtime - startTime;
	float prevTime = deltaTime - gpGlobals->frametime;

	// Clamp..
	if (deltaTime < 0.0f)
		deltaTime = 0.0f;
	if (prevTime < 0.0f)
		prevTime = 0.0f;

	float frame = flFrameRate * deltaTime;	
	float prevFrame = flFrameRate * prevTime;

	int intFrame = ((int)frame) % numFrames; 
	int intPrevFrame = ((int)prevFrame) % numFrames;

	if ( m_iFrameStart != MATERIAL_MODIFY_ANIMATION_UNSET )
	{
		intFrame += m_iFrameStart;
		intPrevFrame += m_iFrameStart;
	}

	// Report wrap situation...
	if (intPrevFrame > intFrame)
	{
		m_bReachedEnd = true;

		if (bWrapAnimation)
		{
			AnimationWrapped( pEntity );
		}
		else
		{
			// Only sent the wrapped message once.
			// when we're in non-wrapping mode
			if (prevFrame < numFrames)
				AnimationWrapped( pEntity );
			intFrame = numFrames - 1;
		}
	}

	m_AnimatedTextureFrameNumVar->SetIntValue( intFrame );

	if ( ToolsEnabled() )
	{
		ToolFramework_RecordMaterialParams( GetMaterial() );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
float CMaterialModifyAnimatedProxy::GetAnimationStartTime( void* pArg )
{
	IClientRenderable *pRend = (IClientRenderable *)pArg;
	if (!pRend)
		return 0.0f;

	C_BaseEntity* pEntity = pRend->GetIClientUnknown()->GetBaseEntity();
	if (pEntity)
	{
		return pEntity->GetTextureAnimationStartTime();
	}
	return 0.0f;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CMaterialModifyAnimatedProxy::AnimationWrapped( void* pArg )
{
	IClientRenderable *pRend = (IClientRenderable *)pArg;
	if (!pRend)
		return;

	C_BaseEntity* pEntity = pRend->GetIClientUnknown()->GetBaseEntity();
	if (pEntity)
	{
		pEntity->TextureAnimationWrapped();
	}
}


EXPOSE_INTERFACE( CMaterialModifyProxy, IMaterialProxy, "MaterialModify" IMATERIAL_PROXY_INTERFACE_VERSION );
EXPOSE_INTERFACE( CMaterialModifyAnimatedProxy, IMaterialProxy, "MaterialModifyAnimated" IMATERIAL_PROXY_INTERFACE_VERSION );