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

#ifndef CMATERIALDICT_H
#define CMATERIALDICT_H

#include "tier1/utlsymbol.h"
#include "tier1/utlrbtree.h"

#ifndef MATSYS_INTERNAL
#error "This file is private to the implementation of IMaterialSystem/IMaterialSystemInternal"
#endif

#if defined( _WIN32 )
#pragma once
#endif

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

class IMaterial;
class IMaterialInternal;
typedef unsigned short MaterialHandle_t;

//-----------------------------------------------------------------------------
// Dictionary of all known materials
//-----------------------------------------------------------------------------

class CMaterialDict
{
public:
	CMaterialDict() :
		m_MaterialDict( 0, 256, MaterialLessFunc ),
		m_MissingList( 0, 32, MissingMaterialLessFunc )
	{
		Assert( ThreadInMainThread() );
	}

	void Shutdown();

	int					GetNumMaterials( void ) const;

	IMaterial*			GetMaterial( MaterialHandle_t h ) const;
	IMaterialInternal * GetMaterialInternal( MaterialHandle_t idx ) const;

	MaterialHandle_t	FirstMaterial() const;
	MaterialHandle_t	NextMaterial( MaterialHandle_t h ) const;
	MaterialHandle_t	InvalidMaterial() const;

	IMaterialInternal *	FindMaterial( const char *pszName, bool bManuallyCreated ) const;

	void				AddMaterialToMaterialList( IMaterialInternal *pMaterial );
	void				RemoveMaterialFromMaterialList( IMaterialInternal *pMaterial );

	void				RemoveMaterial( IMaterialInternal *pMaterial );
	void				RemoveMaterialSubRect( IMaterialInternal *pMaterial );

	IMaterialInternal*	AddMaterial( const char* pName, const char *pTextureGroupName );
	// pKeyValues and pPatchKeyValues should come from LoadVMTFile()
	IMaterialInternal*  AddMaterialSubRect( const char* pName, const char *pTextureGroupName, KeyValues *pKeyValues, KeyValues *pPatchKeyValues ); 

	bool				NoteMissing( const char *pszName );

protected: /*private:*/
	void				RemoveAllMaterials();
	void				RemoveAllMaterialsFromMaterialList();
	void				RemoveMaterialFromMaterialList( MaterialHandle_t h );


	// Stores a dictionary of materials, searched by name
	struct MaterialLookup_t
	{
		IMaterialInternal* m_pMaterial;
		CUtlSymbol m_Name;
		bool m_bManuallyCreated;
	};

	// Stores a dictionary of missing materials to cut down on redundant warning messages
	// TODO:  1) Could add a counter
	//        2) Could dump to file/console at exit for exact list of missing materials
	struct MissingMaterial_t
	{
		CUtlSymbol	m_Name;
	};

	static bool MaterialLessFunc( const MaterialLookup_t& src1, 
		const MaterialLookup_t& src2 );

	static bool MissingMaterialLessFunc( const MissingMaterial_t& src1, 
		const MissingMaterial_t& src2 );

	CUtlRBTree< MaterialLookup_t, MaterialHandle_t > m_MaterialDict;
	CUtlRBTree< MissingMaterial_t, int > m_MissingList;
};

//-----------------------------------------------------------------------------
// Material iteration methods
//-----------------------------------------------------------------------------
inline MaterialHandle_t CMaterialDict::FirstMaterial() const
{
	Assert( ThreadInMainThread() );
	return m_MaterialDict.FirstInorder();
}

inline MaterialHandle_t CMaterialDict::NextMaterial( MaterialHandle_t h ) const
{
	Assert( ThreadInMainThread() );
	return m_MaterialDict.NextInorder(h);
}

inline int CMaterialDict::GetNumMaterials( )	const
{
	Assert( ThreadInMainThread() );
	return m_MaterialDict.Count();
}


//-----------------------------------------------------------------------------
// Invalid index handle....
//-----------------------------------------------------------------------------
inline MaterialHandle_t CMaterialDict::InvalidMaterial() const
{
	Assert( ThreadInMainThread() );
	return m_MaterialDict.InvalidIndex();
}


//-----------------------------------------------------------------------------
// Handle to material
//-----------------------------------------------------------------------------
inline IMaterial* CMaterialDict::GetMaterial( MaterialHandle_t idx ) const
{
	Assert( ThreadInMainThread() );
	return m_MaterialDict[idx].m_pMaterial;
}

inline IMaterialInternal* CMaterialDict::GetMaterialInternal( MaterialHandle_t idx ) const
{
	Assert( ThreadInMainThread() );
	Assert( (m_MaterialDict[idx].m_pMaterial == NULL) || m_MaterialDict[idx].m_pMaterial->IsRealTimeVersion() );	
	return m_MaterialDict[idx].m_pMaterial;
}

inline IMaterialInternal* CMaterialDict::FindMaterial( const char *pszName, bool bManuallyCreated ) const
{
	Assert( ThreadInMainThread() );
	MaterialLookup_t lookup;
	lookup.m_Name = pszName;
	lookup.m_bManuallyCreated = bManuallyCreated;	// This causes the search to find only file-created materials

	MaterialHandle_t h = m_MaterialDict.Find( lookup );

	if ( h != m_MaterialDict.InvalidIndex() )
	{
		return m_MaterialDict[h].m_pMaterial;
	}

	return NULL;
}

inline bool CMaterialDict::NoteMissing( const char *pszName )
{
	Assert( ThreadInMainThread() );
	MissingMaterial_t missing;
	missing.m_Name = pszName;
	if ( m_MissingList.Find( missing ) != m_MissingList.InvalidIndex() )
	{
		return false;
	}

	m_MissingList.Insert( missing );
	return true;
}

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

#endif // CMATERIALDICT_H