//========= Copyright © 1996-2001, Valve LLC, All rights reserved. ============
//
// Purpose: Implementation of IEditorTexture interface for materials.
//
// $Header: $
// $NoKeywords: $
//=============================================================================

#include "stdafx.h"
#include <process.h>
#include <afxtempl.h>
#include <io.h>
#include <sys\stat.h>
#include <fcntl.h>
#include "Worldcraft.h"
#include "MapDoc.h"
#include "Material.h"
#include "GameData.h"
#include "Options.h"
#include "MainFrm.h"
#include "GlobalFunctions.h"
#include "WADTypes.h"
#include "BSPFile.h"
#include "materialsystem/IMaterialSystem.h"
#include "materialsystem/IMaterialSystemHardwareConfig.h"
#include "materialsystem/ITexture.h"
#include "materialsystem/IMaterial.h"
#include "ImageLoader.h" // hack : don't want to include this just for ImageFormat
#include "FileSystem.h"
#include "FileSystem_WC.h"
#include "StudioModel.h"
#include "materialsystem/MaterialSystem_Config.h"
#include "vstdlib/strtools.h"
#include "tier0/dbg.h"
#include "TextureSystem.h"
#include "materialproxyfactory_wc.h"

#pragma warning(disable:4244)

#define _GraphicCacheAllocate(n)	malloc(n)


static HINSTANCE materialSystemDLLHInst = NULL;
IMaterialSystem *g_pMaterialSystemInterface = NULL;
IMaterialSystemHardwareConfig *g_pMaterialSystemHardwareConfig = NULL;
CreateInterfaceFn g_MaterialSystemFactory = NULL;
CreateInterfaceFn g_MaterialSystemClientFactory = NULL;
MaterialSystem_Config_t g_materialSystemConfig;
static bool g_bMaterialSystemLoaded = false;
static MaterialHandle_t g_CurrMaterial;

extern void ScaleBitmap(CSize sizeSrc, CSize sizeDest, char *src, char *dest);


struct MaterialCacheEntry_t
{
	char szName[MAX_PATH];		//
	CMaterial *pMaterial;		//
	int nRefCount;				//
};


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
static void LoadMaterialSystemInterface( void )
{
    char dllName[1024];
    
	materialSystemDLLHInst = LoadLibrary( "materialsystem.dll" );
	if ( !materialSystemDLLHInst )
	{
		Msg( mwError,"Can't load MaterialSystem.dll" );
        return;
	}

	GetModuleFileName(materialSystemDLLHInst, dllName, sizeof(dllName));
	
	g_MaterialSystemFactory = Sys_GetFactory( "materialsystem.dll" );
	if ( g_MaterialSystemFactory )
	{
		g_pMaterialSystemInterface = (IMaterialSystem *)g_MaterialSystemFactory( MATERIAL_SYSTEM_INTERFACE_VERSION, NULL );
		if ( !g_pMaterialSystemInterface )
		{
			Msg( mwError, "Could not get the material system interface from %s", dllName );
			return;
		}
	}
	else
	{
		Msg( mwError, "Could not find factory interface in library %s", dllName );
		return;
	}
	
    g_bMaterialSystemLoaded = true;
}


//-----------------------------------------------------------------------------
// Purpose: stuff for caching textures in memory.
//-----------------------------------------------------------------------------
class CMaterialImageCache
{
	public:

		CMaterialImageCache(int maxNumGraphicsLoaded);
		~CMaterialImageCache(void);
		void EnCache( CMaterial *pMaterial );

	protected:

		CMaterial **pool;
		int cacheSize;
		int currentID;  // next one to get killed.
};


//-----------------------------------------------------------------------------
// Purpose: Constructor. Allocates a pool of material pointers.
// Input  : maxNumGraphicsLoaded - 
//-----------------------------------------------------------------------------
CMaterialImageCache::CMaterialImageCache(int maxNumGraphicsLoaded)
{
	cacheSize = maxNumGraphicsLoaded;
	pool = new CMaterialPtr[cacheSize];
	if (pool != NULL)
	{
		memset(pool, 0, sizeof(CMaterialPtr) * cacheSize);
	}
	currentID = 0;
}


//-----------------------------------------------------------------------------
// Purpose: Destructor. Frees the pool memory.
//-----------------------------------------------------------------------------
CMaterialImageCache::~CMaterialImageCache(void)
{
	if (pool != NULL)
	{
		delete [] pool;
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pMaterial - 
//-----------------------------------------------------------------------------
void CMaterialImageCache::EnCache( CMaterial *pMaterial )
{
	if (pMaterial->m_pData != NULL)
	{
		// Already cached.
		return;
	}

	// kill currentID
	if ((pool[currentID]) && (pool[currentID]->HasData()))
	{
		pool[currentID]->FreeData();
	}

	pool[currentID] = pMaterial;
	pMaterial->LoadMaterialImage();
	currentID = ( currentID + 1 ) % cacheSize;

#if 0
	OutputDebugString( "CMaterialCache::Encache: " );
	OutputDebugString( pMaterial->m_szName );
	OutputDebugString( "\n" );
#endif
}


static CMaterialImageCache *g_pMaterialImageCache = NULL;


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CMaterialCache::CMaterialCache(void)
{
	m_pCache = NULL;
	m_nMaxEntries = 0;
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CMaterialCache::~CMaterialCache(void)
{
	if (m_pCache != NULL)
	{
		delete m_pCache;
	}
}


//-----------------------------------------------------------------------------
// Purpose: Allocates cache memory for a given number of materials.
// Input  : nMaxEntries - Maximum number of materials in the cache.
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CMaterialCache::Create(int nMaxEntries)
{
	Assert((m_pCache == NULL) && (m_nMaxEntries == 0));

	if (m_pCache != NULL)
	{
		delete m_pCache;
		m_pCache = NULL;
		m_nMaxEntries = 0;
	}

	if (nMaxEntries <= 0)
	{
		nMaxEntries = 500;
	}

	m_pCache = new MaterialCacheEntry_t[nMaxEntries];

	if (m_pCache != NULL)
	{
		memset(m_pCache, 0, sizeof(m_pCache[0]) * nMaxEntries);
		m_nMaxEntries = nMaxEntries;
	}

	return(m_pCache != NULL);
}


//-----------------------------------------------------------------------------
// Purpose: Factory. Creates a material by name, first looking in the cache.
// Input  : pszMaterialName - Name of material, ie "brick/brickfloor01".
// Output : Returns a pointer to the new material object, NULL if the given
//			material did not exist.
//-----------------------------------------------------------------------------
CMaterial *CMaterialCache::CreateMaterial(const char *pszMaterialName)
{
	CMaterial *pMaterial = NULL;

	if (pszMaterialName != NULL)
	{
		//
		// Find this material in the cache. If it is here, return it.
		//
		pMaterial = FindMaterial(pszMaterialName);
		if (pMaterial == NULL)
		{
			//
			// Not found in the cache, try to create it.
			//
			pMaterial = CMaterial::CreateMaterial(pszMaterialName, true);
			if (pMaterial != NULL)
			{
				//
				// Success. Add the newly created material to the cache.
				//
				AddMaterial(pMaterial);
				return(pMaterial);
			}
		}
		else
		{
			//
			// Found in the cache, bump the reference count.
			//
			AddRef(pMaterial);
		}
	}

	return(pMaterial);
}


//-----------------------------------------------------------------------------
// Purpose: Finds a material in the cache.
// Input  : char *pszMaterialName - 
// Output : CMaterial
//-----------------------------------------------------------------------------
void CMaterialCache::AddMaterial(CMaterial *pMaterial)
{
	if (pMaterial != NULL)
	{
		Assert(m_nEntries < m_nMaxEntries);

		if (m_nEntries < m_nMaxEntries)
		{
			m_pCache[m_nEntries].pMaterial = pMaterial;
			m_pCache[m_nEntries].nRefCount = 1;
			m_nEntries++;
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: Increments the reference count on a material in the cache. Called by
//			client code when a pointer to the model is copied, making that
//			reference independent.
// Input  : pModel - Model for which to increment the reference count.
//-----------------------------------------------------------------------------
void CMaterialCache::AddRef(CMaterial *pMaterial)
{
	for (int i = 0; i < m_nEntries; i++)
	{
		if (m_pCache[i].pMaterial == pMaterial)
		{
			m_pCache[i].nRefCount++;
			return;
		}
	}	
}


//-----------------------------------------------------------------------------
// Purpose: Finds a material in the cache by name.
// Input  : char *pszMaterialName - 
// Output : CMaterial
//-----------------------------------------------------------------------------
CMaterial *CMaterialCache::FindMaterial(const char *pszMaterialName)
{
	if (pszMaterialName != NULL)
	{
		for (int i = 0; i < m_nEntries; i++)
		{
			if (!stricmp(m_pCache[i].pMaterial->GetName(), pszMaterialName))
			{
				return(m_pCache[i].pMaterial);
			}
		}
	}

	return(NULL);
}


//-----------------------------------------------------------------------------
// Purpose: Decrements the reference count of a material, deleting it and
//			removing it from the cache if its reference count becomes zero.
// Input  : pMaterial - Material to release.
//-----------------------------------------------------------------------------
void CMaterialCache::Release(CMaterial *pMaterial)
{
	if (pMaterial != NULL)
	{
		for (int i = 0; i < m_nEntries; i++)
		{
			if (m_pCache[i].pMaterial == pMaterial)
			{
				m_pCache[i].nRefCount--;
				if (m_pCache[i].nRefCount == 0)
				{
					delete m_pCache[i].pMaterial;

					m_nEntries--;
					m_pCache[i] = m_pCache[m_nEntries];

					memset(&m_pCache[m_nEntries], 0, sizeof(m_pCache[0]));
				}
	
				break;
			}
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: Constructor. Initializes data members.
//-----------------------------------------------------------------------------
CMaterial::CMaterial(void)
{
	memset(m_szName, 0, sizeof(m_szName));
	memset(m_szFileName, 0, sizeof(m_szFileName));
	memset(m_szKeywords, 0, sizeof(m_szKeywords));

	m_nWidth = 0;
	m_nHeight = 0;
    m_nTextureID = 0;
	m_pData = NULL;
	m_bLoaded = false;
	m_pMaterial = NULL;
	m_TranslucentBaseTexture = false;
}


//-----------------------------------------------------------------------------
// Purpose: Destructor. Frees texture image data and palette.
//-----------------------------------------------------------------------------
CMaterial::~CMaterial(void)
{
	//
	// Free image data.
	//
	if (m_pData != NULL)
	{
		free(m_pData);
		m_pData = NULL;
	}

	/* FIXME: Texture manager shuts down after the material system
	if (m_pMaterial)
	{
		m_pMaterial->DecrementReferenceCount();
		m_pMaterial = NULL;
	}
	*/
}


#define MATERIAL_PREFIX_LEN	10
//-----------------------------------------------------------------------------
// Finds all .VMT files in a particular directory
//-----------------------------------------------------------------------------
bool CMaterial::LoadMaterialsInDirectory( char const* pDirectoryName, int nDirectoryNameLen,
						IMaterialEnumerator *pEnum, int nContext, int nFlags )
{
	Assert( Q_strnicmp( pDirectoryName, "materials", 9 ) == 0 );

	char *pWildCard;
	pWildCard = ( char * )stackalloc( nDirectoryNameLen + 7 );
	Q_snprintf( pWildCard, nDirectoryNameLen + 7, "%s/*.vmt", pDirectoryName );

	if ( !g_pFileSystem )
	{
		return false;
	}

	FileFindHandle_t findHandle;
	const char *pFileName = g_pFileSystem->FindFirst( pWildCard, &findHandle );
	while( pFileName )
	{
		if( !g_pFileSystem->FindIsDirectory( findHandle ) )
		{
			// Strip off the 'materials/' part of the material name.
			char *pFileNameWithPath;
			int nAllocSize = nDirectoryNameLen + Q_strlen(pFileName) + 2;
			pFileNameWithPath = (char *)stackalloc( nAllocSize );
			Q_snprintf(	pFileNameWithPath, nAllocSize, "%s/%s", &pDirectoryName[MATERIAL_PREFIX_LEN], pFileName ); 
			Q_strnlwr( pFileNameWithPath, nAllocSize );

			// Strip off the extension...
			char *pExt = Q_strrchr( pFileNameWithPath, '.');
			if (pExt)
				*pExt = 0;

			if (!pEnum->EnumMaterial( pFileNameWithPath, nContext ))
			{
				return false;
			}
		}
		pFileName = g_pFileSystem->FindNext( findHandle );
	}
	g_pFileSystem->FindClose( findHandle );
	return true;
}


//-----------------------------------------------------------------------------
// Discovers all .VMT files lying under a particular directory
// It only finds their names so we can generate shell materials for them
// that we can load up at a later time 
//-----------------------------------------------------------------------------
bool CMaterial::InitDirectoryRecursive( char const* pDirectoryName, 
						IMaterialEnumerator *pEnum, int nContext, int nFlags )
{
	// Make sure this is an ok directory, otherwise don't bother
	if (ShouldSkipMaterial( pDirectoryName + MATERIAL_PREFIX_LEN, nFlags ))
		return true;

	// Compute directory name length
	int nDirectoryNameLen = Q_strlen( pDirectoryName );

	if (!LoadMaterialsInDirectory( pDirectoryName, nDirectoryNameLen, pEnum, nContext, nFlags ))
		return false;

	char *pWildCard = ( char * )stackalloc( nDirectoryNameLen + 4 );
	strcpy(pWildCard, pDirectoryName);
	strcat(pWildCard, "/*.");
	int nPathStrLen = nDirectoryNameLen + 1;

	FileFindHandle_t findHandle;
	const char *pFileName = g_pFileSystem->FindFirst( pWildCard, &findHandle );
	while( pFileName )
	{
		if ((pFileName[0] != '.') || (pFileName[1] != '.' && pFileName[1] != 0))
		{
			if( g_pFileSystem->FindIsDirectory( findHandle ) )
			{
				int fileNameStrLen = Q_strlen( pFileName );
				char *pFileNameWithPath = ( char * )stackalloc( nPathStrLen + fileNameStrLen + 1 );
				memcpy( pFileNameWithPath, pWildCard, nPathStrLen );
				pFileNameWithPath[nPathStrLen] = '\0';
				strcat( pFileNameWithPath, pFileName );

				if (!InitDirectoryRecursive( pFileNameWithPath, pEnum, nContext, nFlags ))
					return false;
			}
		}
		pFileName = g_pFileSystem->FindNext( findHandle );
	}

	return true;
}


//-----------------------------------------------------------------------------
// Discovers all .VMT files lying under a particular directory
// It only finds their names so we can generate shell materials for them
// that we can load up at a later time 
//-----------------------------------------------------------------------------
void CMaterial::EnumerateMaterials( IMaterialEnumerator *pEnum, int nContext, int nFlags )
{
	InitDirectoryRecursive( "materials", pEnum, nContext, nFlags );
}


//-----------------------------------------------------------------------------
// Purpose: Called from GetFirst/GetNextMaterialName to skip unwanted materials.
// Input  : pszName - Name of material to evaluate.
//			nFlags - One or more of the following:
//				INCLUDE_ALL_MATERIALS
//				INCLUDE_WORLD_MATERIALS
//				INCLUDE_MODEL_MATERIALS
// Output : Returns true to skip, false to not skip this material.
//-----------------------------------------------------------------------------
bool CMaterial::ShouldSkipMaterial(const char *pszName, int nFlags)
{
	static char szStrippedName[MAX_PATH];

	// if NULL skip it
	if( !pszName )
		return true;

	//
	// check against the list of exclusion directories
	//
	for( int i = 0; i < g_pGameConfig->m_MaterialExcludeCount; i++ )
	{
		// This will guarantee the match is at the start of the string
		const char *pMatchFound = Q_stristr( pszName, g_pGameConfig->m_szMaterialExcludeDirs[i] );
		if( pMatchFound == pszName )
			return true;
	}

	return false;

#if 0
	bool bSkip = false;

	if (pszName != NULL)
	{
		if (!(nFlags & INCLUDE_MODEL_MATERIALS))
		{
			if (_strnicmp(pszName, "models/", 7) == 0)
			{
				bSkip = true;
			}
		}

		if (!(nFlags & INCLUDE_WORLD_MATERIALS))
		{
			if (_strnicmp(pszName, "models/", 7) != 0)
			{
				bSkip = true;
			}
		}
	}
	else
	{
		bSkip = true;
	}

	return(bSkip);
#endif
}	


//-----------------------------------------------------------------------------
// Purpose: Factory. Creates a material by name.
// Input  : pszMaterialName - Name of material, ie "brick/brickfloor01".
// Output : Returns a pointer to the new material object, NULL if the given
//			material did not exist.
//-----------------------------------------------------------------------------
CMaterial *CMaterial::CreateMaterial(const char *pszMaterialName, bool bLoadImmediately, bool* pFound)
{
	Assert (pszMaterialName);

 	CMaterial *pMaterial = new CMaterial;
	Assert( pMaterial );

	// Store off the material name so we can load it later if we need to
	Q_snprintf( pMaterial->m_szFileName, MAX_PATH, pszMaterialName );
	Q_snprintf( pMaterial->m_szName, MAX_PATH, pszMaterialName );

	//
	// Find the material by name and load it.
	//
	if (bLoadImmediately)
	{
		bool bFound = pMaterial->LoadMaterial();

		// Returns if the material was found or not
		if (pFound)
			*pFound = bFound;
	}

	return pMaterial;
}


//-----------------------------------------------------------------------------
// Will actually load the material bits
// We don't want to load them all at once because it takes way too long
//-----------------------------------------------------------------------------
bool CMaterial::LoadMaterial()
{
	bool bFound = true;
	if (!m_bLoaded)
	{
		m_bLoaded = true;

		IMaterial *pMat = g_pMaterialSystemInterface->FindMaterial(m_szFileName, &bFound);
		Assert( pMat );

		if (!LoadMaterialHeader(pMat))
		{
			Msg( mwError,"Load material header failed: %s", m_szFileName );

			bFound = false;
			pMat = g_pMaterialSystemInterface->FindMaterial("debug/debugempty", NULL);
			LoadMaterialHeader(pMat);
		}
	}

	return bFound;
}


//-----------------------------------------------------------------------------
// Returns the material
//-----------------------------------------------------------------------------
IMaterial* CMaterial::GetMaterial()
{
	LoadMaterial();
	return m_pMaterial;
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CMaterial::DrawIcon( CDC *pDC, CMaterial* pIcon, RECT& dstRect )
{
	if (!pIcon)
		return;

	g_pMaterialImageCache->EnCache(pIcon);

	RECT rect, dst;
	rect.left = 0; rect.right = pIcon->GetWidth();

	// FIXME: Workaround the fact that materials must be power of 2, I want 12 bite
	rect.top = 2; rect.bottom = pIcon->GetHeight() - 2;

	dst = dstRect;
	float dstHeight = dstRect.bottom - dstRect.top;
	float srcAspect = (float)(rect.right - rect.left) / (float)(rect.bottom - rect.top);
	dst.right = dst.left + (dstHeight * srcAspect);
	pIcon->DrawBitmap( pDC, rect, dst );

	dstRect.left += dst.right - dst.left;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pDC - 
//			dstRect - 
//			detectErrors - 
//-----------------------------------------------------------------------------
void CMaterial::DrawBrowserIcons( CDC *pDC, RECT& dstRect, bool detectErrors )
{
	static CMaterial* pTranslucentIcon = 0;
	static CMaterial* pOpaqueIcon = 0;
	static CMaterial* pSelfIllumIcon = 0;
	static CMaterial* pBaseAlphaEnvMapMaskIcon = 0;
	static CMaterial* pErrorIcon = 0;

	if (!pTranslucentIcon)
	{
		pTranslucentIcon			= CreateMaterial("editor/translucenticon", true);
		pOpaqueIcon					= CreateMaterial("editor/opaqueicon", true);
		pSelfIllumIcon				= CreateMaterial("editor/selfillumicon", true);
		pBaseAlphaEnvMapMaskIcon	= CreateMaterial("editor/basealphaenvmapmaskicon", true);
		pErrorIcon					= CreateMaterial("editor/erroricon", true);

		Assert( pTranslucentIcon && pOpaqueIcon && pSelfIllumIcon && pBaseAlphaEnvMapMaskIcon && pErrorIcon );
	}

	bool error = false;

	IMaterial* pMaterial = GetMaterial();
	if ( pMaterial->GetMaterialVarFlag( MATERIAL_VAR_TRANSLUCENT ) )
	{
		DrawIcon( pDC, pTranslucentIcon, dstRect );
		if (detectErrors)
		{
			error = error || !m_TranslucentBaseTexture; 
		}
	}
	else
	{
		DrawIcon( pDC, pOpaqueIcon, dstRect ); 
	}

	if ( pMaterial->GetMaterialVarFlag( MATERIAL_VAR_SELFILLUM ))
	{
		DrawIcon( pDC, pSelfIllumIcon, dstRect ); 
		if (detectErrors)
		{
			error = error || !m_TranslucentBaseTexture;
		}
	}

	if ( pMaterial->GetMaterialVarFlag( MATERIAL_VAR_BASEALPHAENVMAPMASK ))
	{
		DrawIcon( pDC, pBaseAlphaEnvMapMaskIcon, dstRect ); 
		if (detectErrors)
		{
			error = error || !m_TranslucentBaseTexture;
		}
	}

	if (error)
	{
		DrawIcon( pDC, pErrorIcon, dstRect );
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pDC - 
//			srcRect - 
//			dstRect - 
//-----------------------------------------------------------------------------
void CMaterial::DrawBitmap( CDC *pDC, RECT& srcRect, RECT& dstRect )
{
	static struct
	{
		BITMAPINFOHEADER bmih;
		unsigned short colorindex[256];
	} bmi;

	int srcWidth = srcRect.right - srcRect.left;
	int srcHeight = srcRect.bottom - srcRect.top;

	BITMAPINFOHEADER &bmih = bmi.bmih;
	memset(&bmih, 0, sizeof(bmih));
	bmih.biSize = sizeof(bmih);
	bmih.biWidth = srcWidth;
	bmih.biHeight = -srcHeight;
	bmih.biCompression = BI_RGB;

	bmih.biBitCount = 24;
	bmih.biPlanes = 1;

	static BOOL bInit = false;
	if (!bInit)
	{
		bInit = true;
		for (int i = 0; i < 256; i++)
		{
			bmi.colorindex[i] = i;
		}
	}

	int dest_width = dstRect.right - dstRect.left;
	int dest_height = dstRect.bottom - dstRect.top;

	// ** bits **
	SetStretchBltMode(pDC->m_hDC, COLORONCOLOR);
	if (StretchDIBits(pDC->m_hDC, dstRect.left, dstRect.top, dest_width, dest_height, 
		srcRect.left, -srcRect.top, srcWidth, srcHeight, m_pData, (BITMAPINFO*)&bmi, DIB_RGB_COLORS, SRCCOPY) == GDI_ERROR)
	{
		Msg(mwError, "CMaterial::Draw(): StretchDIBits failed.");
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pDC - 
//			rect - 
//			iFontHeight - 
//			dwFlags - 
//-----------------------------------------------------------------------------
void CMaterial::Draw(CDC *pDC, RECT& rect, int iFontHeight, int iIconHeight, DWORD dwFlags)
{
	g_pMaterialImageCache->EnCache(this);
	if (!this->HasData())
	{
		return;
	}
	
	if (m_nWidth <= 0)
	{
NoData:
		// draw "no data"
		CFont *pOldFont = (CFont*) pDC->SelectStockObject(ANSI_VAR_FONT);
		COLORREF cr = pDC->SetTextColor(RGB(0xff, 0xff, 0xff));
		COLORREF cr2 = pDC->SetBkColor(RGB(0, 0, 0));

		// draw black rect first
		pDC->FillRect(&rect, CBrush::FromHandle(HBRUSH(GetStockObject(BLACK_BRUSH))));

		// then text
		pDC->TextOut(rect.left+2, rect.top+2, "No Image", 8);
		pDC->SelectObject(pOldFont);
		pDC->SetTextColor(cr);
		pDC->SetBkColor(cr2);
		return;
	}

	// no data -
	if (!m_pData)
	{
		// try to load -
		if (!Load())
		{
			// can't load -
			goto NoData;
		}
	}

	// Draw the material image
	RECT srcRect, dstRect;
	srcRect.left = 0;
	srcRect.top = 0;
	srcRect.right = m_nWidth;
	srcRect.bottom = m_nHeight;
	dstRect = rect;

	if (dwFlags & drawCaption)
	{
		dstRect.bottom -= iFontHeight +  4;
	}
	if (dwFlags & drawIcons)
	{
		dstRect.bottom -= iIconHeight;
	}

	if (!(dwFlags & drawResizeAlways))
	{
		if (m_nWidth < dstRect.right - dstRect.left )
		{
			dstRect.right = dstRect.left + m_nWidth;
		}

		if (m_nHeight < dstRect.bottom - dstRect.top )
		{
			dstRect.bottom = dstRect.top + m_nHeight;
		}
	}
	DrawBitmap( pDC, srcRect, dstRect );

	// Draw the icons
	if (dwFlags & drawIcons)
	{
		dstRect = rect;
		if (dwFlags & drawCaption)
			dstRect.bottom -= iFontHeight + 5; 
		dstRect.top = dstRect.bottom - iIconHeight; 
		DrawBrowserIcons(pDC, dstRect, (dwFlags & drawErrors) != 0 );
	}

	// ** caption **
	if (dwFlags & drawCaption)
	{
		// draw background for name
		CBrush brCaption(RGB(0, 0, 255));
		CRect rcCaption(rect);
		
		rcCaption.top = rcCaption.bottom - (iFontHeight + 5);
		pDC->FillRect(rcCaption, &brCaption);

		// draw name
		char szShortName[MAX_PATH];
		int iLen = GetShortName(szShortName);
		pDC->TextOut(rect.left, rect.bottom - (iFontHeight + 4), szShortName, iLen);
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CMaterial::FreeData( void )
{
	free( m_pData );
	m_pData = NULL;
}


//-----------------------------------------------------------------------------
// Purpose: Returns a string of comma delimited keywords associated with this
//			material.
// Input  : pszKeywords - Buffer to receive keywords, NULL to query string length.
// Output : Returns the number of characters in the keyword string.
//-----------------------------------------------------------------------------
int CMaterial::GetKeywords(char *pszKeywords) const
{
	// To access keywords, we have to have the header loaded
	const_cast<CMaterial*>(this)->Load();
	if (pszKeywords != NULL)
	{
		strcpy(pszKeywords, m_szKeywords);
	}

	return(strlen(m_szKeywords));
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pszName - 
// Output : int
//-----------------------------------------------------------------------------
int CMaterial::GetShortName(char *pszName) const
{
	if (pszName != NULL)
	{
		strcpy(pszName, m_szName);
	}

	return(strlen(m_szName));
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : material - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CMaterial::LoadMaterialHeader( IMaterial *pMat )
{
	PreviewImageRetVal_t retVal;
	bool translucentBaseTexture;
	ImageFormat eImageFormat;
	int width, height;
	retVal = pMat->GetPreviewImageProperties(&width, &height, &eImageFormat, &translucentBaseTexture);

	if (retVal == MATERIAL_PREVIEW_IMAGE_BAD)
		return false;

	m_pMaterial = pMat;
	m_pMaterial->IncrementReferenceCount();

	m_nWidth = width;
	m_nHeight = height;
	m_TranslucentBaseTexture = translucentBaseTexture; 

	// Find the keywords for this material from the vmt file.
	bool bFound;
	IMaterialVar *pVar = pMat->FindVar("%keywords", &bFound, false);
	if (bFound)
	{
		strcpy(m_szKeywords, pVar->GetStringValue());

		// Register the keywords
		g_Textures.RegisterTextureKeywords( this );
	}

	return(true);
}


//-----------------------------------------------------------------------------
// Purpose: Returns the full path of the file from which this material was loaded.
//-----------------------------------------------------------------------------
const char *CMaterial::GetFileName( void ) const
{
	return(m_szFileName);
}


//-----------------------------------------------------------------------------
// Purpose: If the buffer pointer passed in is not NULL, copies the image data
//			in RGB format to the buffer
// Input  : pImageRGB - Pointer to buffer that receives the image data. If the
//				pointer is NULL, no data is copied, only the data size is returned.
// Output : Returns a the size of the RGB image in bytes.
//-----------------------------------------------------------------------------
int CMaterial::GetImageDataRGB( void *pImageRGB )
{
	Assert( m_nWidth > 0 );

	if ( pImageRGB != NULL )
	{
		Load();

		g_pMaterialImageCache->EnCache( this );
		if (!this->HasData())
		{
			return(NULL);
		}

		unsigned char *src, *dst;
		src = ( unsigned char * )m_pData;
		dst = (unsigned char *)pImageRGB;
		for( ; src < ( unsigned char * )m_pData + m_nWidth * m_nHeight * 3; src += 3, dst += 3 )
		{
			dst[0] = src[2];
			dst[1] = src[1];
			dst[2] = src[0];
		}

		return(	m_nWidth * m_nHeight * 3 );
	}

	return(	m_nWidth * m_nHeight * 3 );
}


//-----------------------------------------------------------------------------
// Purpose: If the buffer pointer passed in is not NULL, copies the image data
//			in RGBA format to the buffer
// Input  : pImageRGBA - Pointer to buffer that receives the image data. If the
//				pointer is NULL, no data is copied, only the data size is returned.
// Output : Returns a the size of the RGBA image in bytes.
//-----------------------------------------------------------------------------
int CMaterial::GetImageDataRGBA(void *pImageRGBA)
{
	Assert( m_nWidth > 0 );

	if (pImageRGBA != NULL)
	{
		Load();

		g_pMaterialImageCache->EnCache(this);
		if (!this->HasData())
		{
			return(NULL);
		}

		unsigned char *src, *dst;
		src = (unsigned char *)m_pData;
		dst = (unsigned char *)pImageRGBA;

		while (src < (unsigned char *)m_pData + m_nWidth * m_nHeight * 4);	
		{
			dst[0] = src[2];
			dst[1] = src[1];
			dst[2] = src[0];
			dst[3] = 0;

			src += 4;
			dst += 4;
		}
	}

	return(m_nWidth * m_nHeight * 4);
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : size - 
//-----------------------------------------------------------------------------
void CMaterial::GetSize(SIZE &size) const
{
	const_cast<CMaterial*>(this)->Load();
	Assert( m_nWidth >= 0 );

	size.cx = m_nWidth;
	size.cy = m_nHeight;
}


//-----------------------------------------------------------------------------
// Purpose: Loads this material's image from disk if it is not already loaded.
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CMaterial::Load( void )
{
	LoadMaterial();
	return true;
}


//-----------------------------------------------------------------------------
// cache in the image size only when we need to
//-----------------------------------------------------------------------------
int CMaterial::GetImageWidth(void) const
{
	const_cast<CMaterial*>(this)->Load();
	return(m_nWidth);
}

int CMaterial::GetImageHeight(void) const
{
	const_cast<CMaterial*>(this)->Load();
	return(m_nHeight);
}

int CMaterial::GetWidth(void) const
{
	const_cast<CMaterial*>(this)->Load();
	return(m_nWidth);
}

int CMaterial::GetHeight(void) const
{
	const_cast<CMaterial*>(this)->Load();
	return(m_nHeight);
}

float CMaterial::GetDecalScale(void) const
{
	const_cast<CMaterial*>(this)->Load();

	IMaterialVar *decalScaleVar;
	bool found;
	
	decalScaleVar = m_pMaterial->FindVar( "$decalScale", &found, false );
	if( !found )
	{
		return 1.0f;
	}
	else
	{
		return decalScaleVar->GetFloatValue();
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CMaterial::LoadMaterialImage( void )
{
	Load();

	if ((!m_nWidth) || (!m_nHeight))
		return(false);

	m_pData = malloc(m_nWidth * m_nHeight * 3);
	Assert(m_pData);

	ImageFormat imageFormat;

//	if( _strnicmp( m_pMaterial->GetName(), "decals", 6 ) == 0 )
//	{
//		imageFormat = IMAGE_FORMAT_BGR888_BLUESCREEN;
//	}
//	else
	{
		imageFormat = IMAGE_FORMAT_BGR888;
	}
	
	PreviewImageRetVal_t retVal;
	retVal = m_pMaterial->GetPreviewImage( (unsigned char *)m_pData, m_nWidth, m_nHeight, imageFormat );
	return (retVal != MATERIAL_PREVIEW_IMAGE_BAD); 
}

static void ReleaseMaterialSystemObjects()
{
	StudioModel::ReleaseStudioModels();
}

static void RestoreMaterialSystemObjects()
{
	StudioModel::RestoreStudioModels();
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pszMaterialsDir - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CMaterial::Initialize( char *pszMaterialsDir, HWND hwnd )
{
	memset( &g_materialSystemConfig, 0, sizeof(g_materialSystemConfig) );

	// NOTE: This gets set to true later upon creating a 3d view.
	g_materialSystemConfig.texGamma = 2.2f;
	g_materialSystemConfig.screenGamma = 2.2f;
	g_materialSystemConfig.overbright = 1.0f;
	g_materialSystemConfig.bAllowCheats = false;
	g_materialSystemConfig.bLinearFrameBuffer = false;
	g_materialSystemConfig.bFilterTextures = true;
	g_materialSystemConfig.bCompressedTextures = true;
	g_materialSystemConfig.bBumpmap = true;
	g_materialSystemConfig.bShowSpecular = true;
	g_materialSystemConfig.bShowDiffuse = true;
	g_materialSystemConfig.maxFrameLatency = 1;
	g_materialSystemConfig.bFilterLightmaps = true;
	g_materialSystemConfig.bMipMapTextures = true;
	g_materialSystemConfig.bBufferPrimitives = true;
	g_materialSystemConfig.bSoftwareLighting = true;
	g_materialSystemConfig.bEditMode = true;	// Yes, we're in WorldCraft.
	g_materialSystemConfig.dxSupportLevel = 0;

	//
	// Create a cache for material images (for browsing and uploading to the driver).
	//
	if (g_pMaterialImageCache == NULL)
	{
		g_pMaterialImageCache = new CMaterialImageCache(500);
		if (g_pMaterialImageCache == NULL)
		{
			return(false);
		}
	}

	LoadMaterialSystemInterface();
    if (!g_bMaterialSystemLoaded)
    {
        return(false);
    }

	if (pszMaterialsDir == NULL)
	{
		return(false);
	}

	char* pShader = "shaderapidx9.dll";

	//
	// Init the game and mod dirs in the file system.
	// This needs to happen before calling Init on the material system.
	//
	if ( !FileSystem_Init( g_pGameConfig->m_szModDir, g_pGameConfig->m_szGameDir ))
	{
		Msg( mwError,"ERROR: Failed to initialize materials file system. GameDir=%s, ModDir=%", g_pGameConfig->m_szGameDir, g_pGameConfig->m_szModDir );
		return false;
	}

	if (!( g_MaterialSystemClientFactory = 
		g_pMaterialSystemInterface->Init( pShader, GetWorldcraftMaterialProxyFactory(), FileSystem_GetFactory() )))
		return false;

	g_pMaterialSystemHardwareConfig = (IMaterialSystemHardwareConfig*)
		g_MaterialSystemClientFactory( MATERIALSYSTEM_HARDWARECONFIG_INTERFACE_VERSION, 0 );
	if ( !g_pMaterialSystemHardwareConfig )
		return false;

	g_pMaterialSystemInterface->UpdateConfig( &g_materialSystemConfig, false );
	g_pMaterialSystemInterface->AddReleaseFunc( ReleaseMaterialSystemObjects );
	g_pMaterialSystemInterface->AddRestoreFunc( RestoreMaterialSystemObjects );

	// Set the mode
	// When setting the mode, we need to grab the parent window
	// since that's going to enclose all our little render windows
	MaterialVideoMode_t mode;
	mode.m_Width = mode.m_Height = 0;

	if (!MaterialSystemInterface()->SetMode( hwnd, mode, 
		MATERIAL_VIDEO_MODE_WINDOWED | MATERIAL_VIDEO_MODE_RESIZING ))
	{
		return false;
	}
	
	ITexture *pCubemapTexture = MaterialSystemInterface()->FindTexture( "editor/cubemap", NULL, true );
	pCubemapTexture->IncrementReferenceCount();
	MaterialSystemInterface()->BindLocalCubemap( pCubemapTexture );
	
	return(true);
}
						    

//-----------------------------------------------------------------------------
// Purpose: Restores the material system to an uninitialized state.
//-----------------------------------------------------------------------------
void CMaterial::ShutDown(void)
{
	if (g_pMaterialSystemInterface != NULL)
	{
		g_pMaterialSystemInterface->Shutdown();
	}

	FreeLibrary(materialSystemDLLHInst);

	delete g_pMaterialImageCache;
	g_pMaterialImageCache = NULL;

	FileSystem_Shutdown();
}


