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

#include "pch_materialsystem.h"

#define MATSYS_INTERNAL

#include "cmaterialdict.h"

#include "materialsystem_global.h"
#include "filesystem.h"
#include "imaterialinternal.h"

// NOTE: This must be the last file included!!!
#include "tier0/memdbgon.h"

//-----------------------------------------------------------------------------
// sort function
//-----------------------------------------------------------------------------
bool CMaterialDict::MaterialLessFunc( const MaterialLookup_t& src1, 
										   const MaterialLookup_t& src2 )
{
	Assert( ThreadInMainThread() );
	// Always sort manually-created materials to the front
	if ( src1.m_bManuallyCreated != src2.m_bManuallyCreated )
		return src1.m_bManuallyCreated;

	return src1.m_Name < src2.m_Name;
}

//-----------------------------------------------------------------------------
// sort function for missing materials
//-----------------------------------------------------------------------------
bool CMaterialDict::MissingMaterialLessFunc( const MissingMaterial_t& src1, 
											const MissingMaterial_t& src2 )
{
	Assert( ThreadInMainThread() );
	return src1.m_Name < src2.m_Name;
}

void CMaterialDict::Shutdown( )
{
	Assert( ThreadInMainThread() );
	// Clean up all materials..
	RemoveAllMaterials();

	// FIXME: Could dump list here...
	if ( m_MissingList.Count() )
		DevMsg( "%s m_MissingList count: %d\n", __FUNCTION__, m_MissingList.Count() );
	m_MissingList.RemoveAll();
}

//-----------------------------------------------------------------------------
// Adds/removes the material to the list of all materials
//-----------------------------------------------------------------------------
void CMaterialDict::AddMaterialToMaterialList( IMaterialInternal *pMaterial )
{
	Assert( ThreadInMainThread() );
	MaterialLookup_t lookup;
	lookup.m_pMaterial = pMaterial;
	lookup.m_Name = pMaterial->GetName();
	lookup.m_bManuallyCreated = pMaterial->IsManuallyCreated();

	m_MaterialDict.Insert( lookup );
}

void CMaterialDict::RemoveMaterialFromMaterialList( IMaterialInternal *pMaterial )
{
	Assert( ThreadInMainThread() );
	// Gotta iterate over this manually; name-based lookup is bogus if there are two
	// materials with the same name, which can happen for procedural materials
	// First remove all the subrect materials, because they'll point at their material pages.
	MaterialHandle_t i;
	MaterialHandle_t iNext = InvalidMaterial();
	for (i = FirstMaterial(); i != InvalidMaterial(); i = iNext )
	{
		iNext = NextMaterial(i);
		if ( m_MaterialDict[i].m_pMaterial == pMaterial )
		{
			m_MaterialDict.RemoveAt( i );
			break;
		}
	}

	Assert( i != InvalidMaterial() );

#ifdef _DEBUG
	for ( i = iNext; i != InvalidMaterial(); i = NextMaterial(i) )
	{
		Assert( m_MaterialDict[i].m_pMaterial != pMaterial );
	}
#endif
}


//-----------------------------------------------------------------------------
// Adds, removes materials
//-----------------------------------------------------------------------------
IMaterialInternal* CMaterialDict::AddMaterial( char const* pName, const char *pTextureGroupName )
{
	Assert( ThreadInMainThread() );
	IMaterialInternal *pMaterial = IMaterialInternal::CreateMaterial( pName, pTextureGroupName );
	Assert( pMaterial && pMaterial->IsRealTimeVersion() );
	AddMaterialToMaterialList( pMaterial );
	return pMaterial;
}

void CMaterialDict::RemoveMaterial( IMaterialInternal* pMaterial )
{
	Assert( ThreadInMainThread() );
	Assert( (pMaterial == NULL) || pMaterial->IsRealTimeVersion() );
	RemoveMaterialFromMaterialList( pMaterial );
}

IMaterialInternal *CMaterialDict::AddMaterialSubRect( const char *pName, const char *pTextureGroupName, KeyValues *pKeyValues, KeyValues *pPatchKeyValues )
{
	Assert( ThreadInMainThread() );
	IMaterialInternal *pMaterial = IMaterialInternal::CreateMaterialSubRect( pName, pTextureGroupName, pKeyValues, pPatchKeyValues, true );
	Assert( pMaterial );
	AddMaterialToMaterialList( pMaterial );
	return pMaterial;
}

void CMaterialDict::RemoveMaterialSubRect( IMaterialInternal *pMaterial )
{
	Assert( ThreadInMainThread() );
	RemoveMaterialFromMaterialList( pMaterial );
	IMaterialInternal::DestroyMaterialSubRect( pMaterial );
}

void CMaterialDict::RemoveMaterialFromMaterialList( MaterialHandle_t h )
{
	Assert( ThreadInMainThread() );
	m_MaterialDict.RemoveAt( h );
}

void CMaterialDict::RemoveAllMaterialsFromMaterialList()
{
	Assert( ThreadInMainThread() );
	m_MaterialDict.RemoveAll();
}

void CMaterialDict::RemoveAllMaterials()
{
	Assert( ThreadInMainThread() );

	// First remove all the subrect materials, because they'll point at their material pages.
	MaterialHandle_t i, iNext;
	for (i = FirstMaterial(); i != InvalidMaterial(); i = iNext )
	{
		iNext = NextMaterial(i);
		IMaterialInternal *pMaterial = GetMaterialInternal( i );
		if ( pMaterial->InMaterialPage() )
		{
			IMaterialInternal::DestroyMaterialSubRect( pMaterial );
			RemoveMaterialFromMaterialList( i );
		}
	}

	// Now get rid of the rest of the materials, including the pages.
	for (i = FirstMaterial(); i != InvalidMaterial(); i = NextMaterial(i) )
	{
		IMaterialInternal::DestroyMaterial( GetMaterialInternal( i ) );
	}

	RemoveAllMaterialsFromMaterialList();
}