//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "sharedInterface.h"
#include "materialsystem/imaterial.h"
#include <KeyValues.h>
#include "materialsystem/imaterialvar.h"
#include "functionproxy.h"
#include "toolframework_client.h"

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

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

class C_BaseEntity;

//-----------------------------------------------------------------------------
// Adds two vars...
//-----------------------------------------------------------------------------

class CAddProxy : public CFunctionProxy
{
public:
	bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
	void OnBind( void *pC_BaseEntity );
};

bool CAddProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
{
	// Requires 2 args..
	bool ok = CFunctionProxy::Init( pMaterial, pKeyValues );
	ok = ok && m_pSrc2;
	return ok;
}

void CAddProxy::OnBind( void *pC_BaseEntity )
{
	Assert( m_pSrc1 && m_pSrc2 && m_pResult );

	MaterialVarType_t resultType;
	int vecSize;
	ComputeResultType( resultType, vecSize );

	switch( resultType )
	{
	case MATERIAL_VAR_TYPE_VECTOR:
		{
			Vector a, b, c;
			m_pSrc1->GetVecValue( a.Base(), vecSize ); 
			m_pSrc2->GetVecValue( b.Base(), vecSize ); 
			VectorAdd( a, b, c );
			m_pResult->SetVecValue( c.Base(), vecSize );
		}
		break;

	case MATERIAL_VAR_TYPE_FLOAT:
		SetFloatResult( m_pSrc1->GetFloatValue() + m_pSrc2->GetFloatValue() );
		break;

	case MATERIAL_VAR_TYPE_INT:
		m_pResult->SetFloatValue( m_pSrc1->GetIntValue() + m_pSrc2->GetIntValue() );
		break;
	}

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

EXPOSE_INTERFACE( CAddProxy, IMaterialProxy, "Add" IMATERIAL_PROXY_INTERFACE_VERSION );


//-----------------------------------------------------------------------------
// Subtracts two vars...
//-----------------------------------------------------------------------------

class CSubtractProxy : public CFunctionProxy
{
public:
	bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
	void OnBind( void *pC_BaseEntity );
};

bool CSubtractProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
{
	// Requires 2 args..
	bool ok = CFunctionProxy::Init( pMaterial, pKeyValues );
	ok = ok && m_pSrc2;
	return ok;
}

void CSubtractProxy::OnBind( void *pC_BaseEntity )
{
	Assert( m_pSrc1 && m_pSrc2 && m_pResult );

	MaterialVarType_t resultType;
	int vecSize;
	ComputeResultType( resultType, vecSize );

	switch( resultType )
	{
	case MATERIAL_VAR_TYPE_VECTOR:
		{
			Vector a, b, c;
			m_pSrc1->GetVecValue( a.Base(), vecSize ); 
			m_pSrc2->GetVecValue( b.Base(), vecSize ); 
			VectorSubtract( a, b, c );
			m_pResult->SetVecValue( c.Base(), vecSize );
		}
		break;

	case MATERIAL_VAR_TYPE_FLOAT:
		SetFloatResult( m_pSrc1->GetFloatValue() - m_pSrc2->GetFloatValue() );
		break;

	case MATERIAL_VAR_TYPE_INT:
		m_pResult->SetFloatValue( m_pSrc1->GetIntValue() - m_pSrc2->GetIntValue() );
		break;
	}

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

EXPOSE_INTERFACE( CSubtractProxy, IMaterialProxy, "Subtract" IMATERIAL_PROXY_INTERFACE_VERSION );


//-----------------------------------------------------------------------------
// Multiplies two vars...
//-----------------------------------------------------------------------------

class CMultiplyProxy : public CFunctionProxy
{
public:
	bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
	void OnBind( void *pC_BaseEntity );
};

bool CMultiplyProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
{
	// Requires 2 args..
	bool ok = CFunctionProxy::Init( pMaterial, pKeyValues );
	ok = ok && m_pSrc2;
	return ok;
}

void CMultiplyProxy::OnBind( void *pC_BaseEntity )
{
	Assert( m_pSrc1 && m_pSrc2 && m_pResult );

	MaterialVarType_t resultType;
	int vecSize;
	ComputeResultType( resultType, vecSize );

	switch( resultType )
	{
	case MATERIAL_VAR_TYPE_VECTOR:
		{
			Vector a, b, c;
			m_pSrc1->GetVecValue( a.Base(), vecSize ); 
			m_pSrc2->GetVecValue( b.Base(), vecSize ); 
			VectorMultiply( a, b, c );
			m_pResult->SetVecValue( c.Base(), vecSize );
		}
		break;

	case MATERIAL_VAR_TYPE_FLOAT:
		SetFloatResult( m_pSrc1->GetFloatValue() * m_pSrc2->GetFloatValue() );
		break;

	case MATERIAL_VAR_TYPE_INT:
		m_pResult->SetFloatValue( m_pSrc1->GetIntValue() * m_pSrc2->GetIntValue() );
		break;
	}

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


EXPOSE_INTERFACE( CMultiplyProxy, IMaterialProxy, "Multiply" IMATERIAL_PROXY_INTERFACE_VERSION );


//-----------------------------------------------------------------------------
// divides two vars...
//-----------------------------------------------------------------------------

class CDivideProxy : public CFunctionProxy
{
public:
	bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
	void OnBind( void *pC_BaseEntity );
};

bool CDivideProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
{
	// Requires 2 args..
	bool ok = CFunctionProxy::Init( pMaterial, pKeyValues );
	ok = ok && m_pSrc2;
	return ok;
}

void CDivideProxy::OnBind( void *pC_BaseEntity )
{
	Assert( m_pSrc1 && m_pSrc2 && m_pResult );

	MaterialVarType_t resultType;
	int vecSize;
	ComputeResultType( resultType, vecSize );

	switch( resultType )
	{
	case MATERIAL_VAR_TYPE_VECTOR:
		{
			Vector a, b, c;
			m_pSrc1->GetVecValue( a.Base(), vecSize ); 
			m_pSrc2->GetVecValue( b.Base(), vecSize ); 
			VectorDivide( a, b, c );
			m_pResult->SetVecValue( c.Base(), vecSize );
		}
		break;

	case MATERIAL_VAR_TYPE_FLOAT:
		if (m_pSrc2->GetFloatValue() != 0)
		{
			SetFloatResult( m_pSrc1->GetFloatValue() / m_pSrc2->GetFloatValue() );
		}
		else
		{
			SetFloatResult( m_pSrc1->GetFloatValue() );
		}
		break;

	case MATERIAL_VAR_TYPE_INT:
		if (m_pSrc2->GetIntValue() != 0)
		{
			m_pResult->SetFloatValue( m_pSrc1->GetIntValue() / m_pSrc2->GetIntValue() );
		}
		else
		{
			m_pResult->SetFloatValue( m_pSrc1->GetIntValue() );
		}
		break;
	}

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

EXPOSE_INTERFACE( CDivideProxy, IMaterialProxy, "Divide" IMATERIAL_PROXY_INTERFACE_VERSION );

//-----------------------------------------------------------------------------
// clamps a var...
//-----------------------------------------------------------------------------

class CClampProxy : public CFunctionProxy
{
public:
	bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
	void OnBind( void *pC_BaseEntity );

private:
	CFloatInput m_Min;
	CFloatInput m_Max;
};

bool CClampProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
{
	if (!CFunctionProxy::Init( pMaterial, pKeyValues ))
		return false;

	if (!m_Min.Init( pMaterial, pKeyValues, "min", 0 ))
		return false;

	if (!m_Max.Init( pMaterial, pKeyValues, "max", 1 ))
		return false;

	return true;
}

void CClampProxy::OnBind( void *pC_BaseEntity )
{
	Assert( m_pSrc1 && m_pResult );

	MaterialVarType_t resultType;
	int vecSize;
	ComputeResultType( resultType, vecSize );

	float flMin = m_Min.GetFloat();
	float flMax = m_Max.GetFloat();

	if (flMin > flMax)
	{
		float flTemp = flMin;
		flMin = flMax;
		flMax = flTemp;
	}

	switch( resultType )
	{
	case MATERIAL_VAR_TYPE_VECTOR:
		{
			Vector a;
			m_pSrc1->GetVecValue( a.Base(), vecSize );
			for (int i = 0; i < vecSize; ++i)
			{
				if (a[i] < flMin)
					a[i] = flMin;
				else if (a[i] > flMax)
					a[i] = flMax;
			}
			m_pResult->SetVecValue( a.Base(), vecSize );
		}
		break;

	case MATERIAL_VAR_TYPE_FLOAT:
		{
			float src = m_pSrc1->GetFloatValue();
			if (src < flMin)
				src = flMin;
			else if (src > flMax)
				src = flMax;
			SetFloatResult( src );
		}
		break;

	case MATERIAL_VAR_TYPE_INT:
		{
			int src = m_pSrc1->GetIntValue();
			if (src < flMin)
				src = flMin;
			else if (src > flMax)
				src = flMax;
			m_pResult->SetIntValue( src );
		}
		break;
	}

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


EXPOSE_INTERFACE( CClampProxy, IMaterialProxy, "Clamp" IMATERIAL_PROXY_INTERFACE_VERSION );

//-----------------------------------------------------------------------------
// Creates a sinusoid
//-----------------------------------------------------------------------------

// sinePeriod: time that it takes to go through whole sine wave in seconds (default: 1.0f)
// sineMax : the max value for the sine wave (default: 1.0f )
// sineMin: the min value for the sine wave  (default: 0.0f )
class CSineProxy : public CResultProxy
{
public:
	virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
	virtual void OnBind( void *pC_BaseEntity );

private:
	CFloatInput m_SinePeriod;
	CFloatInput m_SineMax;
	CFloatInput m_SineMin;
	CFloatInput m_SineTimeOffset;
};


bool CSineProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
{
	if (!CResultProxy::Init( pMaterial, pKeyValues ))
		return false;

	if (!m_SinePeriod.Init( pMaterial, pKeyValues, "sinePeriod", 1.0f ))
		return false;
	if (!m_SineMax.Init( pMaterial, pKeyValues, "sineMax", 1.0f ))
		return false;
	if (!m_SineMin.Init( pMaterial, pKeyValues, "sineMin", 0.0f ))
		return false;
	if (!m_SineTimeOffset.Init( pMaterial, pKeyValues, "timeOffset", 0.0f ))
		return false;

	return true;
}

void CSineProxy::OnBind( void *pC_BaseEntity )
{
	Assert( m_pResult );

	float flValue;
	float flSineTimeOffset = m_SineTimeOffset.GetFloat();
	float flSineMax = m_SineMax.GetFloat();
	float flSineMin = m_SineMin.GetFloat();
	float flSinePeriod = m_SinePeriod.GetFloat();
	if (flSinePeriod == 0)
		flSinePeriod = 1;

	// get a value in [0,1]
	flValue = ( sin( 2.0f * M_PI * (gpGlobals->curtime - flSineTimeOffset) / flSinePeriod ) * 0.5f ) + 0.5f;
	// get a value in [min,max]	
	flValue = ( flSineMax - flSineMin ) * flValue + flSineMin;
	
	SetFloatResult( flValue );

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

EXPOSE_INTERFACE( CSineProxy, IMaterialProxy, "Sine" IMATERIAL_PROXY_INTERFACE_VERSION );

//-----------------------------------------------------------------------------
// copies a var...
//-----------------------------------------------------------------------------

class CEqualsProxy : public CFunctionProxy
{
public:
	void OnBind( void *pC_BaseEntity );
};


void CEqualsProxy::OnBind( void *pC_BaseEntity )
{
	Assert( m_pSrc1 && m_pResult );

	MaterialVarType_t resultType;
	int vecSize;
	ComputeResultType( resultType, vecSize );

	switch( resultType )
	{
	case MATERIAL_VAR_TYPE_VECTOR:
		{
			Vector a;
			m_pSrc1->GetVecValue( a.Base(), vecSize );
			m_pResult->SetVecValue( a.Base(), vecSize );
		}
		break;

	case MATERIAL_VAR_TYPE_FLOAT:
		SetFloatResult( m_pSrc1->GetFloatValue() );
		break;

	case MATERIAL_VAR_TYPE_INT:
		m_pResult->SetIntValue( m_pSrc1->GetIntValue() );
		break;
	}

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


EXPOSE_INTERFACE( CEqualsProxy, IMaterialProxy, "Equals" IMATERIAL_PROXY_INTERFACE_VERSION );


//-----------------------------------------------------------------------------
// Get the fractional part of a var
//-----------------------------------------------------------------------------

class CFracProxy : public CFunctionProxy
{
public:
	void OnBind( void *pC_BaseEntity );
};


void CFracProxy::OnBind( void *pC_BaseEntity )
{
	Assert( m_pSrc1 && m_pResult );

	MaterialVarType_t resultType;
	int vecSize;
	ComputeResultType( resultType, vecSize );

	switch( resultType )
	{
	case MATERIAL_VAR_TYPE_VECTOR:
		{
			Vector a;
			m_pSrc1->GetVecValue( a.Base(), vecSize );
			a[0] -= ( float )( int )a[0];
			a[1] -= ( float )( int )a[1];
			a[2] -= ( float )( int )a[2];
			m_pResult->SetVecValue( a.Base(), vecSize );
		}
		break;

	case MATERIAL_VAR_TYPE_FLOAT:
		{
			float a = m_pSrc1->GetFloatValue();
			a -= ( int )a;
			SetFloatResult( a );
		}
		break;

	case MATERIAL_VAR_TYPE_INT:
		// don't do anything besides assignment!
		m_pResult->SetIntValue( m_pSrc1->GetIntValue() );
		break;
	}

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


EXPOSE_INTERFACE( CFracProxy, IMaterialProxy, "Frac" IMATERIAL_PROXY_INTERFACE_VERSION );

//-----------------------------------------------------------------------------
// Get the Integer part of a var
//-----------------------------------------------------------------------------

class CIntProxy : public CFunctionProxy
{
public:
	void OnBind( void *pC_BaseEntity );
};

void CIntProxy::OnBind( void *pC_BaseEntity )
{
	Assert( m_pSrc1 && m_pResult );

	MaterialVarType_t resultType;
	int vecSize;
	ComputeResultType( resultType, vecSize );

	switch( resultType )
	{
	case MATERIAL_VAR_TYPE_VECTOR:
		{
			Vector a;
			m_pSrc1->GetVecValue( a.Base(), vecSize );
			a[0] = ( float )( int )a[0];
			a[1] = ( float )( int )a[1];
			a[2] = ( float )( int )a[2];
			m_pResult->SetVecValue( a.Base(), vecSize );
		}
		break;

	case MATERIAL_VAR_TYPE_FLOAT:
		{
			float a = m_pSrc1->GetFloatValue();
			a = ( float )( int )a;
			SetFloatResult( a );
		}
		break;

	case MATERIAL_VAR_TYPE_INT:
		// don't do anything besides assignment!
		m_pResult->SetIntValue( m_pSrc1->GetIntValue() );
		break;
	}

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

EXPOSE_INTERFACE( CIntProxy, IMaterialProxy, "Int" IMATERIAL_PROXY_INTERFACE_VERSION );

//-----------------------------------------------------------------------------
// Linear ramp proxy
//-----------------------------------------------------------------------------
class CLinearRampProxy : public CResultProxy
{
public:
	virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
	virtual void OnBind( void *pC_BaseEntity );

private:
	CFloatInput m_Rate;
	CFloatInput m_InitialValue;
};


bool CLinearRampProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
{
	if (!CResultProxy::Init( pMaterial, pKeyValues ))
		return false;

	if (!m_Rate.Init( pMaterial, pKeyValues, "rate", 1 ))
		return false;

	if (!m_InitialValue.Init( pMaterial, pKeyValues, "initialValue", 0 ))
		return false;

	return true;
}

void CLinearRampProxy::OnBind( void *pC_BaseEntity )
{
	Assert( m_pResult );

	float flValue;
	
	// get a value in [0,1]
	flValue = m_Rate.GetFloat() * gpGlobals->curtime + m_InitialValue.GetFloat();	
	SetFloatResult( flValue );

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



EXPOSE_INTERFACE( CLinearRampProxy, IMaterialProxy, "LinearRamp" IMATERIAL_PROXY_INTERFACE_VERSION );


//-----------------------------------------------------------------------------
// Uniform noise proxy
//-----------------------------------------------------------------------------
class CUniformNoiseProxy : public CResultProxy
{
public:
	virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
	virtual void OnBind( void *pC_BaseEntity );

private:
	CFloatInput	m_flMinVal;
	CFloatInput	m_flMaxVal;
};


bool CUniformNoiseProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
{
	if (!CResultProxy::Init( pMaterial, pKeyValues ))
		return false;

	if (!m_flMinVal.Init( pMaterial, pKeyValues, "minVal", 0 ))
		return false;

	if (!m_flMaxVal.Init( pMaterial, pKeyValues, "maxVal", 1 ))
		return false;

	return true;
}

void CUniformNoiseProxy::OnBind( void *pC_BaseEntity )
{
	SetFloatResult( random->RandomFloat( m_flMinVal.GetFloat(), m_flMaxVal.GetFloat() ) );

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


EXPOSE_INTERFACE( CUniformNoiseProxy, IMaterialProxy, "UniformNoise" IMATERIAL_PROXY_INTERFACE_VERSION );


//-----------------------------------------------------------------------------
// Gaussian noise proxy
//-----------------------------------------------------------------------------
class CGaussianNoiseProxy : public CResultProxy
{
public:
	virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
	virtual void OnBind( void *pC_BaseEntity );

private:
	CFloatInput m_Mean;
	CFloatInput m_StdDev;
	CFloatInput	m_flMinVal;
	CFloatInput	m_flMaxVal;
};


bool CGaussianNoiseProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
{
	if (!CResultProxy::Init( pMaterial, pKeyValues ))
		return false;

	if (!m_Mean.Init( pMaterial, pKeyValues, "mean", 0.0f ))
		return false;

	if (!m_StdDev.Init( pMaterial, pKeyValues, "halfwidth", 1.0f ))
		return false;

	if (!m_flMinVal.Init( pMaterial, pKeyValues, "minVal", -FLT_MAX ))
		return false;

	if (!m_flMaxVal.Init( pMaterial, pKeyValues, "maxVal", FLT_MAX ))
		return false;

	return true;
}

void CGaussianNoiseProxy::OnBind( void *pC_BaseEntity )
{
	float flMean = m_Mean.GetFloat();
	float flStdDev = m_StdDev.GetFloat();
	float flVal = randomgaussian->RandomFloat( flMean, flStdDev );
	float flMaxVal = m_flMaxVal.GetFloat();
	float flMinVal = m_flMinVal.GetFloat();

	if (flMinVal > flMaxVal)
	{
		float flTemp = flMinVal;
		flMinVal = flMaxVal;
		flMaxVal = flTemp;
	}

	// clamp
	if (flVal < flMinVal)
		flVal = flMinVal;
	else if ( flVal > flMaxVal )
		flVal = flMaxVal;

	SetFloatResult( flVal );

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


EXPOSE_INTERFACE( CGaussianNoiseProxy, IMaterialProxy, "GaussianNoise" IMATERIAL_PROXY_INTERFACE_VERSION );


//-----------------------------------------------------------------------------
// Exponential proxy
//-----------------------------------------------------------------------------
class CExponentialProxy : public CFunctionProxy
{
public:
	virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
	virtual void OnBind( void *pC_BaseEntity );

private:
	CFloatInput	m_Scale;
	CFloatInput	m_Offset;
	CFloatInput	m_flMinVal;
	CFloatInput	m_flMaxVal;
};


bool CExponentialProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
{
	if (!CFunctionProxy::Init( pMaterial, pKeyValues ))
		return false;

	if (!m_Scale.Init( pMaterial, pKeyValues, "scale", 1.0f ))
		return false;

	if (!m_Offset.Init( pMaterial, pKeyValues, "offset", 0.0f ))
		return false;

	if (!m_flMinVal.Init( pMaterial, pKeyValues, "minVal", -FLT_MAX ))
		return false;

	if (!m_flMaxVal.Init( pMaterial, pKeyValues, "maxVal", FLT_MAX ))
		return false;

	return true;
}

void CExponentialProxy::OnBind( void *pC_BaseEntity )
{	
	float flVal = m_Scale.GetFloat() * exp(m_pSrc1->GetFloatValue( ) + m_Offset.GetFloat());

	float flMaxVal = m_flMaxVal.GetFloat();
	float flMinVal = m_flMinVal.GetFloat();

	if (flMinVal > flMaxVal)
	{
		float flTemp = flMinVal;
		flMinVal = flMaxVal;
		flMaxVal = flTemp;
	}

	// clamp
	if (flVal < flMinVal)
		flVal = flMinVal;
	else if ( flVal > flMaxVal )
		flVal = flMaxVal;

	SetFloatResult( flVal );

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


EXPOSE_INTERFACE( CExponentialProxy, IMaterialProxy, "Exponential" IMATERIAL_PROXY_INTERFACE_VERSION );


//-----------------------------------------------------------------------------
// Absolute value proxy
//-----------------------------------------------------------------------------
class CAbsProxy : public CFunctionProxy
{
public:
	virtual void OnBind( void *pC_BaseEntity );
};


void CAbsProxy::OnBind( void *pC_BaseEntity )
{	
	SetFloatResult( fabs(m_pSrc1->GetFloatValue( )) );

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


EXPOSE_INTERFACE( CAbsProxy, IMaterialProxy, "Abs" IMATERIAL_PROXY_INTERFACE_VERSION );


//-----------------------------------------------------------------------------
// Empty proxy-- used to comment out large proxy blocks
//-----------------------------------------------------------------------------
class CEmptyProxy : public IMaterialProxy
{
public:
	CEmptyProxy() {}
	virtual ~CEmptyProxy() {}
	virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues ) { return true; }
	virtual void OnBind( void *pC_BaseEntity ) {}
	virtual void Release( void ) { delete this; }
	virtual IMaterial *GetMaterial() { return NULL; }
};


EXPOSE_INTERFACE( CEmptyProxy, IMaterialProxy, "Empty" IMATERIAL_PROXY_INTERFACE_VERSION );


//-----------------------------------------------------------------------------
// Comparison proxy
//-----------------------------------------------------------------------------
class CLessOrEqualProxy : public CFunctionProxy
{
public:
	bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
	void OnBind( void *pC_BaseEntity );

private:
	IMaterialVar *m_pLessVar;
	IMaterialVar *m_pGreaterVar;
};

bool CLessOrEqualProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
{
	char const* pLessEqualVar = pKeyValues->GetString( "lessEqualVar" );
	if( !pLessEqualVar )
		return false;

	bool foundVar;
	m_pLessVar = pMaterial->FindVar( pLessEqualVar, &foundVar, true );
	if( !foundVar )
		return false;

	char const* pGreaterVar = pKeyValues->GetString( "greaterVar" );
	if( !pGreaterVar )
		return false;

	foundVar;
	m_pGreaterVar = pMaterial->FindVar( pGreaterVar, &foundVar, true );
	if( !foundVar )
		return false;

	// Compare 2 args..
	bool ok = CFunctionProxy::Init( pMaterial, pKeyValues );
	ok = ok && m_pSrc2;
	return ok;
}

void CLessOrEqualProxy::OnBind( void *pC_BaseEntity )
{
	Assert( m_pSrc1 && m_pSrc2 && m_pLessVar && m_pGreaterVar && m_pResult );

	IMaterialVar *pSourceVar;
	if (m_pSrc1->GetFloatValue() <= m_pSrc2->GetFloatValue())
	{
		pSourceVar = m_pLessVar;
	}
	else
	{
		pSourceVar = m_pGreaterVar;
	}

	int vecSize = 0;
	MaterialVarType_t resultType = m_pResult->GetType();
	if (resultType == MATERIAL_VAR_TYPE_VECTOR)
	{
		if (m_ResultVecComp >= 0)
			resultType = MATERIAL_VAR_TYPE_FLOAT;
		vecSize = m_pResult->VectorSize();
	}
	else if (resultType == MATERIAL_VAR_TYPE_UNDEFINED)
	{
		resultType = pSourceVar->GetType();
		if (resultType == MATERIAL_VAR_TYPE_VECTOR)
		{
			vecSize = pSourceVar->VectorSize();
		}
	}

	switch( resultType )
	{
	case MATERIAL_VAR_TYPE_VECTOR:
		{
			Vector src;
			pSourceVar->GetVecValue( src.Base(), vecSize ); 
			m_pResult->SetVecValue( src.Base(), vecSize );
		}
		break;

	case MATERIAL_VAR_TYPE_FLOAT:
		SetFloatResult( pSourceVar->GetFloatValue() );
		break;

	case MATERIAL_VAR_TYPE_INT:
		m_pResult->SetFloatValue( pSourceVar->GetIntValue() );
		break;
	}

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

EXPOSE_INTERFACE( CLessOrEqualProxy, IMaterialProxy, "LessOrEqual" IMATERIAL_PROXY_INTERFACE_VERSION );

//-----------------------------------------------------------------------------
// WrapMinMax proxy
//-----------------------------------------------------------------------------
class CWrapMinMaxProxy : public CFunctionProxy
{
public:
	virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
	virtual void OnBind( void *pC_BaseEntity );

private:
	CFloatInput	m_flMinVal;
	CFloatInput	m_flMaxVal;
};

bool CWrapMinMaxProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
{
	if (!CFunctionProxy::Init( pMaterial, pKeyValues ))
		return false;

	if (!m_flMinVal.Init( pMaterial, pKeyValues, "minVal", 0 ))
		return false;

	if (!m_flMaxVal.Init( pMaterial, pKeyValues, "maxVal", 1 ))
		return false;

	return true;
}

void CWrapMinMaxProxy::OnBind( void *pC_BaseEntity )
{
	Assert( m_pSrc1 && m_pResult );

	if ( m_flMaxVal.GetFloat() <= m_flMinVal.GetFloat() ) // Bad input, just return the min
	{
		SetFloatResult( m_flMinVal.GetFloat() );
	}
	else
	{
		float flResult = ( m_pSrc1->GetFloatValue() - m_flMinVal.GetFloat() ) / ( m_flMaxVal.GetFloat() - m_flMinVal.GetFloat() );

		if ( flResult >= 0.0f )
		{
			flResult -= ( float )( int )flResult;
		}
		else // Negative
		{
			flResult -= ( float )( ( ( int )flResult ) - 1 );
		}

		flResult *= ( m_flMaxVal.GetFloat() - m_flMinVal.GetFloat() );
		flResult += m_flMinVal.GetFloat();

		SetFloatResult( flResult );
	}

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

EXPOSE_INTERFACE( CWrapMinMaxProxy, IMaterialProxy, "WrapMinMax" IMATERIAL_PROXY_INTERFACE_VERSION );


//-----------------------------------------------------------------------------
// Selects the first var value if it's non-zero, otherwise goes with the second
//-----------------------------------------------------------------------------

class CSelectFirstIfNonZeroProxy : public CFunctionProxy
{
public:
	bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
	void OnBind( void *pC_BaseEntity );
};

bool CSelectFirstIfNonZeroProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
{
	// Requires 2 args..
	bool ok = CFunctionProxy::Init( pMaterial, pKeyValues );
	ok = ok && m_pSrc2;
	return ok;
}

void CSelectFirstIfNonZeroProxy::OnBind( void *pC_BaseEntity )
{
	Assert( m_pSrc1 && m_pSrc2 && m_pResult );

	MaterialVarType_t resultType;
	int vecSize;
	ComputeResultType( resultType, vecSize );

	switch( resultType )
	{
	case MATERIAL_VAR_TYPE_VECTOR:
		{
			Vector a, b;
			m_pSrc1->GetVecValue( a.Base(), vecSize ); 
			m_pSrc2->GetVecValue( b.Base(), vecSize ); 

			if ( !a.IsZero() )
			{
				m_pResult->SetVecValue( a.Base(), vecSize );
			}
			else
			{
				m_pResult->SetVecValue( b.Base(), vecSize );
			}
		}
		break;

	case MATERIAL_VAR_TYPE_FLOAT:
		if ( m_pSrc1->GetFloatValue() )
		{
			SetFloatResult( m_pSrc1->GetFloatValue() );
		}
		else
		{
			SetFloatResult( m_pSrc2->GetFloatValue() );
		}
		break;

	case MATERIAL_VAR_TYPE_INT:
		if ( m_pSrc1->GetIntValue() )
		{
			m_pResult->SetFloatValue( m_pSrc1->GetIntValue() );
		}
		else
		{
			m_pResult->SetFloatValue( m_pSrc2->GetIntValue() );
		}
		break;
	}

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

EXPOSE_INTERFACE( CSelectFirstIfNonZeroProxy, IMaterialProxy, "SelectFirstIfNonZero" IMATERIAL_PROXY_INTERFACE_VERSION );