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

#include "materialsystem/imaterialvar.h"
#include "materialsystem/imaterialsystem.h"
#include "materialsystem/itexture.h"
#include <string.h>
#include "materialsystem_global.h"
#include <stdlib.h>
#include "shaderapi/ishaderapi.h"
#include "imaterialinternal.h"
#include "utlsymbol.h"
#include "mempool.h"
#include "itextureinternal.h"
#include "tier0/dbg.h"
#include "tier1/callqueue.h"
#include "mathlib/vmatrix.h"
#include "tier1/strtools.h"
#include "texturemanager.h"

#define MATERIALVAR_CHAR_BUF_SIZE 512

ConVar mat_texture_tracking( "mat_texture_tracking", IsDebug() ? "1" : "0" );
CUtlMap<ITexture*, CInterlockedInt> s_TextureRefList( DefLessFunc( ITexture* ) );
CUtlMap<ITexture*, CInterlockedInt> *g_pTextureRefList = &s_TextureRefList;

struct MaterialVarMatrix_t
{
	VMatrix m_Matrix;
	bool m_bIsIdent;
};

class CMaterialVar : public IMaterialVar
{
public:
	// stuff from IMaterialVar
	virtual const char *		GetName( void ) const;
	virtual MaterialVarSym_t	GetNameAsSymbol() const;
	virtual void				SetFloatValue( float val );
	virtual void				SetIntValue( int val );
	virtual void				SetStringValue( const char *val );
	virtual const char *		GetStringValue( void ) const;
	virtual void				SetMatrixValue( VMatrix const& matrix );
	virtual VMatrix const&		GetMatrixValue( );
	virtual bool				MatrixIsIdentity( void ) const;
	virtual void				SetVecValue( const float* pVal, int numComps );
	virtual void				SetVecValue( float x, float y );
	virtual void				SetVecValue( float x, float y, float z );
	virtual void				SetVecValue( float x, float y, float z, float w );
	void						SetVecValueInternal( const Vector4D &vec, int nComps );
	virtual void				SetVecComponentValue( float fVal, int nComponent );
	virtual void				GetLinearVecValue( float *val, int numComps ) const;
	virtual void				SetFourCCValue( FourCC type, void *pData );
	virtual void				GetFourCCValue( FourCC *type, void **ppData );
	virtual int					GetIntValueInternal( void ) const;
	virtual float				GetFloatValueInternal( void ) const;
	virtual float const*		GetVecValueInternal( ) const;
	virtual void				GetVecValueInternal( float *val, int numcomps ) const;
	virtual int					VectorSizeInternal() const;

	// revisit: is this a good interface for textures?

	virtual ITexture *			GetTextureValue( void );
	virtual void				SetTextureValue( ITexture * );
	void						SetTextureValueQueued( ITexture *texture );

	virtual IMaterial *			GetMaterialValue( void );
	virtual void				SetMaterialValue( IMaterial * );

	virtual 					operator ITexture *() { return GetTextureValue(); }
	virtual bool				IsDefined() const;
	virtual void				SetUndefined();

	virtual void				CopyFrom( IMaterialVar *pMaterialVar );

	FORCEINLINE void Init( void )
	{
		m_nNumVectorComps = 4;
		m_VecVal.Init();
		m_pStringVal = NULL;
		m_intVal = 0;
		m_nTempIndex = 0xFF;
		m_bFakeMaterialVar = false;
		m_Type = MATERIAL_VAR_TYPE_INT;
	}

	// stuff that is only visible inside of the material system
	CMaterialVar();
	CMaterialVar( IMaterial* pMaterial, const char *key, VMatrix const& matrix );
	CMaterialVar( IMaterial* pMaterial, const char *key, const char *val );
	CMaterialVar( IMaterial* pMaterial, const char *key, float* pVal, int numcomps );
	CMaterialVar( IMaterial* pMaterial, const char *key, float val );
	CMaterialVar( IMaterial* pMaterial, const char *key, int val );
	CMaterialVar( IMaterial* pMaterial, const char *key );
	virtual ~CMaterialVar();

	virtual void			SetValueAutodetectType( const char *val );

	virtual IMaterial *		GetOwningMaterial() { return m_pMaterial; }

private:
	// Cleans up material var data
	CMaterialVar *AllocThreadVar();
	void CleanUpData();

	// NOTE: Dummy vars have no backlink so we have to check the pointer here
	void VarChanged() { if ( m_pMaterial ) m_pMaterial->ReportVarChanged(this); }

	// class data
	static char s_CharBuf[MATERIALVAR_CHAR_BUF_SIZE];
	static ITextureInternal *m_dummyTexture;

	// Fixed-size allocator
#ifdef NO_SBH // not needed if tier0 small block heap enabled
	DECLARE_FIXEDSIZE_ALLOCATOR( CMaterialVar );
#endif

	// Owning material....
	IMaterialInternal* m_pMaterial;

	// Only using one of these at a time...
	struct FourCC_t
	{
		FourCC	m_FourCC;
		void	*m_pFourCCData;
	};

	FourCC_t *AllocFourCC();

	union
	{
		IMaterialInternal* m_pMaterialValue;
		ITextureInternal *m_pTexture;
		MaterialVarMatrix_t* m_pMatrix;
		FourCC_t *m_pFourCC;
	};
};

// Has to exist *after* fixed size allocator declaration
#include "tier0/memdbgon.h"

typedef CMaterialVar *CMaterialVarPtr;

#ifdef NO_SBH // not needed if tier0 small block heap enabled
DEFINE_FIXEDSIZE_ALLOCATOR( CMaterialVar, 1024, true );
#endif

// Stores symbols for the material vars
static CUtlSymbolTableMT s_MaterialVarSymbols( 0, 32, true );

static bool g_bDeleteUnreferencedTexturesEnabled = false;


//-----------------------------------------------------------------------------
// Used to make GetIntValue thread safe from within proxy calls
//-----------------------------------------------------------------------------
static CMaterialVar s_pTempMaterialVar[254];
static MaterialVarMatrix_t s_pTempMatrix[254];
static bool s_bEnableThreadedAccess = false;
static int s_nTempVarsUsed = 0;
static int s_nOverflowTempVars = 0;


//-----------------------------------------------------------------------------
// Global methods related to material vars
//-----------------------------------------------------------------------------
void EnableThreadedMaterialVarAccess( bool bEnable, IMaterialVar **ppParams, int nVarCount )
{
	if ( s_bEnableThreadedAccess == bEnable	)
		return;

	s_bEnableThreadedAccess = bEnable;
	if ( !s_bEnableThreadedAccess )
	{
		// Necessary to free up reference counts
		Assert( s_nTempVarsUsed <= Q_ARRAYSIZE(s_pTempMaterialVar) );
		for ( int i = 0; i < s_nTempVarsUsed; ++i )
		{
			s_pTempMaterialVar[i].SetUndefined();
		}
		for ( int i = 0; i < nVarCount; ++i )
		{
			ppParams[i]->SetTempIndex( 0xFF );
		}
		s_nTempVarsUsed = 0;
		if ( s_nOverflowTempVars )
		{
			Warning("Overflowed %d temp material vars!\n", s_nOverflowTempVars );
			s_nOverflowTempVars = 0;
		}
	}
}


CMaterialVar *CMaterialVar::AllocThreadVar()
{
	if ( s_bEnableThreadedAccess )
	{
		if ( m_nTempIndex == 0xFF )
		{
			if ( s_nTempVarsUsed >= Q_ARRAYSIZE(s_pTempMaterialVar) )
			{
				s_nOverflowTempVars++;
				return NULL;
			}
			m_nTempIndex = s_nTempVarsUsed++;
		}

		return &s_pTempMaterialVar[m_nTempIndex];
	}

	return NULL;
}

//-----------------------------------------------------------------------------
// Purpose: Static method
// Input  : enable - 
//-----------------------------------------------------------------------------
void IMaterialVar::DeleteUnreferencedTextures( bool enable )
{
	g_bDeleteUnreferencedTexturesEnabled = enable;
}

//-----------------------------------------------------------------------------
// class factory methods
//-----------------------------------------------------------------------------
IMaterialVar* IMaterialVar::Create( IMaterial* pMaterial, const char* pKey, VMatrix const& matrix )
{
	return new CMaterialVar( pMaterial, pKey, matrix );
}

IMaterialVar* IMaterialVar::Create( IMaterial* pMaterial, const char* pKey, const char* pVal )
{
	return new CMaterialVar( pMaterial, pKey, pVal );
}

IMaterialVar* IMaterialVar::Create( IMaterial* pMaterial, const char* pKey, float* pVal, int numComps )
{
	return new CMaterialVar( pMaterial, pKey, pVal, numComps );
}

IMaterialVar* IMaterialVar::Create( IMaterial* pMaterial, const char* pKey, float val )
{
	return new CMaterialVar( pMaterial, pKey, val );
}

IMaterialVar* IMaterialVar::Create( IMaterial* pMaterial, const char* pKey, int val )
{
	return new CMaterialVar( pMaterial, pKey, val );
}

IMaterialVar* IMaterialVar::Create( IMaterial* pMaterial, const char* pKey )
{
	return new CMaterialVar( pMaterial, pKey );
}

void IMaterialVar::Destroy( IMaterialVar* pVar )
{
	if (pVar)
	{
		CMaterialVar* pVarImp = static_cast<CMaterialVar*>(pVar);
		delete pVarImp;
	}
}

MaterialVarSym_t IMaterialVar::GetSymbol( const char* pName )
{
	if (!pName)
		return UTL_INVAL_SYMBOL;

	char temp[1024];
	Q_strncpy( temp, pName, sizeof( temp ) );
	Q_strlower( temp );
	return s_MaterialVarSymbols.AddString( temp );
}

MaterialVarSym_t IMaterialVar::FindSymbol( const char* pName )
{
	if (!pName)
		return UTL_INVAL_SYMBOL;

	return s_MaterialVarSymbols.Find( pName );
}

bool IMaterialVar::SymbolMatches( const char* pName, MaterialVarSym_t symbol )
{
	return !Q_stricmp( s_MaterialVarSymbols.String(symbol), pName );
}


//-----------------------------------------------------------------------------
// class globals
//-----------------------------------------------------------------------------
char CMaterialVar::s_CharBuf[MATERIALVAR_CHAR_BUF_SIZE];


//-----------------------------------------------------------------------------
// constructors
//-----------------------------------------------------------------------------
inline CMaterialVar::FourCC_t *CMaterialVar::AllocFourCC()
{
	return new FourCC_t;
}


//-----------------------------------------------------------------------------
// NOTE: This constructor is only used by the "fake" material vars
// used to get thread mode working
//-----------------------------------------------------------------------------
CMaterialVar::CMaterialVar()
{
	Init();
	m_pMaterial = NULL;
	m_bFakeMaterialVar = true;
}

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

CMaterialVar::CMaterialVar( IMaterial* pMaterial, const char *pKey, VMatrix const& matrix )
{
	Init();
	Assert( pKey );

	m_pMaterial = static_cast<IMaterialInternal*>(pMaterial);
	m_Name = GetSymbol( pKey );
	Assert( m_Name != UTL_INVAL_SYMBOL );
	m_Type = MATERIAL_VAR_TYPE_MATRIX;
	m_pMatrix = new MaterialVarMatrix_t;
	Assert( m_pMatrix );
	MatrixCopy( matrix, m_pMatrix->m_Matrix );
	m_pMatrix->m_bIsIdent = matrix.IsIdentity();
	m_intVal = 0;
	m_VecVal.Init();

}

CMaterialVar::CMaterialVar( IMaterial* pMaterial, const char *pKey, const char *pVal )
{
	Init();
	Assert( pVal && pKey );

	m_pMaterial = static_cast<IMaterialInternal*>(pMaterial);
	m_Name = GetSymbol( pKey );
	Assert( m_Name != UTL_INVAL_SYMBOL );
	int len = Q_strlen( pVal ) + 1;
	m_pStringVal = new char[ len ];
	Q_strncpy( m_pStringVal, pVal, len );
	m_Type = MATERIAL_VAR_TYPE_STRING;
	m_VecVal[0] = m_VecVal[1] = m_VecVal[2] = m_VecVal[3] = atof( m_pStringVal );
	m_intVal = int( atof( m_pStringVal ) );
}

CMaterialVar::CMaterialVar( IMaterial* pMaterial, const char *pKey, float* pVal, int numComps )
{
	Init();
	Assert( pVal && pKey && (numComps <= 4) );

	m_pMaterial = static_cast<IMaterialInternal*>(pMaterial);;
	m_Name = GetSymbol( pKey );
	Assert( m_Name != UTL_INVAL_SYMBOL );
	m_Type = MATERIAL_VAR_TYPE_VECTOR;
	memcpy( m_VecVal.Base(), pVal, numComps * sizeof(float) );
	for (int i = numComps; i < 4; ++i)
		m_VecVal[i] = 0.0f;

	m_intVal = ( int ) m_VecVal[0];
	m_nNumVectorComps = numComps;
}

CMaterialVar::CMaterialVar( IMaterial* pMaterial, const char *pKey, float val )
{
	Init();
	m_pMaterial = static_cast<IMaterialInternal*>(pMaterial);
	m_Name = GetSymbol( pKey );
	Assert( m_Name != UTL_INVAL_SYMBOL );
	m_Type = MATERIAL_VAR_TYPE_FLOAT;
	m_VecVal[0] = m_VecVal[1] = m_VecVal[2] = m_VecVal[3] = val;
	m_intVal = (int) val;

}

CMaterialVar::CMaterialVar( IMaterial* pMaterial, const char *pKey, int val )
{
	Init();
	m_pMaterial = static_cast<IMaterialInternal*>(pMaterial);
	m_Name = GetSymbol( pKey );
	Assert( m_Name != UTL_INVAL_SYMBOL );
	m_Type = MATERIAL_VAR_TYPE_INT;
	m_VecVal[0] = m_VecVal[1] = m_VecVal[2] = m_VecVal[3] = (float) val;
	m_intVal = val;
}

CMaterialVar::CMaterialVar( IMaterial* pMaterial, const char *pKey )
{
	Init();
	m_pMaterial = static_cast<IMaterialInternal*>(pMaterial);
	m_Name = GetSymbol( pKey );
	Assert( m_Name != UTL_INVAL_SYMBOL );
	m_Type = MATERIAL_VAR_TYPE_UNDEFINED;
}


//-----------------------------------------------------------------------------
// destructor
//-----------------------------------------------------------------------------
CMaterialVar::~CMaterialVar()
{
	CleanUpData();
}


//-----------------------------------------------------------------------------
// Cleans up material var allocated data if necessary
//-----------------------------------------------------------------------------
void CMaterialVar::CleanUpData()
{
	switch ( m_Type )
	{
	case MATERIAL_VAR_TYPE_STRING:
		delete [] m_pStringVal;
		m_pStringVal = NULL;
		break;

	case MATERIAL_VAR_TYPE_TEXTURE:
		// garymcthack
		if( m_pTexture )
		{
			m_pTexture->DecrementReferenceCount();
			if ( g_bDeleteUnreferencedTexturesEnabled )
			{
				m_pTexture->DeleteIfUnreferenced();
			}
			m_pTexture = NULL;
		}
		break;

	case MATERIAL_VAR_TYPE_MATERIAL:
		if( m_pMaterialValue != NULL )
		{
			m_pMaterialValue->DecrementReferenceCount();
			m_pMaterialValue = NULL;
		}
		break;

	case MATERIAL_VAR_TYPE_MATRIX:
		delete m_pMatrix;
		m_pMatrix = NULL;
		break;

	case MATERIAL_VAR_TYPE_FOURCC:
		delete m_pFourCC;
		m_pFourCC = NULL;
		break;

	case MATERIAL_VAR_TYPE_VECTOR:
	case MATERIAL_VAR_TYPE_INT:
	case MATERIAL_VAR_TYPE_FLOAT:
	default:
		break;
	}
}


//-----------------------------------------------------------------------------
// name	+ type
//-----------------------------------------------------------------------------
MaterialVarSym_t CMaterialVar::GetNameAsSymbol() const
{
	return m_Name;
}

const char *CMaterialVar::GetName( ) const
{
	if( !m_Name.IsValid() )
	{
		Warning( "m_pName is NULL for CMaterialVar\n" );
		return "";
	}
	return s_MaterialVarSymbols.String( m_Name );
}


//-----------------------------------------------------------------------------
// Thread-safe versions
//-----------------------------------------------------------------------------
int	CMaterialVar::GetIntValueInternal( void ) const
{
	CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
	if ( pCallQueue && !m_bFakeMaterialVar )
	{
		if ( !s_bEnableThreadedAccess )
		{
			//DevMsg( 2, "Non-thread safe call to CMaterialVar %s!\n", GetName() );
		}

		if ( m_nTempIndex != 0xFF )
			return s_pTempMaterialVar[m_nTempIndex].GetIntValueInternal();
	}

	// Set methods for float and vector update this
	return m_intVal;
}

float CMaterialVar::GetFloatValueInternal( void ) const
{
	CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
	if ( pCallQueue && !m_bFakeMaterialVar )
	{
		if ( !s_bEnableThreadedAccess )
		{
			//DevMsg( 2, "Non-thread safe call to CMaterialVar %s!\n", GetName() );
		}

		if ( m_nTempIndex != 0xFF )
			return s_pTempMaterialVar[m_nTempIndex].GetFloatValueInternal();
	}

	return m_VecVal[0];
}

float const* CMaterialVar::GetVecValueInternal( ) const
{
	CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
	if ( pCallQueue && !m_bFakeMaterialVar )
	{
		if ( !s_bEnableThreadedAccess )
		{
			//DevMsg( 2, "Non-thread safe call to CMaterialVar %s!\n", GetName() );
		}

		if ( m_nTempIndex != 0xFF )
			return s_pTempMaterialVar[m_nTempIndex].GetVecValueInternal();
	}

	return m_VecVal.Base();
}

void CMaterialVar::GetVecValueInternal( float *val, int numcomps ) const
{
	CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
	if ( pCallQueue && !m_bFakeMaterialVar )
	{
		if ( !s_bEnableThreadedAccess )
		{
			//DevMsg( 2, "Non-thread safe call to CMaterialVar %s!\n", GetName() );
		}

		if ( m_nTempIndex != 0xFF )
		{
			s_pTempMaterialVar[m_nTempIndex].GetVecValueInternal( val, numcomps );
			return;
		}
	}

	Assert( ( numcomps >0 ) && ( numcomps <= 4 ) );
	for( int i=0 ; i < numcomps; i++ )
	{
		val[i] = m_VecVal[ i ];
	}
}

int	CMaterialVar::VectorSizeInternal() const
{
	CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
	if ( pCallQueue && !m_bFakeMaterialVar )
	{
		if ( !s_bEnableThreadedAccess )
		{
			//DevMsg( 2, "Non-thread safe call to CMaterialVar %s!\n", GetName() );
		}

		if ( m_nTempIndex != 0xFF )
			return s_pTempMaterialVar[m_nTempIndex].VectorSizeInternal( );
	}

	return m_nNumVectorComps;
}

// Don't want to be grabbing the dummy var and changing it's value.  That usually means badness.
#define ASSERT_NOT_DUMMY_VAR()	AssertMsg( m_bFakeMaterialVar || ( V_stricmp( GetName(), "$dummyvar" ) != 0 ), "TRYING TO MODIFY $dummyvar, WHICH IS BAD, MMMKAY!" )

//-----------------------------------------------------------------------------
// float
//-----------------------------------------------------------------------------
void CMaterialVar::SetFloatValue( float val )
{
	ASSERT_NOT_DUMMY_VAR();
	CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
	if ( !m_bFakeMaterialVar && pCallQueue )
	{
		CMaterialVar *pThreadVar = AllocThreadVar();
		if ( pThreadVar )
		{
			pThreadVar->SetFloatValue( val );
		}
		pCallQueue->QueueCall( this, &CMaterialVar::SetFloatValue, val );
		return;
	}

	// Suppress all this if we're not actually changing anything
	if ((m_Type == MATERIAL_VAR_TYPE_FLOAT) && (m_VecVal[0] == val))
		return;

	// Gotta flush if we've changed state and this is the current material
	if ( !m_bFakeMaterialVar && m_pMaterial && (m_pMaterial == ( IMaterialInternal* )MaterialSystem()->GetCurrentMaterial()))
		g_pShaderAPI->FlushBufferedPrimitives();

	CleanUpData();
	m_VecVal[0] = m_VecVal[1] = m_VecVal[2] = m_VecVal[3] = val;
	m_intVal = (int) val;
	m_Type = MATERIAL_VAR_TYPE_FLOAT;
	VarChanged();
}


//-----------------------------------------------------------------------------
// int
//-----------------------------------------------------------------------------
void CMaterialVar::SetIntValue( int val )
{
	ASSERT_NOT_DUMMY_VAR();
	CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
	if ( !m_bFakeMaterialVar && pCallQueue )
	{
		CMaterialVar *pThreadVar = AllocThreadVar();
		if ( pThreadVar )
		{
			pThreadVar->SetIntValue( val );
		}
		pCallQueue->QueueCall( this, &CMaterialVar::SetIntValue, val );
		return;
	}

	// Suppress all this if we're not actually changing anything
	if ((m_Type == MATERIAL_VAR_TYPE_INT) && (m_intVal == val))
		return;

	// Gotta flush if we've changed state and this is the current material
	if ( !m_bFakeMaterialVar && m_pMaterial && (m_pMaterial == ( IMaterialInternal* )MaterialSystem()->GetCurrentMaterial()))
		g_pShaderAPI->FlushBufferedPrimitives();

	CleanUpData();
	m_intVal = val;
	m_VecVal[0] = m_VecVal[1] = m_VecVal[2] = m_VecVal[3] = (float) val;
	m_Type = MATERIAL_VAR_TYPE_INT;
	VarChanged();
}


//-----------------------------------------------------------------------------
// string
//-----------------------------------------------------------------------------
const char *CMaterialVar::GetStringValue( void ) const
{
	CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
	if ( pCallQueue && !m_bFakeMaterialVar )
	{
		if ( !s_bEnableThreadedAccess )
		{
			//DevMsg( 2, "Non-thread safe call to CMaterialVar %s!\n", GetName() );
		}

		if ( m_nTempIndex != 0xFF )
			return s_pTempMaterialVar[m_nTempIndex].GetStringValue();
	}

	switch( m_Type )
	{
	case MATERIAL_VAR_TYPE_STRING:
		return m_pStringVal;

	case MATERIAL_VAR_TYPE_INT:
		Q_snprintf( s_CharBuf, sizeof( s_CharBuf ), "%d", m_intVal );
		return s_CharBuf;

	case MATERIAL_VAR_TYPE_FLOAT:
		Q_snprintf( s_CharBuf, sizeof( s_CharBuf ), "%f", m_VecVal[0] );
		return s_CharBuf;

	case MATERIAL_VAR_TYPE_VECTOR:
		{
			s_CharBuf[0] = '[';
			s_CharBuf[1] = ' ';
			int len = 2;
			for (int i = 0; i < m_nNumVectorComps; ++i)
			{
				if (len < sizeof( s_CharBuf ))
				{
					Q_snprintf( s_CharBuf + len, sizeof( s_CharBuf ) - len, "%f ", m_VecVal[i] );
					len += strlen( s_CharBuf + len );
				}
			}
			if (len < sizeof( s_CharBuf ) - 1)
			{
				s_CharBuf[len] = ']';
				s_CharBuf[len+1] = '\0';
			}
			else
			{
				s_CharBuf[sizeof( s_CharBuf )-1] = '\0';
			}
			return s_CharBuf;
		}

	case MATERIAL_VAR_TYPE_MATRIX:
 		{
			s_CharBuf[0] = '[';
			s_CharBuf[1] = ' ';
			int len = 2;
			for (int i = 0; i < 4; ++i)
			{
				for (int j = 0; j < 4; ++j)
				{
					if (len < sizeof( s_CharBuf ))
						len += Q_snprintf( s_CharBuf + len, sizeof( s_CharBuf ) - len, "%.3f ", m_pMatrix->m_Matrix[j][i] );
				}
			}
			if (len < sizeof( s_CharBuf ) - 1)
			{
				s_CharBuf[len] = ']';
				s_CharBuf[len+1] = '\0';
			}
			else
			{
				s_CharBuf[sizeof( s_CharBuf )-1] = '\0';
			}
			return s_CharBuf;
		}

	case MATERIAL_VAR_TYPE_TEXTURE:
		Q_snprintf( s_CharBuf, sizeof( s_CharBuf ), "%s", m_pTexture->GetName() );
		return s_CharBuf;
	case MATERIAL_VAR_TYPE_MATERIAL:
		Q_snprintf( s_CharBuf, sizeof( s_CharBuf ), "%s", ( m_pMaterialValue ? m_pMaterialValue->GetName() : "" ) );
		return s_CharBuf;

	case MATERIAL_VAR_TYPE_UNDEFINED:
		return "<UNDEFINED>";

	default:
		Warning( "CMaterialVar::GetStringValue: Unknown material var type\n" );
		return "";
	}
}

void CMaterialVar::SetStringValue( const char *val )
{
	ASSERT_NOT_DUMMY_VAR();
	CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
	if ( !m_bFakeMaterialVar && pCallQueue )
	{
		CMaterialVar *pThreadVar = AllocThreadVar();
		if ( pThreadVar )
		{
			pThreadVar->SetStringValue( val );
		}
		pCallQueue->QueueCall( this, &CMaterialVar::SetStringValue, CUtlEnvelope<const char *>(val) );
		return;
	}

	// Gotta flush if we've changed state and this is the current material
	if ( !m_bFakeMaterialVar && m_pMaterial && (m_pMaterial == ( IMaterialInternal* )MaterialSystem()->GetCurrentMaterial()))
		g_pShaderAPI->FlushBufferedPrimitives();

	CleanUpData();
	int len = Q_strlen( val ) + 1;
	m_pStringVal = new char[len];
	Q_strncpy( m_pStringVal, val, len );
	m_Type = MATERIAL_VAR_TYPE_STRING;
	m_intVal = atoi( val );
	m_VecVal[0] = m_VecVal[1] = m_VecVal[2] = m_VecVal[3] = atof( m_pStringVal );
	VarChanged();
}

void CMaterialVar::SetFourCCValue( FourCC type, void *pData )
{
	ASSERT_NOT_DUMMY_VAR();
	CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
	if ( !m_bFakeMaterialVar && pCallQueue )
	{
		CMaterialVar *pThreadVar = AllocThreadVar();
		if ( pThreadVar )
		{
			pThreadVar->SetFourCCValue( type, pData );
		}
		pCallQueue->QueueCall( this, &CMaterialVar::SetFourCCValue, type, pData );
		return;
	}

	// Suppress all this if we're not actually changing anything
	if ((m_Type == MATERIAL_VAR_TYPE_FOURCC) && m_pFourCC->m_FourCC == type && m_pFourCC->m_pFourCCData == pData )
		return;

	// Gotta flush if we've changed state and this is the current material
	if ( !m_bFakeMaterialVar &&  m_pMaterial && (m_pMaterial == ( IMaterialInternal* )MaterialSystem()->GetCurrentMaterial()))
		g_pShaderAPI->FlushBufferedPrimitives();

	CleanUpData();

	m_pFourCC = AllocFourCC();
	m_pFourCC->m_FourCC = type;
	m_pFourCC->m_pFourCCData = pData;
	m_Type = MATERIAL_VAR_TYPE_FOURCC;
	m_VecVal.Init();
	m_intVal = 0;
	VarChanged();
}

void CMaterialVar::GetFourCCValue( FourCC *type, void **ppData )
{
	CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
	if ( pCallQueue && !m_bFakeMaterialVar )
	{
		if ( !s_bEnableThreadedAccess )
		{
			//DevMsg( 2, "Non-thread safe call to CMaterialVar %s!\n", GetName() );
		}

		if ( m_nTempIndex != 0xFF )
			return s_pTempMaterialVar[m_nTempIndex].GetFourCCValue( type, ppData );
	}

	if( m_Type == MATERIAL_VAR_TYPE_FOURCC )
	{
		*type = m_pFourCC->m_FourCC;
		*ppData = m_pFourCC->m_pFourCCData;
	}
	else
	{
		*type = FOURCC_UNKNOWN;
		*ppData = 0;

		static int bitchCount;
		if( bitchCount < 10 )
		{
			Warning( "CMaterialVar::GetVecValue: trying to get a vec value for %s which is of type %d\n",
				GetName(), ( int )m_Type );
			bitchCount++;
		}
	}
}


//-----------------------------------------------------------------------------
// texture
//-----------------------------------------------------------------------------
ITexture *CMaterialVar::GetTextureValue( void )
{
	CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
	if ( pCallQueue && !m_bFakeMaterialVar )
	{
		if ( !s_bEnableThreadedAccess )
		{
			//DevMsg( 2, "Non-thread safe call to CMaterialVar %s!\n", GetName() );
		}

		if ( m_nTempIndex != 0xFF )
			return s_pTempMaterialVar[m_nTempIndex].GetTextureValue( );
	}

	ITexture *retVal = NULL;
	
	if( m_pMaterial )
	{
		m_pMaterial->Precache();
	}
	
	if( m_Type == MATERIAL_VAR_TYPE_TEXTURE )
	{
		if ( strcmp(m_pTexture->GetName(), "bitch_cubemap") == 0 )
			retVal = MaterialSystem()->GetLocalCubemap();
		else
			retVal = static_cast<ITexture *>( m_pTexture );

		if( !retVal )
		{
			static int bitchCount = 0;
			if( bitchCount < 10 )
			{
				Warning( "Invalid texture value in CMaterialVar::GetTextureValue\n" );
				bitchCount++;
			}
		}
	}
	else
	{
		static int bitchCount = 0;
		if( bitchCount < 10 )
		{
			Warning( "Requesting texture value from var \"%s\" which is "
					  "not a texture value (material: %s)\n", GetName(),
						m_pMaterial ? m_pMaterial->GetName() : "NULL material" );
			bitchCount++;
		}
	}

	if( !retVal )
	{
		retVal = TextureManager()->ErrorTexture();
	}
	return retVal;
}


void CMaterialVar::SetTextureValueQueued( ITexture *texture )
{
	SetTextureValue( texture );

	// Matches IncrementReferenceCount in SetTextureValue
	if ( texture )
		texture->DecrementReferenceCount();

	// Debug
	if ( mat_texture_tracking.GetBool() )
	{
		int iIndex = g_pTextureRefList->Find( texture );
		Assert( iIndex != g_pTextureRefList->InvalidIndex() );
		g_pTextureRefList->Element( iIndex )--;
	}
}

static bool s_bInitTextureRefList = false;

void CMaterialVar::SetTextureValue( ITexture *texture )
{
	if ( !s_bInitTextureRefList )
	{
		g_pTextureRefList->SetLessFunc( DefLessFunc( ITexture* ) );
		s_bInitTextureRefList = true;
	}

	// Avoid the garymcthack in CShaderSystem::LoadCubeMap by ensuring we're not using 
	// the internal env cubemap.
	if ( ThreadInMainThread() )
	{
		ITextureInternal* pTexInternal = assert_cast<ITextureInternal *>( texture );
		TextureManager()->RequestAllMipmaps( pTexInternal );
	}

	ASSERT_NOT_DUMMY_VAR();
	CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
	if ( !m_bFakeMaterialVar && pCallQueue )
	{
		// FIXME (toml): deal with reference count
		CMaterialVar *pThreadVar = AllocThreadVar();
		if ( pThreadVar )
		{
			pThreadVar->SetTextureValue( texture );
		}

		// Matches DecrementReferenceCount in SetTextureValueQueued
		if ( texture )
			texture->IncrementReferenceCount();

		// Debug!
		if ( mat_texture_tracking.GetBool() )
		{
			int iIndex = g_pTextureRefList->Find( texture );
			if ( iIndex == g_pTextureRefList->InvalidIndex() )
			{
				g_pTextureRefList->Insert( texture, 1 );
			}
			else
			{
				g_pTextureRefList->Element( iIndex )++;
			}
		}

		pCallQueue->QueueCall( this, &CMaterialVar::SetTextureValueQueued, texture );
		return;
	}

	ITextureInternal* pTexImp = static_cast<ITextureInternal *>( texture );

	// Suppress all this if we're not actually changing anything
	if ((m_Type == MATERIAL_VAR_TYPE_TEXTURE) && (m_pTexture == pTexImp))
		return;

	// Gotta flush if we've changed state and this is the current material
	if ( !m_bFakeMaterialVar && m_pMaterial && (m_pMaterial == MaterialSystem()->GetCurrentMaterial()))
		g_pShaderAPI->FlushBufferedPrimitives();

	if( pTexImp )
		pTexImp->IncrementReferenceCount();

	CleanUpData();
	m_pTexture = pTexImp;
	m_Type = MATERIAL_VAR_TYPE_TEXTURE;
	m_intVal = 0;
	m_VecVal.Init();
	VarChanged();
}


//-----------------------------------------------------------------------------
// material
//-----------------------------------------------------------------------------
IMaterial *CMaterialVar::GetMaterialValue( void )
{
	CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
	if ( pCallQueue && !m_bFakeMaterialVar )
	{
		if ( !s_bEnableThreadedAccess )
		{
			//DevMsg( 2, "Non-thread safe call to CMaterialVar %s!\n", GetName() );
		}

		if ( m_nTempIndex != 0xFF )
			return s_pTempMaterialVar[m_nTempIndex].GetMaterialValue( );
	}

	IMaterial *retVal = NULL;
	
	if( m_pMaterial )
	{
		m_pMaterial->Precache();
	}
	
	if( m_Type == MATERIAL_VAR_TYPE_MATERIAL )
	{
		retVal = static_cast<IMaterial *>( m_pMaterialValue );
	}
	else
	{
		static int bitchCount = 0;
		if( bitchCount < 10 )
		{
			Warning( "Requesting material value from var \"%s\" which is "
					  "not a material value (material: %s)\n", GetName(),
						m_pMaterial ? m_pMaterial->GetName() : "NULL material" );
			bitchCount++;
		}
	}
	return retVal;
}

void CMaterialVar::SetMaterialValue( IMaterial *pMaterial )
{
	ASSERT_NOT_DUMMY_VAR();
	CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
	if ( !m_bFakeMaterialVar && pCallQueue )
	{
		// FIXME (toml): deal with reference count
		CMaterialVar *pThreadVar = AllocThreadVar();
		if ( pThreadVar )
		{
			pThreadVar->SetMaterialValue( pMaterial );
		}
		pCallQueue->QueueCall( this, &CMaterialVar::SetMaterialValue, pMaterial );
		return;
	}

	//HACKHACK: Only use the realtime material as the material value since converting it every time it's loaded could be forgotten, and chance of game code usage is low
	if( pMaterial )
		pMaterial = ((IMaterialInternal *)pMaterial)->GetRealTimeVersion();

	IMaterialInternal* pMaterialImp = static_cast<IMaterialInternal *>( pMaterial );

	// Suppress all this if we're not actually changing anything
	if ((m_Type == MATERIAL_VAR_TYPE_MATERIAL) && (m_pMaterialValue == pMaterialImp))
		return;

	// Gotta flush if we've changed state and this is the current material
	if ( !m_bFakeMaterialVar && m_pMaterial && (m_pMaterial == MaterialSystem()->GetCurrentMaterial()))
	{
		g_pShaderAPI->FlushBufferedPrimitives();
	}

	if( pMaterialImp != NULL )
	{
		pMaterialImp->IncrementReferenceCount();
	}

	CleanUpData();
	m_pMaterialValue = pMaterialImp;
	m_Type = MATERIAL_VAR_TYPE_MATERIAL;
	m_intVal = 0;
	m_VecVal.Init();
	VarChanged();
}


//-----------------------------------------------------------------------------
// Vector
//-----------------------------------------------------------------------------
void CMaterialVar::GetLinearVecValue( float *pVal, int numComps ) const
{
	CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
	if ( pCallQueue && !m_bFakeMaterialVar )
	{
		if ( !s_bEnableThreadedAccess )
		{
			//DevMsg( 2, "Non-thread safe call to CMaterialVar %s!\n", GetName() );
		}

		if ( m_nTempIndex != 0xFF )
			return s_pTempMaterialVar[m_nTempIndex].GetLinearVecValue( pVal, numComps );
	}

	Assert( numComps <= 4 );

	switch( m_Type )
	{
	case MATERIAL_VAR_TYPE_VECTOR:
		{
			for ( int i = 0; i < numComps; ++i )
			{
				pVal[i] = GammaToLinear( m_VecVal[i] );
			}
		}
		break;

	case MATERIAL_VAR_TYPE_INT:
		{
			for ( int i = 0; i < numComps; ++i )
			{
				pVal[i] = GammaToLinear( m_intVal );
			}
		}
		break;

	case MATERIAL_VAR_TYPE_FLOAT:
		{
			for ( int i = 0; i < numComps; ++i )
			{
				pVal[i] = GammaToLinear( m_VecVal[0] );
			}
		}
		break;

	case MATERIAL_VAR_TYPE_MATRIX:
	case MATERIAL_VAR_TYPE_UNDEFINED:
		{
			for ( int i = 0; i < numComps; ++i )
			{
				pVal[i] = 0.0f;
			}
		}
		break;

	default:
		Warning( "CMaterialVar::GetVecValue: trying to get a vec value for %s which is of type %d\n",
			GetName(), ( int )m_Type );
		break;
	}
}

void CMaterialVar::SetVecValueInternal( const Vector4D &vec, int nComps )
{
	ASSERT_NOT_DUMMY_VAR();
	CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
	if ( !m_bFakeMaterialVar && pCallQueue )
	{
		CMaterialVar *pThreadVar = AllocThreadVar();
		if ( pThreadVar )
		{
			pThreadVar->SetVecValueInternal( vec, nComps );
		}
		pCallQueue->QueueCall( this, &CMaterialVar::SetVecValueInternal, RefToVal( vec ), nComps );
		return;
	}
	// Suppress all this if we're not actually changing anything
	if ((m_Type == MATERIAL_VAR_TYPE_VECTOR ) && (m_VecVal == vec ) )
		return;
	// Gotta flush if we've changed state and this is the current material
	if ( !m_bFakeMaterialVar && m_pMaterial && (m_pMaterial == MaterialSystem()->GetCurrentMaterial()))
		g_pShaderAPI->FlushBufferedPrimitives();

	if ( m_Type != MATERIAL_VAR_TYPE_VECTOR )
	{
		CleanUpData();
		m_Type = MATERIAL_VAR_TYPE_VECTOR;
	}
	Assert( nComps <= 4 );
	m_nNumVectorComps = nComps;
	memcpy( m_VecVal.Base(), vec.Base(), 4 * sizeof(float) );
	m_intVal = ( int ) m_VecVal[0];

#ifdef _DEBUG
	for (int i = m_nNumVectorComps; i < 4; ++i )
		Assert( m_VecVal[i] == 0.0f );
#endif
	VarChanged();
}

void CMaterialVar::SetVecValue( const float* pVal, int numComps )
{
	Vector4D vec;
	memcpy( vec.Base(), pVal, numComps * sizeof(float) );
	for (int i = numComps; i < 4; ++i )
	{
		vec[i] = 0.0f;
	}
	SetVecValueInternal( vec, numComps);
}

void CMaterialVar::SetVecValue( float x, float y )
{
	SetVecValueInternal( Vector4D( x, y, 0.0f, 0.0f ), 2 );
}

void CMaterialVar::SetVecValue( float x, float y, float z )
{
	SetVecValueInternal( Vector4D( x, y, z, 0.0f ), 3 );
}

void CMaterialVar::SetVecValue( float x, float y, float z, float w )
{
	SetVecValueInternal( Vector4D( x, y, z, w ), 4 );
}


void CMaterialVar::SetVecComponentValue( float fVal, int nComponent )
{
	ASSERT_NOT_DUMMY_VAR();

#ifndef _CERT
	// DIAF
	if ( nComponent < 0 || nComponent > 3 )
	{
		Error( "Invalid vector component (%d) of variable %s referenced in material %s", nComponent, GetName(), GetOwningMaterial()->GetName() );
		return;
	}
#endif

	CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
	if ( !m_bFakeMaterialVar && pCallQueue )
	{
		if ( s_bEnableThreadedAccess )
		{
			bool bInit = ( m_nTempIndex == 0xFF ) ? true : false;
			CMaterialVar *pThreadVar = AllocThreadVar();
			if ( pThreadVar )
			{
				if ( bInit )
				{
					pThreadVar->SetVecValue( m_VecVal.Base(), m_nNumVectorComps );
				}
				pThreadVar->SetVecComponentValue( fVal, nComponent );
			}
		}
		pCallQueue->QueueCall( this, &CMaterialVar::SetVecComponentValue, fVal, nComponent );
		return;
	}

	// Suppress all this if we're not actually changing anything
	if ((m_Type == MATERIAL_VAR_TYPE_VECTOR ) && (m_VecVal[nComponent] == fVal ) )
		return;

	// Gotta flush if we've changed state and this is the current material
	if ( !m_bFakeMaterialVar && m_pMaterial && (m_pMaterial == MaterialSystem()->GetCurrentMaterial()))
		g_pShaderAPI->FlushBufferedPrimitives();

	if ( m_Type != MATERIAL_VAR_TYPE_VECTOR )
	{
		CleanUpData();
		m_Type = MATERIAL_VAR_TYPE_VECTOR;
	}
	Assert( nComponent <= 3 );

	if( m_nNumVectorComps < nComponent )
	{
		//reset all undefined components to 0
		for( int i = m_nNumVectorComps; i != nComponent; ++i )
			m_VecVal[i] = 0.0f; 

		m_nNumVectorComps = nComponent;
	}

	m_VecVal[nComponent] = fVal;

#ifdef _DEBUG
	for (int i = m_nNumVectorComps; i < 4; ++i )
		Assert( m_VecVal[i] == 0.0f );
#endif
	VarChanged();
}


//-----------------------------------------------------------------------------
// Matrix 
//-----------------------------------------------------------------------------
VMatrix const& CMaterialVar::GetMatrixValue( )
{
	CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
	if ( pCallQueue && !m_bFakeMaterialVar )
	{
		if ( !s_bEnableThreadedAccess )
		{
			//DevMsg( 2, "Non-thread safe call to CMaterialVar %s!\n", GetName() );
		}

		if ( m_nTempIndex != 0xFF )
			return s_pTempMaterialVar[m_nTempIndex].GetMatrixValue();
	}

	if (m_Type == MATERIAL_VAR_TYPE_MATRIX)
		return m_pMatrix->m_Matrix;

	static VMatrix identity( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 );
	return identity;
}

void CMaterialVar::SetMatrixValue( VMatrix const& matrix )
{
	ASSERT_NOT_DUMMY_VAR();
	CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
	if ( !m_bFakeMaterialVar && pCallQueue )
	{
		CMaterialVar *pThreadVar = AllocThreadVar();
		if ( pThreadVar )
		{
			pThreadVar->SetMatrixValue( matrix );
		}
		pCallQueue->QueueCall( this, &CMaterialVar::SetMatrixValue, RefToVal( matrix ) );
		return;
	}

	// Gotta flush if we've changed state and this is the current material
	if ( !m_bFakeMaterialVar && m_pMaterial && (m_pMaterial == MaterialSystem()->GetCurrentMaterial()))
		g_pShaderAPI->FlushBufferedPrimitives();

	CleanUpData();

	// NOTE: This is necessary because the mempool MaterialVarMatrix_t uses is not threadsafe
	m_pMatrix = new MaterialVarMatrix_t;

	MatrixCopy( matrix, m_pMatrix->m_Matrix );
	m_Type = MATERIAL_VAR_TYPE_MATRIX;
	m_pMatrix->m_bIsIdent = matrix.IsIdentity();
	m_VecVal.Init();
	m_intVal = ( int ) m_VecVal[0];
	VarChanged();
}

bool CMaterialVar::MatrixIsIdentity( void ) const
{
	if( m_Type != MATERIAL_VAR_TYPE_MATRIX ) 
	{
		return true;
	}
	return m_pMatrix->m_bIsIdent;
}

//-----------------------------------------------------------------------------
// Undefined 
//-----------------------------------------------------------------------------
bool CMaterialVar::IsDefined() const
{
	return m_Type != MATERIAL_VAR_TYPE_UNDEFINED;
}

void CMaterialVar::SetUndefined()
{
	ASSERT_NOT_DUMMY_VAR();
	CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
	if ( !m_bFakeMaterialVar && pCallQueue )
	{
		CMaterialVar *pThreadVar = AllocThreadVar();
		if ( pThreadVar )
		{
			pThreadVar->SetUndefined( );
		}
		pCallQueue->QueueCall( this, &CMaterialVar::SetUndefined );
		return;
	}

	if (m_Type == MATERIAL_VAR_TYPE_UNDEFINED)
		return;

	// Gotta flush if we've changed state and this is the current material
	if ( !m_bFakeMaterialVar && m_pMaterial && (m_pMaterial == MaterialSystem()->GetCurrentMaterial()))
		g_pShaderAPI->FlushBufferedPrimitives();

	CleanUpData();
	m_Type = MATERIAL_VAR_TYPE_UNDEFINED;
	VarChanged();
}


//-----------------------------------------------------------------------------
// Copy from another material var 
//-----------------------------------------------------------------------------
void CMaterialVar::CopyFrom( IMaterialVar *pMaterialVar )
{
	CMatCallQueue *pCallQueue = MaterialSystem()->GetRenderCallQueue();
	if ( !m_bFakeMaterialVar && pCallQueue )
	{
		CMaterialVar *pThreadVar = AllocThreadVar();
		if ( pThreadVar )
		{
			pThreadVar->CopyFrom( pMaterialVar );
		}
		pCallQueue->QueueCall( this, &CMaterialVar::CopyFrom, pMaterialVar );
		return;
	}

	switch( pMaterialVar->GetType() )
	{
	case MATERIAL_VAR_TYPE_FLOAT:
		SetFloatValue( pMaterialVar->GetFloatValue() );
		break;

	case MATERIAL_VAR_TYPE_STRING:
		SetStringValue( pMaterialVar->GetStringValue() );
		break;

	case MATERIAL_VAR_TYPE_VECTOR:
		SetVecValue( pMaterialVar->GetVecValue(), pMaterialVar->VectorSize() );
		break;

	case MATERIAL_VAR_TYPE_TEXTURE:
		SetTextureValue( pMaterialVar->GetTextureValue() );
		break;

	case MATERIAL_VAR_TYPE_INT:
		SetIntValue( pMaterialVar->GetIntValue() );
		break;

	case MATERIAL_VAR_TYPE_FOURCC:
		{
			FourCC fourCC;
			void *pData;
			pMaterialVar->GetFourCCValue( &fourCC, &pData );
			SetFourCCValue( fourCC, pData );
		}
		break;

	case MATERIAL_VAR_TYPE_UNDEFINED:
		SetUndefined();
		break;

	case MATERIAL_VAR_TYPE_MATRIX:
		SetMatrixValue( pMaterialVar->GetMatrixValue() );
		break;

	case MATERIAL_VAR_TYPE_MATERIAL:
		SetMaterialValue( pMaterialVar->GetMaterialValue() );
		break;

	default:
		Assert(0);
	}
}

//-----------------------------------------------------------------------------
// Parser utilities
//-----------------------------------------------------------------------------
static inline bool IsWhitespace( char c )
{
	return c == ' ' || c == '\t';
}

static inline bool IsEndline( char c )
{
	return c == '\n' || c == '\0';
}

static inline bool IsVector( const char* v )
{
	while (IsWhitespace(*v))
	{
		++v;
		if (IsEndline(*v))
			return false;
	}
	return *v == '[' || *v == '{';
}

//-----------------------------------------------------------------------------
// Creates a vector material var
//-----------------------------------------------------------------------------
static int ParseVectorFromKeyValueString( const char *pString, float vecVal[4] )
{
	const char* pScan = pString;
	bool divideBy255 = false;

	// skip whitespace
	while( IsWhitespace(*pScan) )
	{
		++pScan;
	}

	if( *pScan == '{' )
	{
		divideBy255 = true;
	}
	else
	{
		Assert( *pScan == '[' );
	}
	
	// skip the '['
	++pScan;
	int i;
	for( i = 0; i < 4; i++ )
	{
		// skip whitespace
		while( IsWhitespace(*pScan) )
		{
			++pScan;
		}

		if( IsEndline(*pScan) || *pScan == ']' || *pScan == '}' )
		{
			if (*pScan != ']' && *pScan != '}')
			{
				Warning( "no ']' or '}' found in vector key in ParseVectorFromKeyValueString\n" );
			}

			// allow for vec2's, etc.
			vecVal[i] = 0.0f;
			break;
		}

		char* pEnd;

		vecVal[i] = strtod( pScan, &pEnd );
		if (pScan == pEnd)
		{
			Warning( "error parsing vector element in ParseVectorFromKeyValueString\n" );
			return 0;
		}

		pScan = pEnd;
	}

	if( divideBy255 )
	{
		vecVal[0] *= ( 1.0f / 255.0f );
		vecVal[1] *= ( 1.0f / 255.0f );
		vecVal[2] *= ( 1.0f / 255.0f );
		vecVal[3] *= ( 1.0f / 255.0f );
	}

	return i;
}

void CMaterialVar::SetValueAutodetectType( const char *val )
{
	ASSERT_NOT_DUMMY_VAR();
	int len = Q_strlen( val );

	// Here, let's determine if we got a float or an int....
	char* pIEnd;	// pos where int scan ended
	char* pFEnd;	// pos where float scan ended
	const char* pSEnd = val + len ; // pos where token ends

	int ival = strtol( val, &pIEnd, 10 );
	float fval = (float)strtod( val, &pFEnd );

	if ( ( pFEnd > pIEnd ) && ( pFEnd == pSEnd ) )
	{
		SetFloatValue( fval );
		return;
	}
	
	if ( pIEnd == pSEnd )
	{
		SetIntValue( ival );
		return;
	}

	// Isn't an int or a float.

	// Is it a matrix?

	VMatrix mat;
	int count = sscanf( val, " [ %f %f %f %f  %f %f %f %f  %f %f %f %f  %f %f %f %f ]",
		&mat.m[0][0], &mat.m[0][1], &mat.m[0][2], &mat.m[0][3],
		&mat.m[1][0], &mat.m[1][1], &mat.m[1][2], &mat.m[1][3],
		&mat.m[2][0], &mat.m[2][1], &mat.m[2][2], &mat.m[2][3],
		&mat.m[3][0], &mat.m[3][1], &mat.m[3][2], &mat.m[3][3] );
	if (count == 16)
	{
		SetMatrixValue( mat );
		return;
	}

	Vector2D scale, center;
	float angle;
	Vector2D translation;
	count = sscanf( val, " center %f %f scale %f %f rotate %f translate %f %f",
		&center.x, &center.y, &scale.x, &scale.y, &angle, &translation.x, &translation.y );
	if (count == 7)
	{
		VMatrix temp;
		MatrixBuildTranslation( mat, -center.x, -center.y, 0.0f );
		MatrixBuildScale( temp, scale.x, scale.y, 1.0f );
		MatrixMultiply( temp, mat, mat );
		MatrixBuildRotateZ( temp, angle );
		MatrixMultiply( temp, mat, mat );
		MatrixBuildTranslation( temp, center.x + translation.x, center.y + translation.y, 0.0f );
		MatrixMultiply( temp, mat, mat );
		SetMatrixValue( mat );
		return;
	}

	if( IsVector( val ) )
	{
		float vecVal[4];
		int nDim = ParseVectorFromKeyValueString( val, vecVal );
		if ( nDim > 0 )
		{
			SetVecValue( vecVal, nDim );
			return;
		}
	}

	SetStringValue( val );
}