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

#include "filesystem.h"
#include "matsys_controls/baseassetpicker.h"
#include "tier1/KeyValues.h"
#include "tier1/utlntree.h"
#include "tier1/utlrbtree.h"
#include "vgui_controls/ListPanel.h"
#include "vgui_controls/TextEntry.h"
#include "vgui_controls/ComboBox.h"
#include "vgui_controls/Button.h"
#include "vgui_controls/Splitter.h"
#include "vgui_controls/TreeView.h"
#include "vgui_controls/ImageList.h"
#include "vgui_controls/CheckButton.h"
#include "vgui/ISurface.h"
#include "vgui/IInput.h"
#include "vgui/IVGui.h"
#include "vgui/Cursor.h"


using namespace vgui;


#define ASSET_LIST_DIRECTORY_INITIAL_SEARCH_TIME 0.25f
#define ASSET_LIST_DIRECTORY_SEARCH_TIME 0.025f


//-----------------------------------------------------------------------------
// sorting function, should return true if node1 should be displayed before node2
//-----------------------------------------------------------------------------
bool AssetTreeViewSortFunc( KeyValues *node1, KeyValues *node2 )
{
	const char *pDir1 = node1->GetString( "text", NULL );
	const char *pDir2 = node2->GetString( "text", NULL );
	return Q_stricmp( pDir1, pDir2 ) < 0;
}


//-----------------------------------------------------------------------------
//
// Tree view for assets
//
//-----------------------------------------------------------------------------
class CAssetTreeView : public vgui::TreeView
{
	DECLARE_CLASS_SIMPLE( CAssetTreeView, vgui::TreeView );

public:
	CAssetTreeView( vgui::Panel *parent, const char *name, const char *pRootFolderName, const char *pRootDir );

	// Inherited from base classes
	virtual void GenerateChildrenOfNode( int itemIndex );
	virtual void ApplySchemeSettings( vgui::IScheme *pScheme );

	// Opens and selects the root folder
	void OpenRoot();

	// Purpose: Refreshes the active file list
	void RefreshFileList();

	// Adds a subdirectory
	DirHandle_t GetRootDirectory( );
	DirHandle_t AddSubDirectory( DirHandle_t hParent, const char *pDirName );
	void ClearDirectories();

	// Selects a folder
	void SelectFolder( const char *pSubDir, const char *pPath );

private:
	// Allocates the root node
	void AllocateRootNode( );

	// Purpose: Refreshes the active file list
	DirHandle_t RefreshTreeViewItem( int nItemIndex );

	// Sets an item to be colored as if its a menu
	void SetItemColorForDirectories( int nItemID );

	// Add a directory into the treeview
	void AddDirectoryToTreeView( int nParentItemIndex, const char *pFullParentPath, DirHandle_t hPath );

	// Selects an item in the tree
	bool SelectFolder_R( int nItemID, const char *pPath );

	CUtlString m_RootFolderName;
	CUtlString m_RootDirectory;
	vgui::ImageList m_Images;
	CUtlNTree< CUtlString, DirHandle_t > m_DirectoryStructure;
};


//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CAssetTreeView::CAssetTreeView( Panel *pParent, const char *pName, const char *pRootFolderName, const char *pRootDir ) : BaseClass(pParent, pName), m_Images( false )
{
	SetSortFunc( AssetTreeViewSortFunc );

	m_RootFolderName = pRootFolderName;
	m_RootDirectory = pRootDir;
	AllocateRootNode();

	// build our list of images
	m_Images.AddImage( scheme()->GetImage( "resource/icon_folder", false ) );
	SetImageList( &m_Images, false );

	SETUP_PANEL( this );
}


//-----------------------------------------------------------------------------
// Purpose: Refreshes the active file list
//-----------------------------------------------------------------------------
void CAssetTreeView::OpenRoot()
{
	RemoveAll();

	// add the base node
	const char *pRootDir = m_DirectoryStructure[ m_DirectoryStructure.Root() ];
	KeyValues *pkv = new KeyValues( "root" );
	pkv->SetString( "text", m_RootFolderName.Get() );
	pkv->SetInt( "root", 1 );
	pkv->SetInt( "expand", 1 );
	pkv->SetInt( "dirHandle", m_DirectoryStructure.Root() );
	pkv->SetString( "path", pRootDir );
	int iRoot = AddItem( pkv, GetRootItemIndex() );
	pkv->deleteThis();
	ExpandItem( iRoot, true );
}


//-----------------------------------------------------------------------------
// Allocates the root node
//-----------------------------------------------------------------------------
void CAssetTreeView::AllocateRootNode( )
{
	DirHandle_t hRoot = m_DirectoryStructure.Alloc();
	m_DirectoryStructure.SetRoot( hRoot );
	m_DirectoryStructure[hRoot] = m_RootDirectory;
}


//-----------------------------------------------------------------------------
// Adds a subdirectory (maintains sorted order)
//-----------------------------------------------------------------------------
DirHandle_t CAssetTreeView::GetRootDirectory( )
{
	return m_DirectoryStructure.Root();
}

DirHandle_t CAssetTreeView::AddSubDirectory( DirHandle_t hParent, const char *pDirName )
{
	DirHandle_t hSubdir = m_DirectoryStructure.Alloc();
	m_DirectoryStructure[hSubdir] = pDirName;
	m_DirectoryStructure[hSubdir].ToLower();

	DirHandle_t hChild = m_DirectoryStructure.FirstChild( hParent );
	m_DirectoryStructure.LinkChildBefore( hParent, hChild, hSubdir );

	return hSubdir;
}

void CAssetTreeView::ClearDirectories()
{
	m_DirectoryStructure.RemoveAll();
	AllocateRootNode();
}


//-----------------------------------------------------------------------------
// Sets an item to be colored as if its a menu
//-----------------------------------------------------------------------------
void CAssetTreeView::SetItemColorForDirectories( int nItemID )
{
	// mark directories in orange
	SetItemFgColor( nItemID, Color(224, 192, 0, 255) );
}


//-----------------------------------------------------------------------------
// Add a directory into the treeview
//-----------------------------------------------------------------------------
void CAssetTreeView::AddDirectoryToTreeView( int nParentItemIndex, const char *pFullParentPath, DirHandle_t hPath )
{
	const char *pDirName = m_DirectoryStructure[hPath].Get();
	KeyValues *kv = new KeyValues( "node", "text", pDirName );

	char pFullPath[MAX_PATH];
	Q_snprintf( pFullPath, sizeof( pFullPath ), "%s/%s", pFullParentPath, pDirName );
	Q_FixSlashes( pFullPath );
	Q_strlower( pFullPath );
	bool bHasSubdirectories = m_DirectoryStructure.FirstChild( hPath ) != m_DirectoryStructure.InvalidIndex();
	kv->SetString( "path", pFullPath );
	kv->SetInt( "expand", bHasSubdirectories );
	kv->SetInt( "image", 0 );
	kv->SetInt( "dirHandle", hPath );

	int nItemID = AddItem( kv, nParentItemIndex );
	kv->deleteThis();

	// mark directories in orange
	SetItemColorForDirectories( nItemID );
}


//-----------------------------------------------------------------------------
// override to incremental request and show p4 directories
//-----------------------------------------------------------------------------
void CAssetTreeView::GenerateChildrenOfNode( int nItemIndex )
{
	KeyValues *pkv = GetItemData( nItemIndex );

	const char *pFullParentPath = pkv->GetString( "path", NULL );
	if ( !pFullParentPath )
		return;

	DirHandle_t hPath = (DirHandle_t)pkv->GetInt( "dirHandle", m_DirectoryStructure.InvalidIndex() );
	if ( hPath == m_DirectoryStructure.InvalidIndex() )
		return;

	DirHandle_t hChild = m_DirectoryStructure.FirstChild( hPath );
	while ( hChild != m_DirectoryStructure.InvalidIndex() )
	{
		AddDirectoryToTreeView( nItemIndex, pFullParentPath, hChild );
		hChild = m_DirectoryStructure.NextSibling( hChild );
	}
}


//-----------------------------------------------------------------------------
// Purpose: Refreshes the active file list
//-----------------------------------------------------------------------------
DirHandle_t CAssetTreeView::RefreshTreeViewItem( int nItemIndex )
{
	if ( nItemIndex < 0 )
		return m_DirectoryStructure.InvalidIndex();

	// Make sure the expand icons are set correctly
	KeyValues *pkv = GetItemData( nItemIndex );
	DirHandle_t hPath = (DirHandle_t)pkv->GetInt( "dirHandle", m_DirectoryStructure.InvalidIndex() );
	const char *pFullParentPath = pkv->GetString( "path", NULL );
	bool bHasSubdirectories = m_DirectoryStructure.FirstChild( hPath ) != m_DirectoryStructure.InvalidIndex();
	if ( bHasSubdirectories != ( pkv->GetInt( "expand" ) != 0 ) )
	{
		pkv->SetInt( "expand", bHasSubdirectories );
		ModifyItem( nItemIndex, pkv );
	}
	bool bIsExpanded = IsItemExpanded( nItemIndex );
	if ( !bIsExpanded )
		return hPath;

	// Check all children + build a list of children we've already got
	int nChildCount = GetNumChildren( nItemIndex );
	DirHandle_t *pFoundHandles = (DirHandle_t*)_alloca( nChildCount * sizeof(DirHandle_t) );
	memset( pFoundHandles, 0xFF, nChildCount * sizeof(DirHandle_t) );
	for ( int i = 0; i < nChildCount; ++i )
	{
		int nChildItemIndex = GetChild( nItemIndex, i );
		pFoundHandles[i] = RefreshTreeViewItem( nChildItemIndex );
	}

	// Check directory structure to see if other directories were added
	DirHandle_t hChild = m_DirectoryStructure.FirstChild( hPath );
	for ( ; hChild != m_DirectoryStructure.InvalidIndex(); hChild = m_DirectoryStructure.NextSibling( hChild ) )
	{
		// Search for existence of this child already
		bool bFound = false;
		for ( int j = 0; j < nChildCount; ++j )
		{
			if ( pFoundHandles[j] == hChild )
			{
				pFoundHandles[j] = pFoundHandles[nChildCount-1];
				--nChildCount;
				bFound = true;
				break;
			}
		}

		if ( bFound )
			continue;

		// Child is new, add it
		AddDirectoryToTreeView( nItemIndex, pFullParentPath, hChild );
	}

	return hPath;
}

void CAssetTreeView::RefreshFileList()
{
	// Make sure the expand icons are set correctly
	RefreshTreeViewItem( GetRootItemIndex() );
	InvalidateLayout();
}


//-----------------------------------------------------------------------------
// Selects a folder
//-----------------------------------------------------------------------------
bool CAssetTreeView::SelectFolder_R( int nItemID, const char *pPath )
{
	if ( nItemID < 0 )
		return false;

	KeyValues *kv = GetItemData( nItemID );
	const char *pTestPath = kv->GetString( "path" );
	if ( !Q_stricmp( pTestPath, pPath ) )
	{
		AddSelectedItem( nItemID, true, false, true );
		return true;
	}

	// Substring match..
	CUtlString str = pTestPath;
	str += '\\';
	if ( Q_strnicmp( str, pPath, str.Length() ) )
		return false;

	ExpandItem( nItemID, true );

	int nChildCount = GetNumChildren( nItemID );
	for ( int i = 0; i < nChildCount; ++i )
	{
		int nChildItemID = GetChild( nItemID, i );
		if ( SelectFolder_R( nChildItemID, pPath ) )
			return true;
	}
	return false;
}

void CAssetTreeView::SelectFolder( const char *pSubDir, const char *pPath )
{
	char pTemp[MAX_PATH];
	Q_snprintf( pTemp, sizeof(pTemp), "%s\\%s", pSubDir, pPath );
	Q_StripTrailingSlash( pTemp );

	int nItem = GetRootItemIndex();
	SelectFolder_R( nItem, pTemp );
}



//-----------------------------------------------------------------------------
// setup a smaller font
//-----------------------------------------------------------------------------
void CAssetTreeView::ApplySchemeSettings( IScheme *pScheme )
{
	BaseClass::ApplySchemeSettings( pScheme );
	SetFont( pScheme->GetFont("DefaultSmall") );
	SetFgColor( Color(216, 222, 211, 255) );
}


//-----------------------------------------------------------------------------
//
// Cache of asset data so we don't need to rebuild all the time
//
//-----------------------------------------------------------------------------
DECLARE_POINTER_HANDLE( AssetList_t );
#define ASSET_LIST_INVALID ((AssetList_t)(0xFFFF))


class CAssetCache
{
public:
	struct CachedAssetInfo_t
	{
		CUtlString m_AssetName;
		int m_nModIndex;
	};

	struct ModInfo_t
	{
		CUtlString m_ModName;
		CUtlString m_Path;
	};

	CAssetCache();

	// Mod iteration
	int ModCount() const;
	const ModInfo_t& ModInfo( int nIndex ) const;
	
	// Building the mod list
	void BuildModList();

	AssetList_t FindAssetList( const char *pAssetType, const char *pSubDir, int nExtCount, const char **ppExt );
	bool BeginAssetScan( AssetList_t hList, bool bForceRescan = false );
	CAssetTreeView* GetFileTree( AssetList_t hList );
	int GetAssetCount( AssetList_t hList ) const;
	const CachedAssetInfo_t& GetAsset( AssetList_t hList, int nIndex ) const;

	void AddAsset( AssetList_t hList, const CachedAssetInfo_t& info );

	bool ContinueSearchForAssets( AssetList_t hList, float flDuration );

private:
	struct DirToCheck_t
	{
		CUtlString m_DirName;
		DirHandle_t m_hDirHandle;
	};

	struct CachedAssetList_t
	{
		CachedAssetList_t() = default;
		CachedAssetList_t( const char *pSearchSubDir, int nExtCount, const char **ppSearchExt )	:
			m_pSubDir( pSearchSubDir, Q_strlen( pSearchSubDir ) + 1 )
		{
			m_Ext.AddMultipleToTail( nExtCount, ppSearchExt );
		}
 		CachedAssetList_t( const CachedAssetList_t& )
		{
			// Only used during insertion; do nothing
		}

		CUtlVector< CachedAssetInfo_t > m_AssetList;
		CAssetTreeView *m_pFileTree;

		CUtlString m_pSubDir;
		CUtlVector< const char * > m_Ext;

		CUtlLinkedList< DirToCheck_t > m_DirectoriesToCheck;
		FileFindHandle_t m_hFind;
		bool m_bAssetScanComplete;
	};

private:
	bool AddFilesInDirectory( CachedAssetList_t& list, const char *pStartingFile, const char *pFilePath, DirHandle_t hDirHandle, float flStartTime, float flDuration );
	bool DoesExtensionMatch( CachedAssetList_t& list, const char *pFileName );
	void AddAssetToList( CachedAssetList_t& list, const char *pAssetName, int nModIndex );

private:
	// List of known mods
	CUtlVector< ModInfo_t > m_ModList;

	// List of cached assets
	CUtlRBTree< CachedAssetList_t > m_CachedAssets;

	// Have we built the mod list?
	bool m_bBuiltModList;

	static bool CachedAssetLessFunc( const CachedAssetList_t& src1, const CachedAssetList_t& src2 );
};


//-----------------------------------------------------------------------------
// Static instance of the asset cache
//-----------------------------------------------------------------------------
static CAssetCache s_AssetCache;


//-----------------------------------------------------------------------------
// Map sort func
//-----------------------------------------------------------------------------
bool CAssetCache::CachedAssetLessFunc( const CAssetCache::CachedAssetList_t& src1, const CAssetCache::CachedAssetList_t& src2 )
{
	int nRetVal = Q_stricmp( src1.m_pSubDir, src2.m_pSubDir ) > 0;
	if ( nRetVal != 0 )
		return nRetVal > 0;

	int nCount = src1.m_Ext.Count();
	int nDiff = nCount - src2.m_Ext.Count();
	if ( nDiff != 0 )
		return nDiff > 0;

	for ( int i = 0; i < nCount; ++i )
	{
		nRetVal = Q_stricmp( src1.m_Ext[i], src2.m_Ext[i] );
		if ( nRetVal != 0 )
			return nRetVal > 0;
	}

	return false;
}


//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CAssetCache::CAssetCache() : m_CachedAssets( 0, 0, CachedAssetLessFunc )
{
	m_bBuiltModList = false;
}


//-----------------------------------------------------------------------------
// Mod iteration
//-----------------------------------------------------------------------------
int CAssetCache::ModCount() const
{
	return m_ModList.Count();
}

const CAssetCache::ModInfo_t& CAssetCache::ModInfo( int nIndex ) const
{
	return m_ModList[nIndex];
}


//-----------------------------------------------------------------------------
// Building the mod list
//-----------------------------------------------------------------------------
void CAssetCache::BuildModList()
{
	if ( m_bBuiltModList )
		return;

	m_bBuiltModList = true;

	m_ModList.RemoveAll();

	// Add all mods
	int nLen = g_pFullFileSystem->GetSearchPath( "GAME", false, NULL, 0 );
	char *pSearchPath = (char*)stackalloc( nLen * sizeof(char) );
	g_pFullFileSystem->GetSearchPath( "GAME", false, pSearchPath, nLen );
	char *pPath = pSearchPath;
	while( pPath )
	{
		char *pSemiColon = strchr( pPath, ';' );
		if ( pSemiColon )
		{
			*pSemiColon = 0;
		}

		Q_StripTrailingSlash( pPath );
		Q_FixSlashes( pPath );

		char pModName[ MAX_PATH ];
		Q_FileBase( pPath, pModName, sizeof( pModName ) );

		// Always start in an asset-specific directory
//		char pAssetPath[MAX_PATH];
//		Q_snprintf( pAssetPath, MAX_PATH, "%s\\%s", pPath, m_pAssetSubDir );
//		Q_FixSlashes( pPath );

		int i = m_ModList.AddToTail( );
		m_ModList[i].m_ModName.Set( pModName );
		m_ModList[i].m_Path.Set( pPath );

		pPath = pSemiColon ? pSemiColon + 1 : NULL;
	}
}


//-----------------------------------------------------------------------------
// Adds an asset to the list of assets of this type
//-----------------------------------------------------------------------------
void CAssetCache::AddAssetToList( CachedAssetList_t& list, const char *pAssetName, int nModIndex )
{
	int i = list.m_AssetList.AddToTail( );
	CachedAssetInfo_t& info = list.m_AssetList[i];
	info.m_AssetName.Set( pAssetName );
	info.m_nModIndex = nModIndex;
}


//-----------------------------------------------------------------------------
// Extension matches?
//-----------------------------------------------------------------------------
bool CAssetCache::DoesExtensionMatch( CachedAssetList_t& info, const char *pFileName )
{
	char pChildExt[MAX_PATH];
	Q_ExtractFileExtension( pFileName, pChildExt, sizeof(pChildExt) );

	// Check the extension matches
	int nCount = info.m_Ext.Count();
	for ( int i = 0; i < nCount; ++i )
	{
		if ( !Q_stricmp( info.m_Ext[i], pChildExt ) )
			return true;
	}

	return false;
}


//-----------------------------------------------------------------------------
// Recursively add all files matching the wildcard under this directory
//-----------------------------------------------------------------------------
bool CAssetCache::AddFilesInDirectory( CachedAssetList_t& list, const char *pStartingFile, const char *pFilePath, DirHandle_t hCurrentDir, float flStartTime, float flDuration )
{
	// Indicates no files found
	if ( list.m_hFind == FILESYSTEM_INVALID_FIND_HANDLE )
		return true;

	// generate children
	// add all the items
	int nModCount = m_ModList.Count();
	int nSubDirLen = list.m_pSubDir ? Q_strlen(list.m_pSubDir) : 0;
	const char *pszFileName	= pStartingFile;
	while ( pszFileName )
	{
		char pRelativeChildPath[MAX_PATH];
		Q_snprintf( pRelativeChildPath, MAX_PATH, "%s\\%s", pFilePath, pszFileName );

		if ( g_pFullFileSystem->FindIsDirectory( list.m_hFind ) )
		{
			// If .svn is in the name, don't add this directory!!
			if ( strstr (pszFileName, ".svn") )
			{
				pszFileName = g_pFullFileSystem->FindNext( list.m_hFind );
				continue;
			}

			if ( Q_strnicmp( pszFileName, ".", 2 ) && Q_strnicmp( pszFileName, "..", 3 ) )
			{
				DirHandle_t hDirHandle = list.m_pFileTree->AddSubDirectory( hCurrentDir, pszFileName );
				int i = list.m_DirectoriesToCheck.AddToTail();
				list.m_DirectoriesToCheck[i].m_DirName = pRelativeChildPath;
				list.m_DirectoriesToCheck[i].m_hDirHandle = hDirHandle;
			}
		}
		else
		{
			// Check the extension matches
			if ( DoesExtensionMatch( list, pszFileName ) )
			{
				char pFullAssetPath[MAX_PATH];
				g_pFullFileSystem->RelativePathToFullPath( pRelativeChildPath, "GAME", pFullAssetPath, sizeof(pFullAssetPath) );

				int nModIndex = -1;
				for ( int i = 0; i < nModCount; ++i )
				{
					if ( !Q_strnicmp( pFullAssetPath, m_ModList[i].m_Path, m_ModList[i].m_Path.Length() ) )
					{
						nModIndex = i;
						break;
					}
				}

				if ( nModIndex >= 0 )
				{
					// Strip 'subdir/' prefix
					char *pAssetName = pRelativeChildPath;
					if ( list.m_pSubDir )
					{
						if ( !Q_strnicmp( list.m_pSubDir, pAssetName, nSubDirLen ) )
						{
							if ( pAssetName[nSubDirLen] == '\\' )
							{
								pAssetName += nSubDirLen + 1;
							}
						}
					}
					strlwr( pAssetName );

					AddAssetToList( list, pAssetName, nModIndex );
				}
			}
		}

		// Don't let the search go for too long at a time
		if ( Plat_FloatTime() - flStartTime >= flDuration )
			return false;

		pszFileName = g_pFullFileSystem->FindNext( list.m_hFind );
	}

	return true;
}


//-----------------------------------------------------------------------------
// Recursively add all files matching the wildcard under this directory
//-----------------------------------------------------------------------------
bool CAssetCache::ContinueSearchForAssets( AssetList_t hList, float flDuration )
{
	CachedAssetList_t& list = m_CachedAssets[ (intp)hList ];

	float flStartTime = Plat_FloatTime();
	while ( list.m_DirectoriesToCheck.Count() )
	{
		const char *pFilePath = list.m_DirectoriesToCheck[ list.m_DirectoriesToCheck.Head() ].m_DirName;
		DirHandle_t hCurrentDir = list.m_DirectoriesToCheck[ list.m_DirectoriesToCheck.Head() ].m_hDirHandle;

		const char *pStartingFile;
		if ( list.m_hFind == FILESYSTEM_INVALID_FIND_HANDLE )
		{
			char pSearchString[MAX_PATH];
			Q_snprintf( pSearchString, MAX_PATH, "%s\\*", pFilePath );

			// get the list of files
			pStartingFile = g_pFullFileSystem->FindFirstEx( pSearchString, "GAME", &list.m_hFind );
		}
		else
		{
			pStartingFile = g_pFullFileSystem->FindNext( list.m_hFind );
		}

		if ( !AddFilesInDirectory( list, pStartingFile, pFilePath, hCurrentDir, flStartTime, flDuration ) )
			return false;

		g_pFullFileSystem->FindClose( list.m_hFind );
		list.m_hFind = FILESYSTEM_INVALID_FIND_HANDLE;
		list.m_DirectoriesToCheck.Remove( list.m_DirectoriesToCheck.Head() );
	}
	list.m_bAssetScanComplete = true;
	return true;
}


//-----------------------------------------------------------------------------
// Asset cache iteration
//-----------------------------------------------------------------------------
bool CAssetCache::BeginAssetScan( AssetList_t hList, bool bForceRescan )
{
	CachedAssetList_t& list = m_CachedAssets[ (intp)hList ];
	if ( bForceRescan )
	{
		list.m_bAssetScanComplete = false;
		if ( list.m_hFind != FILESYSTEM_INVALID_FIND_HANDLE )
		{
			g_pFullFileSystem->FindClose( list.m_hFind );
			list.m_hFind = FILESYSTEM_INVALID_FIND_HANDLE;
		}
		list.m_DirectoriesToCheck.RemoveAll();
	}

	if ( list.m_bAssetScanComplete )
		return false;

	// This case occurs if we stopped the picker previously while in the middle of a scan
	if ( list.m_hFind != FILESYSTEM_INVALID_FIND_HANDLE )
		return true;

	list.m_AssetList.RemoveAll();
	list.m_pFileTree->ClearDirectories();

	// Add all files, determine which mod they are in.
	int i = list.m_DirectoriesToCheck.AddToTail();
	list.m_DirectoriesToCheck[i].m_DirName = list.m_pSubDir;
	list.m_DirectoriesToCheck[i].m_hDirHandle = list.m_pFileTree->GetRootDirectory();
	return true;
}


//-----------------------------------------------------------------------------
// Asset cache iteration
//-----------------------------------------------------------------------------
AssetList_t CAssetCache::FindAssetList( const char *pAssetType, const char *pSubDir, int nExtCount, const char **ppExt )
{
	CachedAssetList_t search( pSubDir, nExtCount, ppExt );
	int nIndex = m_CachedAssets.Find( search );
	if ( nIndex == m_CachedAssets.InvalidIndex() )
	{
		nIndex = m_CachedAssets.Insert( search );
		CachedAssetList_t &list = m_CachedAssets[nIndex];
		list.m_pSubDir = pSubDir;
		list.m_Ext.AddMultipleToTail( nExtCount, ppExt );
		list.m_hFind = FILESYSTEM_INVALID_FIND_HANDLE;
		list.m_bAssetScanComplete = false;
		list.m_pFileTree = new CAssetTreeView( NULL, "FolderFilter", pAssetType, pSubDir );
	}

	return (AssetList_t)(intp)nIndex;
}

CAssetTreeView* CAssetCache::GetFileTree( AssetList_t hList )
{
	if ( hList == ASSET_LIST_INVALID )
		return NULL;
	return m_CachedAssets[ (intp)hList ].m_pFileTree;
}

int CAssetCache::GetAssetCount( AssetList_t hList ) const
{
	if ( hList == ASSET_LIST_INVALID )
		return 0;
	return m_CachedAssets[ (intp)hList ].m_AssetList.Count();
}

const CAssetCache::CachedAssetInfo_t& CAssetCache::GetAsset( AssetList_t hList, int nIndex ) const
{
	Assert( nIndex < GetAssetCount(hList) );
	return m_CachedAssets[ (intp)hList ].m_AssetList[ nIndex ];
}



//-----------------------------------------------------------------------------
//
// Base asset Picker
//
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
// Sort by asset name
//-----------------------------------------------------------------------------
static int __cdecl AssetBrowserSortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
{
	bool bRoot1 = item1.kv->GetInt("root") != 0;
	bool bRoot2 = item2.kv->GetInt("root") != 0;
	if ( bRoot1 != bRoot2 )
		return bRoot1 ? -1 : 1;
	const char *pString1 = item1.kv->GetString("asset");
	const char *pString2 = item2.kv->GetString("asset");
	return Q_stricmp( pString1, pString2 );
}

static int __cdecl AssetBrowserModSortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
{
	int nMod1 = item1.kv->GetInt("modIndex", -1);
	int nMod2 = item2.kv->GetInt("modIndex", -1);
	if ( nMod1 != nMod2 )
		return nMod1 - nMod2;
	return AssetBrowserSortFunc( pPanel, item1, item2 );
}


//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CBaseAssetPicker::CBaseAssetPicker( vgui::Panel *pParent, const char *pAssetType, 
	const char *pExt, const char *pSubDir, const char *pTextType ) : 
	BaseClass( pParent, "AssetPicker" )
{
	m_bBuiltAssetList = false;
	m_pAssetType = pAssetType;
	m_pAssetTextType = pTextType;
	m_pAssetExt = pExt;
	m_pAssetSubDir = pSubDir;
	m_bFinishedAssetListScan = false;
	m_bFirstAssetScan = false;
	m_nMatchingAssets = 0;
	m_bSubDirCheck = true;
	m_hAssetList = ASSET_LIST_INVALID;
}


//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CBaseAssetPicker::~CBaseAssetPicker()
{
	SaveUserConfig();

	// Detach!
	m_pFileTree->RemoveActionSignalTarget( this );
	m_pFileTree->SetParent( (Panel*)NULL );
	m_pFileTree = NULL;
}


//-----------------------------------------------------------------------------
// Creates standard controls
//-----------------------------------------------------------------------------
void CBaseAssetPicker::CreateStandardControls( vgui::Panel *pParent, bool bAllowMultiselect )
{
	int nExtCount = 1 + m_ExtraAssetExt.Count();
	const char **ppExt = (const char **)_alloca( nExtCount * sizeof(const char *) );
	ppExt[0] = m_pAssetExt;
	if ( nExtCount > 1 )
	{
		memcpy( ppExt + 1, m_ExtraAssetExt.Base(), nExtCount - 1 );
	}

	m_hAssetList = s_AssetCache.FindAssetList( m_pAssetType, m_pAssetSubDir, nExtCount, ppExt );

	m_pAssetSplitter = new vgui::Splitter( pParent, "AssetSplitter", SPLITTER_MODE_HORIZONTAL, 1 );
	vgui::Panel *pSplitterTopSide = m_pAssetSplitter->GetChild( 0 );
	vgui::Panel *pSplitterBottomSide = m_pAssetSplitter->GetChild( 1 );

	// Combo box for mods
	m_pModSelector = new ComboBox( pSplitterTopSide, "ModFilter", 5, false );
	m_pModSelector->AddActionSignalTarget( this );

	// Rescan button
	m_pRescanButton = new Button( pSplitterTopSide, "RescanButton", "Rescan", this, "AssetRescan" );

	// file browser tree controls
	m_pFileTree = s_AssetCache.GetFileTree( m_hAssetList );
	m_pFileTree->SetParent( pSplitterTopSide );
	m_pFileTree->AddActionSignalTarget( this );

	m_pSubDirCheck = new CheckButton( pSplitterTopSide, "SubDirCheck", "Check subfolders for files?" );
	m_pSubDirCheck->SetSelected( true );
	m_pSubDirCheck->SetEnabled( false );
	m_pSubDirCheck->SetVisible( false );
	m_pSubDirCheck->AddActionSignalTarget( this );

	char pTemp[512];
	Q_snprintf( pTemp, sizeof(pTemp), "No .%s files", m_pAssetExt );
	m_pAssetBrowser = new vgui::ListPanel( pSplitterBottomSide, "AssetBrowser" );
 	m_pAssetBrowser->AddColumnHeader( 0, "mod", "Mod", 52, 0 );
	m_pAssetBrowser->AddColumnHeader( 1, "asset", m_pAssetType, 128, ListPanel::COLUMN_RESIZEWITHWINDOW );
    m_pAssetBrowser->SetSelectIndividualCells( false );
    m_pAssetBrowser->SetMultiselectEnabled( bAllowMultiselect );
	m_pAssetBrowser->SetEmptyListText( pTemp );
 	m_pAssetBrowser->SetDragEnabled( true );
	m_pAssetBrowser->AddActionSignalTarget( this );
	m_pAssetBrowser->SetSortFunc( 0, AssetBrowserModSortFunc );
	m_pAssetBrowser->SetSortFunc( 1, AssetBrowserSortFunc );
	m_pAssetBrowser->SetSortColumn( 1 );
						 
	// filter selection
	m_pFilter = new TextEntry( pSplitterBottomSide, "FilterList" );
	m_pFilter->AddActionSignalTarget( this );

	// full path
	m_pFullPath = new TextEntry( pSplitterBottomSide, "FullPath" );
	m_pFullPath->SetEnabled( false );
	m_pFullPath->SetEditable( false );

	m_nCurrentModFilter = -1;
}


//-----------------------------------------------------------------------------
// Reads user config settings
//-----------------------------------------------------------------------------
void CBaseAssetPicker::ApplyUserConfigSettings( KeyValues *pUserConfig )
{
	BaseClass::ApplyUserConfigSettings( pUserConfig );

	// Populates the mod list names
	RefreshAssetList();

	const char *pFilter = pUserConfig->GetString( "filter", "" );
	m_FolderFilter = pUserConfig->GetString( "folderfilter", "" );
	const char *pMod = pUserConfig->GetString( "mod", "" );
	SetFilter( pFilter );
	m_nCurrentModFilter = -1;
	if ( pMod && pMod[0] )
	{
		int nCount = s_AssetCache.ModCount();
		for ( int i = 0; i < nCount; ++i )
		{
			const CAssetCache::ModInfo_t& modInfo = s_AssetCache.ModInfo( i );
			if ( Q_stricmp( pMod, modInfo.m_ModName ) )
				continue;

			int nItemCount = m_pModSelector->GetItemCount();
			for ( int j = 0; j < nItemCount; ++j )
			{
				int nItemID = m_pModSelector->GetItemIDFromRow( j );
				KeyValues *kv = m_pModSelector->GetItemUserData( nItemID );
				int nModIndex = kv->GetInt( "mod" );
				if ( nModIndex == i )
				{
					m_nCurrentModFilter = i;
					m_pModSelector->ActivateItem( nItemID );
					break;
				}
			}
			break;
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: returns user config settings for this control
//-----------------------------------------------------------------------------
void CBaseAssetPicker::GetUserConfigSettings( KeyValues *pUserConfig )
{
	BaseClass::GetUserConfigSettings( pUserConfig );
	pUserConfig->SetString( "filter", m_Filter );
	pUserConfig->SetString( "folderfilter", m_FolderFilter );
	pUserConfig->SetString( "mod", ( m_nCurrentModFilter >= 0 ) ? 
		s_AssetCache.ModInfo( m_nCurrentModFilter ).m_ModName : "" );
}


//-----------------------------------------------------------------------------
// Purpose: optimization, return true if this control has any user config settings
//-----------------------------------------------------------------------------
bool CBaseAssetPicker::HasUserConfigSettings()
{
	return true;
}


//-----------------------------------------------------------------------------
// Allows the picker to browse multiple asset types
//-----------------------------------------------------------------------------
void CBaseAssetPicker::AddExtension( const char *pExtension )
{
	m_ExtraAssetExt.AddToTail( pExtension );
}


//-----------------------------------------------------------------------------
// Is multiselect enabled?
//-----------------------------------------------------------------------------
bool CBaseAssetPicker::IsMultiselectEnabled() const
{
	return m_pAssetBrowser->IsMultiselectEnabled(); 
}


//-----------------------------------------------------------------------------
// Sets the initial selected asset
//-----------------------------------------------------------------------------
void CBaseAssetPicker::SetInitialSelection( const char *pAssetName )
{
	// This makes it so the background list filling code will automatically select this asset when it gets to it.
	m_SelectedAsset = pAssetName;		
								   
	if ( pAssetName )
	{	
		// Sometimes we've already refreshed our list with a bunch of cached resources and the item is already in the list,
		// so in that case just select it here.
		int cnt = m_pAssetBrowser->GetItemCount();
		for ( int i=0; i < cnt; i++ )
		{
			KeyValues *kv = m_pAssetBrowser->GetItem( i );
			if ( !kv )
				continue;
			
			const char *pTestAssetName = kv->GetString( "asset" );
			if ( !pTestAssetName )
				continue;
				
			if ( Q_stricmp( pTestAssetName, pAssetName ) == 0 )
			{
				m_pAssetBrowser->SetSelectedCell( i, 0 );
				break;
			}			
		}
	}
}

	
//-----------------------------------------------------------------------------
// Set/get the filter
//-----------------------------------------------------------------------------
void CBaseAssetPicker::SetFilter( const char *pFilter )
{
	m_Filter = pFilter;
	m_pFilter->SetText( pFilter );
}

const char *CBaseAssetPicker::GetFilter()
{
	return m_Filter;
}

	
//-----------------------------------------------------------------------------
// Purpose: called to open
//-----------------------------------------------------------------------------
void CBaseAssetPicker::Activate()
{
	RefreshAssetList();
	RequestFilterFocus();
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CBaseAssetPicker::OnKeyCodePressed( KeyCode code )
{
	if (( code == KEY_UP ) || ( code == KEY_DOWN ) || ( code == KEY_PAGEUP ) || ( code == KEY_PAGEDOWN ))
	{
		KeyValues *pMsg = new KeyValues("KeyCodePressed", "code", code);
		vgui::ipanel()->SendMessage( m_pAssetBrowser->GetVPanel(), pMsg, GetVPanel());
		pMsg->deleteThis();
	}
	else
	{
		BaseClass::OnKeyCodePressed( code );
	}
}


//-----------------------------------------------------------------------------
// Is a particular asset visible?
//-----------------------------------------------------------------------------
bool CBaseAssetPicker::IsAssetVisible( int nAssetIndex )
{
	const CAssetCache::CachedAssetInfo_t& info = s_AssetCache.GetAsset( m_hAssetList, nAssetIndex );

	// Filter based on active mod
	int nModIndex = info.m_nModIndex;
	if ( ( m_nCurrentModFilter >= 0 ) && ( m_nCurrentModFilter != nModIndex ) )
		return false;

	// Filter based on name
	const char *pAssetName = info.m_AssetName;
	if ( !Q_strcmp( pAssetName, m_SelectedAsset ) )
		return true;

	if ( m_Filter.Length() && !Q_stristr( pAssetName, m_Filter.Get() ) )
		return false;

	// Filter based on folder
	if ( m_FolderFilter.Length() && Q_strnicmp( pAssetName, m_FolderFilter.Get(), m_FolderFilter.Length() ) )
		return false;

	// Filter based on subdirectory check
	if ( !m_bSubDirCheck && strchr( pAssetName + m_FolderFilter.Length(), '\\' ) )
		return false;

	return true;
}
		

//-----------------------------------------------------------------------------
// Adds an asset from the cache to the list
//-----------------------------------------------------------------------------
void CBaseAssetPicker::AddAssetToList( int nAssetIndex )
{
	const CAssetCache::CachedAssetInfo_t& info = s_AssetCache.GetAsset( m_hAssetList, nAssetIndex );

	bool bInRootDir = !strchr( info.m_AssetName, '\\' ) && !strchr( info.m_AssetName, '/' );

	KeyValues *kv = new KeyValues( "node", "asset", info.m_AssetName );
	kv->SetString( "mod", s_AssetCache.ModInfo( info.m_nModIndex ).m_ModName );
	kv->SetInt( "modIndex", info.m_nModIndex );
	kv->SetInt( "root", bInRootDir );
	int nItemID = m_pAssetBrowser->AddItem( kv, 0, false, false );
	kv->deleteThis();
	
	if ( m_pAssetBrowser->GetSelectedItemsCount() == 0 && !Q_strcmp( m_SelectedAsset, info.m_AssetName ) )
	{
		m_pAssetBrowser->SetSelectedCell( nItemID, 0 );
	}

	KeyValues *pDrag = new KeyValues( "drag", "text", info.m_AssetName );
	if ( m_pAssetTextType )
	{
		pDrag->SetString( "texttype", m_pAssetTextType );
	}
	m_pAssetBrowser->SetItemDragData( nItemID, pDrag );

	int i = m_AssetList.AddToTail( );
	m_AssetList[i].m_nAssetIndex = nAssetIndex;
	m_AssetList[i].m_nItemId = nItemID;

	bool bIsVisible = IsAssetVisible( i );
	m_pAssetBrowser->SetItemVisible( nItemID, bIsVisible );
	if ( bIsVisible )
	{
		++m_nMatchingAssets;
	}
}


//-----------------------------------------------------------------------------
// Continues to build the asset list
//-----------------------------------------------------------------------------
void CBaseAssetPicker::OnTick()
{
	BaseClass::OnTick();

	int nPreAssetCount = s_AssetCache.GetAssetCount( m_hAssetList );

	// Stop getting called back once all assets have been found
	float flTime = m_bFirstAssetScan ? ASSET_LIST_DIRECTORY_INITIAL_SEARCH_TIME : ASSET_LIST_DIRECTORY_SEARCH_TIME;
	bool bFinished = s_AssetCache.ContinueSearchForAssets( m_hAssetList, flTime );

	if ( m_bFirstAssetScan )
	{
		m_pFileTree->OpenRoot();
	}
	m_bFirstAssetScan = false;

	int nPostAssetCount = s_AssetCache.GetAssetCount( m_hAssetList );
	for ( int i = nPreAssetCount; i < nPostAssetCount; ++i )
	{
		AddAssetToList( i );
	}

	if ( bFinished )
	{
		vgui::ivgui()->RemoveTickSignal( GetVPanel() );
		m_bFinishedAssetListScan = true;

		// Copy the current folder filter.. this is necessary
		// to finally select the folder loaded from the user config settings
		// in the free view (since it's finally populated at this point)
		// NOTE: if a user has changed the folder filter between startup
		// and this point, this should still work since m_FolderFilter should be updated
		m_pFileTree->SelectFolder( m_pAssetSubDir, m_FolderFilter );
		RefreshAssetList( );
		return;
	}

	UpdateAssetColumnHeader();
}

	
//-----------------------------------------------------------------------------
// Builds the Bsp name list
//-----------------------------------------------------------------------------
void CBaseAssetPicker::BuildAssetNameList( )
{
	if ( m_bBuiltAssetList )
		return;

	m_bBuiltAssetList = true;
	m_nMatchingAssets = 0;
	m_nCurrentModFilter = -1;

	// Build the list of known mods if we haven't 
	s_AssetCache.BuildModList();

	m_pModSelector->RemoveAll();
	m_pModSelector->AddItem( "All Mods", new KeyValues( "Mod", "mod", -1 ) );
	int nModCount = s_AssetCache.ModCount();
	for ( int i = 0; i < nModCount; ++i )
	{
		const char *pModName = s_AssetCache.ModInfo( i ).m_ModName;
		m_pModSelector->AddItem( pModName, new KeyValues( "Mod", "mod", i ) );
	}
	m_pModSelector->ActivateItemByRow( 0 );

	// If we've already read in
	if ( s_AssetCache.BeginAssetScan( m_hAssetList ) )
	{
		m_bFirstAssetScan = true;
		m_bFinishedAssetListScan = false;
		vgui::ivgui()->AddTickSignal( GetVPanel(), 10 );
	}
	else
	{
		m_bFirstAssetScan = false;
		m_bFinishedAssetListScan = true;
	}

	int nAssetCount = s_AssetCache.GetAssetCount( m_hAssetList );
	for ( int i = 0; i < nAssetCount; ++i )
	{
		AddAssetToList( i );
	}
}


//-----------------------------------------------------------------------------
// Rescan assets
//-----------------------------------------------------------------------------
void CBaseAssetPicker::RescanAssets()
{
	m_pAssetBrowser->RemoveAll();
	m_AssetList.RemoveAll();
	s_AssetCache.BeginAssetScan( m_hAssetList, true );
	m_bFirstAssetScan = true;
	m_nMatchingAssets = 0;

	if ( m_bFinishedAssetListScan )
	{
		m_bFinishedAssetListScan = false;
		vgui::ivgui()->AddTickSignal( GetVPanel(), 10 );
	}
}


//-----------------------------------------------------------------------------
// Returns the mod path to the item index
//-----------------------------------------------------------------------------
const char *CBaseAssetPicker::GetModPath( int nModIndex )
{
	return s_AssetCache.ModInfo( nModIndex ).m_Path.Get();
}


//-----------------------------------------------------------------------------
// Command handler
//-----------------------------------------------------------------------------
void CBaseAssetPicker::OnCommand( const char *pCommand )
{
	if ( !Q_stricmp( pCommand, "AssetRescan" ) )
	{
		RescanAssets();
		return;
	}

	BaseClass::OnCommand( pCommand );
}


//-----------------------------------------------------------------------------
// Update column headers
//-----------------------------------------------------------------------------
void CBaseAssetPicker::UpdateAssetColumnHeader( )
{
	char pColumnTitle[512];
	Q_snprintf( pColumnTitle, sizeof(pColumnTitle), "%s (%d/%d)%s",
		m_pAssetType, m_nMatchingAssets, m_AssetList.Count(), m_bFinishedAssetListScan ? "" : " ..." );
	m_pAssetBrowser->SetColumnHeaderText( 1, pColumnTitle );
}

	
//-----------------------------------------------------------------------------
// Request focus of the filter box
//-----------------------------------------------------------------------------
void CBaseAssetPicker::RequestFilterFocus()
{
    if ( m_Filter.Length() )
	{
		m_pFilter->SelectAllOnFirstFocus( true );
	}
	m_pFilter->RequestFocus();
}


//-----------------------------------------------------------------------------
// Purpose: refreshes the asset list
//-----------------------------------------------------------------------------
void CBaseAssetPicker::RefreshAssetList( )
{
	BuildAssetNameList();

	// Check the filter matches
	int nCount = m_AssetList.Count();
	m_nMatchingAssets = 0;
	for ( int i = 0; i < nCount; ++i )
	{
		// Filter based on active mod
		bool bIsVisible = IsAssetVisible( i );
		m_pAssetBrowser->SetItemVisible( m_AssetList[i].m_nItemId, bIsVisible );
		if ( bIsVisible )
		{
			++m_nMatchingAssets;
		}
	}

	UpdateAssetColumnHeader();
	m_pAssetBrowser->SortList();

	if ( ( m_pAssetBrowser->GetSelectedItemsCount() == 0 ) && ( m_pAssetBrowser->GetItemCount() > 0 ) )
	{
		// Invoke a callback if the next selection will be a 'default' selection
		OnNextSelectionIsDefault();
		int nItemID = m_pAssetBrowser->GetItemIDFromRow( 0 );
		m_pAssetBrowser->SetSelectedCell( nItemID, 0 );
	}

	m_pFileTree->RefreshFileList();
}


//-----------------------------------------------------------------------------
// Purpose: refreshes dialog on file folder changing
//-----------------------------------------------------------------------------
void CBaseAssetPicker::OnFileSelected()
{
	// update list
	const char *pFolderFilter = "";
	int iItem = m_pFileTree->GetFirstSelectedItem();
	if ( iItem >= 0 )
	{
		KeyValues *pkv = m_pFileTree->GetItemData( iItem );
		pFolderFilter = pkv->GetString( "path" );

		// The first keys are always the subdir
		pFolderFilter += Q_strlen( m_pAssetSubDir );
		if ( *pFolderFilter )
		{
			++pFolderFilter;
		}
	}

	if ( Q_stricmp( pFolderFilter, m_FolderFilter.Get() ) )
	{
		int nLen = Q_strlen( pFolderFilter );
		m_FolderFilter = pFolderFilter;
		if ( nLen > 0 )
		{
			m_FolderFilter += '\\';
		}
		RefreshAssetList();
	}
}


//-----------------------------------------------------------------------------
// Purpose: refreshes dialog on text changing
//-----------------------------------------------------------------------------
void CBaseAssetPicker::OnTextChanged( KeyValues *pKeyValues )
{
	vgui::Panel *pSource = (vgui::Panel*)pKeyValues->GetPtr( "panel" );
	if ( pSource == m_pFilter )
	{
		int nLength = m_pFilter->GetTextLength();
		char *pNewFilter = (char*)_alloca( (nLength+1) * sizeof(char) );
		if ( nLength > 0 )
		{
			m_pFilter->GetText( pNewFilter, nLength+1 );
		}
		else
		{
			pNewFilter[0] = 0;
		}
		if ( Q_stricmp( pNewFilter, m_Filter.Get() ) )
		{
			m_Filter.SetLength( nLength );
			m_Filter = pNewFilter;
			RefreshAssetList();
		}
		return;
	}

	if ( pSource == m_pModSelector )
	{
		KeyValues *pKeyValuesActive = m_pModSelector->GetActiveItemUserData();
		if ( pKeyValuesActive )
		{
			m_nCurrentModFilter = pKeyValuesActive->GetInt( "mod", -1 );
			RefreshAssetList();
		}
		return;
	}
}


//-----------------------------------------------------------------------------
// Purpose: Updates preview when an item is selected
//-----------------------------------------------------------------------------
void CBaseAssetPicker::OnItemSelected( KeyValues *kv )
{
	Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL );
	if ( pPanel == m_pAssetBrowser )
	{
		int nCount = GetSelectedAssetCount();
		Assert( nCount > 0 );
		const char *pSelectedAsset = GetSelectedAsset( nCount - 1 );

		// Fill in the full path
		int nModIndex = GetSelectedAssetModIndex();
		char pBuf[MAX_PATH];
		Q_snprintf( pBuf, sizeof(pBuf), "%s\\%s\\%s", 
			s_AssetCache.ModInfo( nModIndex ).m_Path.Get(), m_pAssetSubDir, pSelectedAsset );
		Q_FixSlashes( pBuf );
		m_pFullPath->SetText( pBuf );

		surface()->SetCursor( dc_waitarrow );
		OnSelectedAssetPicked( pSelectedAsset );
		return;
	}
}

void CBaseAssetPicker::OnCheckButtonChecked( KeyValues *kv )
{
	vgui::Panel *pSource = (vgui::Panel*)kv->GetPtr( "panel" );
	if ( pSource == m_pSubDirCheck )
	{
		m_bSubDirCheck = m_pSubDirCheck->IsSelected();
		RefreshAssetList();
	}
}


//-----------------------------------------------------------------------------
// Returns the selceted asset count
//-----------------------------------------------------------------------------
int CBaseAssetPicker::GetSelectedAssetCount()
{
	return m_pAssetBrowser->GetSelectedItemsCount();
}

	
//-----------------------------------------------------------------------------
// Returns the selceted asset name
//-----------------------------------------------------------------------------
const char *CBaseAssetPicker::GetSelectedAsset( int nAssetIndex )
{
	int nSelectedAssetCount = m_pAssetBrowser->GetSelectedItemsCount();
	if ( nAssetIndex < 0 )
	{
		nAssetIndex = nSelectedAssetCount - 1;
	}
	if ( nSelectedAssetCount <= nAssetIndex || nAssetIndex < 0 )
		return NULL;

	int nIndex = m_pAssetBrowser->GetSelectedItem( nAssetIndex );
	KeyValues *pItemKeyValues = m_pAssetBrowser->GetItem( nIndex );
	return pItemKeyValues->GetString( "asset" );
}

	
//-----------------------------------------------------------------------------
// Returns the selceted asset mod index
//-----------------------------------------------------------------------------
int CBaseAssetPicker::GetSelectedAssetModIndex( )
{
	if ( m_pAssetBrowser->GetSelectedItemsCount() == 0 )
		return 0;

	int nIndex = m_pAssetBrowser->GetSelectedItem( 0 );
	KeyValues *pItemKeyValues = m_pAssetBrowser->GetItem( nIndex );
	return pItemKeyValues->GetInt( "modIndex" );
}


//-----------------------------------------------------------------------------
//
// Purpose: Modal picker frame
//
//-----------------------------------------------------------------------------
CBaseAssetPickerFrame::CBaseAssetPickerFrame( vgui::Panel *pParent ) : 
	BaseClass( pParent, "AssetPickerFrame" )
{
	m_pContextKeyValues = NULL;
	SetDeleteSelfOnClose( true );
	m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Open", this, "Open" );
	m_pCancelButton = new Button( this, "CancelButton", "#FileOpenDialog_Cancel", this, "Cancel" );
	SetBlockDragChaining( true );
}

CBaseAssetPickerFrame::~CBaseAssetPickerFrame()
{
	CleanUpMessage();
}


//-----------------------------------------------------------------------------
// Allows the derived class to create the picker
//-----------------------------------------------------------------------------
void CBaseAssetPickerFrame::SetAssetPicker( CBaseAssetPicker* pPicker )
{
	m_pPicker = pPicker;
	m_pPicker->AddActionSignalTarget( this );
}

	
//-----------------------------------------------------------------------------
// Deletes the message
//-----------------------------------------------------------------------------
void CBaseAssetPickerFrame::CleanUpMessage()
{
	if ( m_pContextKeyValues )
	{
		m_pContextKeyValues->deleteThis();
		m_pContextKeyValues = NULL;
	}
}


//-----------------------------------------------------------------------------
// Sets the initial selected asset
//-----------------------------------------------------------------------------
void CBaseAssetPickerFrame::SetInitialSelection( const char *pAssetName )
{	
	m_pPicker->SetInitialSelection( pAssetName );
}
	

//-----------------------------------------------------------------------------
// Set/get the filter
//-----------------------------------------------------------------------------
void CBaseAssetPickerFrame::SetFilter( const char *pFilter )
{
	m_pPicker->SetFilter( pFilter );
}

const char *CBaseAssetPickerFrame::GetFilter()
{
	return m_pPicker->GetFilter( );
}


//-----------------------------------------------------------------------------
// Purpose: Activate the dialog
//-----------------------------------------------------------------------------
void CBaseAssetPickerFrame::DoModal( KeyValues *pKeyValues )
{
	BaseClass::DoModal();
	CleanUpMessage();
	m_pContextKeyValues = pKeyValues;
	m_pPicker->Activate();
}


//-----------------------------------------------------------------------------
// Posts a message (passing the key values)
//-----------------------------------------------------------------------------
void CBaseAssetPickerFrame::PostMessageAndClose( KeyValues *pKeyValues )
{
	if ( m_pContextKeyValues )
	{
		pKeyValues->AddSubKey( m_pContextKeyValues );
		m_pContextKeyValues = NULL;
	}
	CloseModal();
	PostActionSignal( pKeyValues );
}


//-----------------------------------------------------------------------------
// On command
//-----------------------------------------------------------------------------
void CBaseAssetPickerFrame::OnCommand( const char *pCommand )
{
	if ( !Q_stricmp( pCommand, "Open" ) )
	{
		KeyValues *pActionKeys = new KeyValues( "AssetSelected" );
		if ( !m_pPicker->IsMultiselectEnabled() )
		{
			const char *pAssetName = m_pPicker->GetSelectedAsset( );
			pActionKeys->SetString( "asset", pAssetName );
		}
		else
		{
			char pBuf[512];
			KeyValues *pAssetKeys = pActionKeys->FindKey( "assets", true );
			int nCount = m_pPicker->GetSelectedAssetCount();
			for ( int i = 0; i < nCount; ++i )
			{
				Q_snprintf( pBuf, sizeof(pBuf), "asset%d", i );
				pAssetKeys->SetString( pBuf, m_pPicker->GetSelectedAsset( i ) );
			}
		}
		PostMessageAndClose( pActionKeys );
		return;
	}

	if ( !Q_stricmp( pCommand, "Cancel" ) )
	{
		CloseModal();
		return;
	}

	BaseClass::OnCommand( pCommand );
}