//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include "vmtcheck_util.h"
#include "tier0/dbg.h"
#include "utldict.h"
#include "filesystem.h"
#include "FileSystem_Tools.h"
#include "KeyValues.h"
#include "cmdlib.h"

bool uselogfile = false;

struct AnalysisData
{
	CUtlSymbolTable				symbols;
};

static AnalysisData g_Analysis;


static bool spewed = false;

SpewRetval_t SpewFunc( SpewType_t type, char const *pMsg )
{	
	spewed = true;

	printf( "%s", pMsg );
	OutputDebugString( pMsg );
	
	if ( type == SPEW_ERROR )
	{
		printf( "\n" );
		OutputDebugString( "\n" );
	}

	return SPEW_CONTINUE;
}



//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void printusage( void )
{
	vprint( 0, "usage:  vmtcheck <materials/.vmt root directory>\n\
		\t-v = verbose output\n\
		\t-l = log to file log.txt\n\
		\ne.g.:  vmtcheck -l u:/hl2/hl2/materials\n" );

	// Exit app
	exit( 1 );
}

void BuildFileList_R( CUtlVector< CUtlSymbol >& files, char const *dir, char const *extension )
{
	WIN32_FIND_DATA wfd;

	char directory[ 256 ];
	char filename[ 256 ];
	HANDLE ff;

	sprintf( directory, "%s\\*.*", dir );

	if ( ( ff = FindFirstFile( directory, &wfd ) ) == INVALID_HANDLE_VALUE )
		return;

	int extlen = strlen( extension );

	do
	{
		if ( wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
		{

			if ( wfd.cFileName[ 0 ] == '.' )
				continue;

			// Recurse down directory
			sprintf( filename, "%s\\%s", dir, wfd.cFileName );
			BuildFileList_R( files, filename, extension );
		}
		else
		{
			int len = strlen( wfd.cFileName );
			if ( len > extlen )
			{
				if ( !stricmp( &wfd.cFileName[ len - extlen ], extension ) )
				{
					char filename[ MAX_PATH ];
					Q_snprintf( filename, sizeof( filename ), "%s\\%s", dir, wfd.cFileName );
					_strlwr( filename );

					Q_FixSlashes( filename );

					CUtlSymbol sym = g_Analysis.symbols.AddString( filename );
					files.AddToTail( sym );

					if ( !( files.Count() % 3000 ) )
					{
						vprint( 0, "...found %i .vmt files\n", files.Count() );
					}
				}
			}
		}
	} while ( FindNextFile( ff, &wfd ) );
}

void BuildFileList( CUtlVector< CUtlSymbol >& files, char const *rootdir, char const *extension )
{
	files.RemoveAll();
	BuildFileList_R( files, rootdir, extension );
}

bool ValidateVMTFile( char const *vmtname, int offset )
{
	bool valid = true;

	KeyValues *kv = new KeyValues( "Test" );
	if ( kv->LoadFromFile( g_pFileSystem, &vmtname[offset] ) )
	{
		// Do any custom checking here...
	}
	else
	{
		valid = false;
	}
	kv->deleteThis();
	
	return valid;
}

void ProcessMaterialsDirectory( char const *basedir )
{
	vprint( 0, "building .vmt list\n" );

	CUtlVector< CUtlSymbol > vmts;

	BuildFileList( vmts, basedir, ".vmt" );

	vprint( 0, "found %i .vmt files\n\n", vmts.Count() );

	int offset = strlen( basedir ) + 1;
	offset -= strlen( "materials/" );
	if ( offset < 0 )
	{
		Error( "Bogus offset\n" );
	}

	// Now iterate vmts and load into memory, etc.

	int c = vmts.Count();
	int valid = 0;
	for ( int i = 0; i < c; i++ )
	{
		CUtlSymbol& sym = vmts[ i ];

		char const *vmtfile = g_Analysis.symbols.String( sym );

		if ( verbose )
		{
			vprint( 0, "checking %i .vmt %s\n", i, vmtfile );
		}

		spewed = false;
		if ( ValidateVMTFile( vmtfile, offset ) )
		{
			if ( !spewed )
			{
				valid++;
			}
		}

		if ( i > 0 && !( i % 1000 ) )
		{
			vprint( 0, "Analyzed %i .vmt files (%.2f %%%%)\n", i, 100.0f * (float)i/(float)c );
		}
	}

	int ecount = c - valid;
	vprint( 0, "\nSummary:  found %i/%i (%.2f percent) .vmt errors\n", ecount, c, 100.0 * ecount / max( c, 1 ) );

}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CheckLogFile( void )
{
	if ( uselogfile )
	{
		_unlink( "log.txt" );
		vprint( 0, "    Outputting to log.txt\n" );
	}
}

void PrintHeader()
{
	vprint( 0, "Valve Software - vmtcheck.exe (%s)\n", __DATE__ );
	vprint( 0, "--- VMT File Consistency Checker ---\n" );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : argc - 
//			argv[] - 
// Output : int
//-----------------------------------------------------------------------------
int main( int argc, char* argv[] )
{
	SpewOutputFunc( SpewFunc );
	SpewActivate( "vmtcheck", 2 );

	int i=1;
	for ( i ; i<argc ; i++)
	{
		if ( argv[ i ][ 0 ] == '-' )
		{
			switch( argv[ i ][ 1 ] )
			{
			case 'l':
				uselogfile = true;
				break;
			case 'v':
				verbose = true;
				break;
			default:
				printusage();
				break;
			}
		}
	}

	if ( argc < 2 || ( i != argc ) )
	{
		PrintHeader();
		printusage();
	}

	CheckLogFile();

	PrintHeader();

	vprint( 0, "    Looking for messed up .vmt files...\n" );

	char vmtdir[ 256 ];
	strcpy( vmtdir, argv[ i - 1 ] );

	if ( !strstr( vmtdir, "materials" ) )
	{
		vprint( 0, "Materials dir %s looks invalid (format:  u:/tf2/hl2/materials)\n", vmtdir );

		return 0;
	}

	char workingdir[ 256 ];
	workingdir[0] = 0;
	Q_getwd( workingdir, sizeof( workingdir ) );

	// If they didn't specify -game on the command line, use VPROJECT.
	CmdLib_InitFileSystem( workingdir );

	vprint( 0, "game dir %s\nmaterials dir %s\n\n",
		gamedir,
		vmtdir );

	Q_StripTrailingSlash( vmtdir );
	ProcessMaterialsDirectory( vmtdir );

	FileSystem_Term();

	return 0;
}