//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//
#include <stdio.h>
#include <windows.h>
#include "tier0/dbg.h"
#include "utldict.h"
#include "filesystem.h"
#include "KeyValues.h"
#include "cmdlib.h"
#include "interface.h"
#include "imysqlwrapper.h"
#include "../bugreporter/trktool.h"
#include "utlbuffer.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <direct.h>

bool uselogfile = false;

static bool spewed = false;

static char workingdir[ 256 ];

#define SCR_TYPE 1

#define DEFAULT_DBMS		"tracker"
#define DEFAULT_USERNAME	"PublicUser"
#define DEFAULT_PASSWORD	DEFAULT_USERNAME

#define BUG_REPOSITORY_FMT		"\\\\fileserver\\bugs\\public\\%s\\%s\\%s.zip"

struct BugField_t
{
	BugField_t()
	{
		value[ 0 ] = 0;
		numvalue = 0;
		trkfield[ 0 ] = 0;
		isdesc = false;
		isnumeric = false;
	}

	char	value[ 8192 ];
	int		numvalue;
	char	trkfield[ 256 ];
	bool	isdesc;
	bool	isnumeric;
};

#define SQL_SETVALUE( fieldname, sqlcolumn )	Q_strncpy( bug.m_pBug->fieldname, sqlcolumn.String(), sizeof( bug.m_pBug->fieldname ) );
#define SQL_SETVALUESTRING( fieldname, str )	Q_strncpy( bug.m_pBug->fieldname, str, sizeof( bug.m_pBug->fieldname ) );


class CBugReporter
{
public:

	CBugReporter();
	virtual ~CBugReporter();

	// Initialize and login with default username/password for this computer (from resource/bugreporter.res)
	virtual bool		Init( char const *projectname );
	virtual void		Shutdown();

	virtual bool		IsPublicUI() { return false; }

// Submission API
	virtual void		StartNewBugReport();
	virtual void		CancelNewBugReport();
	virtual bool		CommitBugReport( int& bugSubmissionId );

	virtual void		AddField( char const *fieldname, char const *value, bool isdesc = false );
	virtual void		AddNumericField( char const *fieldname, int value );

private:

	void				ReportError(TRK_UINT rc, char const *func, char const *msg );
	TRK_UINT			Login(TRK_HANDLE* pTrkHandle, char const *projectname );

	void				SubstituteBugId( int bugid, char *out, int outlen, CUtlBuffer& src );

	TRK_HANDLE					trkHandle;
	TRK_RECORD_HANDLE			trkRecHandle;

public:
	CUtlVector< BugField_t	>	m_Fields;
};

void CBugReporter::AddField( char const *fieldname, char const *value, bool isdesc /*=false*/ )
{
	BugField_t fld;
	Q_strncpy( fld.value, value, sizeof( fld.value ) );
	fld.isnumeric = false;
	Q_strncpy( fld.trkfield, fieldname, sizeof( fld.trkfield ) );
	fld.isdesc = isdesc;

	m_Fields.AddToTail( fld );
}

void CBugReporter::AddNumericField( char const *fieldname, int value )
{
	BugField_t fld;
	fld.numvalue = value;
	fld.isnumeric = true;
	Q_strncpy( fld.trkfield, fieldname, sizeof( fld.trkfield ) );
	fld.isdesc = false;

	m_Fields.AddToTail( fld );
}

CBugReporter::CBugReporter()
{
	trkHandle		= (TRK_HANDLE)0;
	trkRecHandle	= (TRK_RECORD_HANDLE)0;
}

CBugReporter::~CBugReporter()
{
	m_Fields.RemoveAll();
}

struct TRKELookup
{
	unsigned int id;
	char const *str;
};

#define TRKERROR( id )	{ id, #id }

static TRKELookup g_Lookup[] =
{
	TRKERROR( TRK_SUCCESS ),
	TRKERROR( TRK_E_VERSION_MISMATCH ),
	TRKERROR( TRK_E_OUT_OF_MEMORY ),
	TRKERROR( TRK_E_BAD_HANDLE ),
	TRKERROR( TRK_E_BAD_INPUT_POINTER ),
	TRKERROR( TRK_E_BAD_INPUT_VALUE ),
	TRKERROR( TRK_E_DATA_TRUNCATED ),
	TRKERROR( TRK_E_NO_MORE_DATA ),
	TRKERROR( TRK_E_LIST_NOT_INITIALIZED ),
	TRKERROR( TRK_E_END_OF_LIST ),
	TRKERROR( TRK_E_NOT_LOGGED_IN ),
	TRKERROR( TRK_E_SERVER_NOT_PREPARED ),
	TRKERROR( TRK_E_BAD_DATABASE_VERSION ),
	TRKERROR( TRK_E_UNABLE_TO_CONNECT ),
	TRKERROR( TRK_E_UNABLE_TO_DISCONNECT ),
	TRKERROR( TRK_E_UNABLE_TO_START_TIMER ),
	TRKERROR( TRK_E_NO_DATA_SOURCES ),
	TRKERROR( TRK_E_NO_PROJECTS ),
	TRKERROR( TRK_E_WRITE_FAILED ),
	TRKERROR( TRK_E_PERMISSION_DENIED ),
	TRKERROR( TRK_E_SET_FIELD_DENIED ),
	TRKERROR( TRK_E_ITEM_NOT_FOUND ),
	TRKERROR( TRK_E_CANNOT_ACCESS_DATABASE ),
	TRKERROR( TRK_E_CANNOT_ACCESS_QUERY ),
	TRKERROR( TRK_E_CANNOT_ACCESS_INTRAY ),
	TRKERROR( TRK_E_CANNOT_OPEN_FILE ),
	TRKERROR( TRK_E_INVALID_DBMS_TYPE ),
	TRKERROR( TRK_E_INVALID_RECORD_TYPE ),
	TRKERROR( TRK_E_INVALID_FIELD ),
	TRKERROR( TRK_E_INVALID_CHOICE ),
	TRKERROR( TRK_E_INVALID_USER ),
	TRKERROR( TRK_E_INVALID_SUBMITTER ),
	TRKERROR( TRK_E_INVALID_OWNER ),
	TRKERROR( TRK_E_INVALID_DATE ),
	TRKERROR( TRK_E_INVALID_STORED_QUERY ),
	TRKERROR( TRK_E_INVALID_MODE ),
	TRKERROR( TRK_E_INVALID_MESSAGE ),
	TRKERROR( TRK_E_VALUE_OUT_OF_RANGE ),
	TRKERROR( TRK_E_WRONG_FIELD_TYPE ),
	TRKERROR( TRK_E_NO_CURRENT_RECORD ),
	TRKERROR( TRK_E_NO_CURRENT_NOTE ),
	TRKERROR( TRK_E_NO_CURRENT_ATTACHED_FILE ),
	TRKERROR( TRK_E_NO_CURRENT_ASSOCIATION ),
	TRKERROR( TRK_E_NO_RECORD_BEGIN ),
	TRKERROR( TRK_E_NO_MODULE ),
	TRKERROR( TRK_E_USER_CANCELLED ),
	TRKERROR( TRK_E_SEMAPHORE_TIMEOUT ),
	TRKERROR( TRK_E_SEMAPHORE_ERROR ),
	TRKERROR( TRK_E_INVALID_SERVER_NAME ),
	TRKERROR( TRK_E_NOT_LICENSED )
};

void CBugReporter::ReportError(TRK_UINT rc, char const *func, char const *msg )
{
    if ( rc != TRK_SUCCESS )
	{
		switch (rc)
		{
		case TRK_E_ITEM_NOT_FOUND:
		    Msg( "%s %s was not found!\n", func,  msg );
			break;
		case TRK_E_INVALID_FIELD:
			Msg( "%s %s Invalid field!\n", func,  msg );
			break;
		default:
			int i = 0;
			for ( i; i < ARRAYSIZE( g_Lookup ) ; ++i )
			{
				if ( g_Lookup[ i ].id == rc )
				{
					Msg( "%s returned %i - %s (%s)!\n", func,  rc, g_Lookup[ i ].str, msg );
					break;
				}
			}

			if ( i >= ARRAYSIZE( g_Lookup ) )
			{
				Msg( "%s returned %i - %s! (%s)\n", func,  rc, "???", msg );
			}
			break;
		}
    }
}

TRK_UINT CBugReporter::Login(TRK_HANDLE* pTrkHandle, char const *projectname )
{
	char dbms[50] = DEFAULT_DBMS;

	char username[ 50 ];
	char password[ 50 ];

	Q_strncpy( username, DEFAULT_USERNAME, sizeof( username ) );
	Q_strncpy( password, DEFAULT_PASSWORD, sizeof( password ) );

	TRK_UINT rc = TrkProjectLogin(*pTrkHandle,
		username,
		password,
		projectname,
		NULL,
		NULL,
		NULL,
		NULL,
		TRK_USE_INI_FILE_DBMS_LOGIN);

	if (rc != TRK_SUCCESS)
	{
		rc = TrkProjectLogin(*pTrkHandle,
			username,
			"",
			projectname,
			NULL,
			NULL,
			NULL,
			NULL,
			TRK_USE_INI_FILE_DBMS_LOGIN);
		if (rc != TRK_SUCCESS)
		{
			Msg("Bug reporter init failed: Your tracker password must be your user name or blank.\n");
			return rc;
		}
	}

	TrkGetLoginDBMSName(*pTrkHandle, sizeof(dbms), dbms );

	char projout[ 256 ];

	TrkGetLoginProjectName(*pTrkHandle, sizeof( projout ), projout );
	
	Msg( "Project:  %s\n", projout );
	Msg( "Server:  %s\n", dbms );

	return rc;
}

//-----------------------------------------------------------------------------
// Purpose: Initialize and login with default username/password for this computer (from resource/bugreporter.res)
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CBugReporter::Init( char const *projectname )
{
	TRK_UINT			rc;
	rc = TrkHandleAlloc( TRK_VERSION_ID, &trkHandle);
	if ( rc != TRK_SUCCESS )
	{
		ReportError(rc, "TrkHandleAlloc", "Failed to Allocate Tracker Handle!");
		return false;
	}

	// Login to default project out of INI file.
	rc = Login( &trkHandle, projectname );
	if (rc != TRK_SUCCESS)
	{
		return false;
	}

	rc = TrkRecordHandleAlloc(trkHandle, &trkRecHandle);
	if (rc != TRK_SUCCESS)
	{
		ReportError(rc, "TrkRecordHandleAlloc", 
			"Failed to Allocate Tracker Record Handle!");
		return false;
	}

	return true;
}

void CBugReporter::Shutdown()
{
	TRK_UINT			rc;
	
	if ( trkRecHandle )
	{
		rc = TrkRecordHandleFree(&trkRecHandle);
		if (rc != TRK_SUCCESS)
		{
			ReportError(rc, "TrkRecordHandleFree", "Failed to Free Tracker Record Handle!");
		}
	}

	if ( trkHandle )
	{
		rc = TrkProjectLogout(trkHandle);		
		if (rc != TRK_SUCCESS)
		{
			ReportError(rc, "TrkProjectLogout", "Failed to Logout of Project!");
		}
		else
		{
			rc = TrkHandleFree(&trkHandle);     
			if (rc != TRK_SUCCESS)
			{
				ReportError(rc, "TrkHandleFree", "Failed to Free Tracker Handle!");
			}
		}
	}
}

void CBugReporter::StartNewBugReport()
{
	m_Fields.RemoveAll();
}

void CBugReporter::CancelNewBugReport()
{
	m_Fields.RemoveAll();
}

void CBugReporter::SubstituteBugId( int bugid, char *out, int outlen, CUtlBuffer& src )
{
	out[ 0 ] = 0;

	char *dest = out;

	src.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );

	char const *replace = "\\BugId\\";
	int replace_len = Q_strlen( replace );

	for ( int pos = 0; pos <= src.TellPut() && ( ( dest - out ) < outlen ); )
	{
		char const *str = ( char const * )src.PeekGet( pos );
		if ( !Q_strnicmp( str, replace, replace_len ) )
		{
			*dest++ = '\\';

			char num[ 32 ];
			Q_snprintf( num, sizeof( num ), "%i", bugid );
			char *pnum = num;
			while ( *pnum )
			{
				*dest++ = *pnum++;
			}

			*dest++ = '\\';
			pos += replace_len;
			continue;
		}

		*dest++ = *str;
		++pos;
	}
	*dest = 0;
}

bool CBugReporter::CommitBugReport( int& bugSubmissionId )
{
	bugSubmissionId = -1;

	int fieldCount = m_Fields.Count();
	if ( fieldCount == 0 )
		return false;

	TRK_UINT rc = 0;
	rc = TrkNewRecordBegin( trkRecHandle, SCR_TYPE );
	if ( rc != TRK_SUCCESS )
	{
		ReportError(rc, "TrkNewRecordBegin", 
			"Failed to TrkNewRecordBegin!");
		return false;
	}

	CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );

	for ( int i = 0; i < fieldCount; ++i )
	{
		BugField_t& fld = m_Fields[ i ];

		if ( !fld.isdesc )
		{
			// Populate fields
			if ( !fld.isnumeric )
			{
				rc = TrkSetStringFieldValue( 
					trkRecHandle,
					fld.trkfield,
					fld.value );
			}
			else
			{
				rc = TrkSetNumericFieldValue( 
					trkRecHandle,
					fld.trkfield,
					fld.numvalue );
			}
			if ( rc != TRK_SUCCESS )
			{
				char es[ 256 ];
				Q_snprintf( es, sizeof( es ), "Failed to add '%s'", fld.trkfield );

				ReportError( rc, "TrkSetStringFieldValue",  es );
				return false;
			}
		}
		else
		{
			buf.Printf( "%s\n", fld.value );

			buf.PutChar( 0 );

			rc = TrkSetDescriptionData( trkRecHandle,
				buf.TellPut(),
				(const char * )buf.Base(),
				0 );
			if ( rc != TRK_SUCCESS )
			{
				ReportError(rc, "TrkSetDescriptionData", 
					"Failed to set description data!");
				return false;
			}
		}

	}

	TRK_TRANSACTION_ID id;
	rc = TrkNewRecordCommit( trkRecHandle, &id );
	if ( rc != TRK_SUCCESS )
	{
		ReportError(rc, "TrkNewRecordCommit", 
			"Failed to TrkNewRecordCommit!");
		return false;
	}

	TRK_UINT bugId;
	rc = TrkGetNumericFieldValue( trkRecHandle, "Id", &bugId );
	if ( rc != TRK_SUCCESS )
	{
		ReportError(rc, "TrkGetNumericFieldValue", 
			"Failed to TrkGetNumericFieldValue for bug Id #!");
	}
	else
	{
		bugSubmissionId = (int)bugId;
	}

	rc = TrkGetSingleRecord( trkRecHandle, bugId, SCR_TYPE );
	if ( rc != TRK_SUCCESS )
	{
		ReportError( rc, "TrkGetSingleRecord",
			"Failed to open bug id for update" );
		return false;
	}

	rc = TrkUpdateRecordBegin( trkRecHandle );
	if ( rc != TRK_SUCCESS )
	{
		ReportError( rc, "TrkUpdateRecordBegin",
			"Failed to open bug id for update" );
		return false;
	}
	else
	{
		int textbuflen = 2 * buf.TellPut() + 1;

		char *textbuf = new char [ textbuflen ];
		Q_memset( textbuf, 0, textbuflen );

		SubstituteBugId( (int)bugId, textbuf, textbuflen, buf );

		// Update the description with the substituted text!!!
		rc = TrkSetDescriptionData( trkRecHandle,
			Q_strlen( textbuf ) + 1,
			(const char * )textbuf,
			0 );

		delete[] textbuf;

		if ( rc != TRK_SUCCESS )
		{
			ReportError(rc, "TrkSetDescriptionData(update)", 
				"Failed to set description data!");
			return false;
		}

		rc = TrkUpdateRecordCommit( trkRecHandle, &id );
		if ( rc != TRK_SUCCESS )
		{
			ReportError(rc, "TrkUpdateRecordCommit", 
				"Failed to TrkUpdateRecordCommit for bug Id #!");
			return false;
		}
	}

	m_Fields.RemoveAll();

	return true;
}

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: 
// Input  : depth - 
//			*fmt - 
//			... - 
//-----------------------------------------------------------------------------
void vprint( int depth, const char *fmt, ... )
{
	char string[ 8192 ];
	va_list va;
	va_start( va, fmt );
	vsprintf( string, fmt, va );
	va_end( va );

	FILE *fp = NULL;

	if ( uselogfile )
	{
		fp = fopen( "log.txt", "ab" );
	}

	while ( depth-- > 0 )
	{
		printf( "  " );
		OutputDebugString( "  " );
		if ( fp )
		{
			fprintf( fp, "  " );
		}
	}

	::printf( "%s", string );
	OutputDebugString( string );

	if ( fp )
	{
		char *p = string;
		while ( *p )
		{
			if ( *p == '\n' )
			{
				fputc( '\r', fp );
			}
			fputc( *p, fp );
			p++;
		}
		fclose( fp );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void printusage( void )
{
	vprint( 0, "usage:  getbugs pvcsproject hostname database username password contentadminexe <startbug endbug>\n\
		\ne.g.:  getbugs \"Steam Beta\" steamweb cserr username password \"u:/p4clients/yahn_steam_work/projects/gazelleproto/tools/contentadmin/vc70_debug_static/contentadmin.exe\" 1 10\n" );

	// Exit app
	exit( 1 );
}

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

void PrintHeader()
{
	vprint( 0, "Valve Software - getbugs.exe (%s)\n", __DATE__ );
	vprint( 0, "--- Pulls public bugreporter bugs into PVCS tracker ---\n" );
}

bool GetBugZip( int bugnum, char const *admin )
{
	bool retval = false;

	char commandline[ 512 ];
	char directory[ 512 ];

	Q_strncpy( directory, admin, sizeof( directory ) );
	Q_StripFilename( directory );


	//	sprintf( commandline, "msdev engdll.dsw /MAKE \"quiver - Win32 GL Debug\" /OUT log.txt" );

	// Builds the default configuration
	sprintf( commandline, "\"%s\" bugreport %i", admin, bugnum );

	PROCESS_INFORMATION pi;
	memset( &pi, 0, sizeof( pi ) );

	STARTUPINFO si;
	memset( &si, 0, sizeof( si ) );
	si.cb = sizeof( si );

	if ( !CreateProcess( NULL, commandline, NULL, NULL, TRUE, 0, NULL, directory, &si, &pi ) )
	{
		LPVOID lpMsgBuf;
		FormatMessage( 
			FORMAT_MESSAGE_ALLOCATE_BUFFER | 
			FORMAT_MESSAGE_FROM_SYSTEM | 
			FORMAT_MESSAGE_IGNORE_INSERTS,
			NULL,
			GetLastError(),
			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
			(LPTSTR) &lpMsgBuf,
			0,
			NULL 
		);
		// Process any inserts in lpMsgBuf.
		// ...
		// Display the string.
		MessageBox( NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION );
		// Free the buffer.
		LocalFree( lpMsgBuf );
		return retval;
	}

	// Wait until child process exits.
    WaitForSingleObject( pi.hProcess, INFINITE );

	DWORD exitCode = (DWORD)-1;
	if ( GetExitCodeProcess( pi.hProcess, &exitCode ) )
	{
		if ( exitCode == 0 )
		{
			retval = true;
		}
	}
	
    // Close process and thread handles. 
    CloseHandle( pi.hProcess );
    CloseHandle( pi.hThread );

	return retval;
}

void CreateDirHierarchy(const char *path)
{
	char temppath[512];
	Q_strncpy( temppath, path, sizeof(temppath) );
	
	for (char *ofs = temppath+1 ; *ofs ; ofs++)
	{
		if (*ofs == '/' || *ofs == '\\')
		{       // create the directory
			char old = *ofs;
			*ofs = 0;
			_mkdir (temppath);
			*ofs = old;
		}
	}
}


void GetBugInfo( CBugReporter& bug, char const *host, char const *database, char const *username, char const *password, char const *admin, int startbug, int endbug )
{
	// Now connect to steamweb and update the engineaccess table
	CSysModule *sql = Sys_LoadModule( "mysql_wrapper" );
	if ( sql )
	{
		CreateInterfaceFn factory = Sys_GetFactory( sql );
		if ( factory )
		{
			IMySQL *mysql = ( IMySQL * )factory( MYSQL_WRAPPER_VERSION_NAME, NULL );
			if ( mysql )
			{
				if ( mysql->InitMySQL( database, host, username, password ) )
				{
					char q[ 512 ];

					/*
					Q_snprintf( q, sizeof( q ), "insert into engineaccess (BuildIdentifier,AllowAccess) values (\"%s\",1);",
						argv[2] );

					int retcode = mysql->Execute( q );
					if ( retcode != 0 )
					{
						printf( "Query %s failed\n", q );
					}
					else
					{
						printf( "Successfully added buildidentifier '%s' to %s:%s\n",
							argv[ 2 ], argv[ 3 ], argv[ 4 ] );
					}
					*/

					char where[ 256 ];
					where[ 0 ] = 0;
					if ( startbug != -1 && endbug != -1 )
					{
						Q_snprintf( where, sizeof( where ), "BugId>=%i and BugId<=%i and ", startbug, endbug );
					}

					Q_snprintf( q, sizeof( q ), "select BugId, Time, BuildNumber, ExeName, GameDirectory, MapName, Title, Description, IP,"
						"BaseIP, RAM, CPU, ProcessorVendor, DXVersionHighPart, DXVersionLowPart, DXVendorID, DXDeviceID, OSVersion, "
						"BugReportFilePath, ReportType, EMail, AccountName, SteamID, Processed, Important from bugreports where %s ( Processed is NULL or Processed = 0 );", where );


					int retcode = mysql->Execute( q );
					if ( retcode != 0 )
					{
						vprint( 0, "Query %s failed\n", q );
					}
					else
					{
						IMySQLRowSet *rows = mysql->DuplicateRowSet();

						CUtlVector< int >	processed;

						if ( rows && rows->NumFields() > 0 )
						{
							while ( rows->NextRow() )
							{
								CColumnValue BugId = rows->GetColumnValue( "BugId" );
								CColumnValue Time = rows->GetColumnValue( "Time" );
								CColumnValue BuildNumber = rows->GetColumnValue( "BuildNumber" );
								CColumnValue ExeName = rows->GetColumnValue( "ExeName" );
								CColumnValue GameDirectory = rows->GetColumnValue( "GameDirectory" );
								CColumnValue MapName = rows->GetColumnValue( "MapName" );
								CColumnValue Title = rows->GetColumnValue( "Title" );
								CColumnValue Description = rows->GetColumnValue( "Description" );
								CColumnValue IP = rows->GetColumnValue( "IP" );
								CColumnValue BaseIP = rows->GetColumnValue( "BaseIP" );
								CColumnValue RAM = rows->GetColumnValue( "RAM" );
								CColumnValue CPU = rows->GetColumnValue( "CPU" );
								CColumnValue ProcessorVendor = rows->GetColumnValue( "ProcessorVendor" );
								CColumnValue DXVersionHighPart = rows->GetColumnValue( "DXVersionHighPart" );
								CColumnValue DXVersionLowPart = rows->GetColumnValue( "DXVersionLowPart" );
								CColumnValue DXVendorID = rows->GetColumnValue( "DXVendorID" );
								CColumnValue DXDeviceID = rows->GetColumnValue( "DXDeviceID" );
								CColumnValue OSVersion = rows->GetColumnValue( "OSVersion" );
								CColumnValue BugReportFilePath = rows->GetColumnValue( "BugReportFilePath" );
								CColumnValue ReportType = rows->GetColumnValue( "ReportType" );
								CColumnValue EMail = rows->GetColumnValue( "EMail" );
								CColumnValue Accountname = rows->GetColumnValue( "Accountname" );
								CColumnValue SteamID = rows->GetColumnValue( "SteamID" );
								CColumnValue Processed = rows->GetColumnValue( "Processed" );
								CColumnValue Important = rows->GetColumnValue( "Important" );

								bug.StartNewBugReport();
								
								vprint( 1, "Processing bug %i\n", BugId.Int32() );

								bug.AddField( "Owner", "PublicUser" );
								bug.AddField( "Submitter", "PublicUser" );
								bug.AddField( "Title", Title.String() );


								bug.AddNumericField( "BugId", BugId.Int32() );
								bug.AddNumericField( "Build", BuildNumber.Int32() );
								bug.AddField( "Exe", ExeName.String() );
								bug.AddField( "Gamedir", GameDirectory.String() );
								bug.AddField( "Map", MapName.String() );
								bug.AddField( "Operating System", OSVersion.String() );
								bug.AddField( "Processor", ProcessorVendor.String() );
								bug.AddNumericField( "Memory", RAM.Int32() );
								bug.AddField( "SteamID", SteamID.String() );
								bug.AddNumericField( "CPU", CPU.Int32() );

								bug.AddField( "Time", Time.String() );

								char dxversion[ 512 ];
								int vhigh = DXVersionHighPart.Int32();
								int vlow = DXVersionLowPart.Int32();

								Q_snprintf( dxversion, sizeof( dxversion ), "%u.%u.%u.%u", ( vhigh >> 16 ) , vhigh & 0xffff, ( vlow >> 16 ), vlow & 0xffff );

								bug.AddField( "DXVersion", dxversion);

								bug.AddNumericField( "DXDevice", DXDeviceID.Int32() );
								bug.AddNumericField( "DXVendor", DXVendorID.Int32() );

								bug.AddField( "BugType", ReportType.String() );
								bug.AddField( "E-Mail Address", EMail.String() );
								bug.AddField( "Account Name", Accountname.String() );

								char zipurl[ 512 ];
								zipurl[ 0 ] = 0;

								char basepath[ 512 ];
								Q_FileBase( BugReportFilePath.String(), basepath, sizeof( basepath ) );

								char desc[ 8192 ];
								
								if ( BugReportFilePath.String()[ 0 ] )
								{
									Q_snprintf( zipurl, sizeof( zipurl ), BUG_REPOSITORY_FMT, database, "BugId", basepath );

									Q_snprintf( desc, sizeof( desc ), "%s\n\nzip url:  %s\n", Description.String(), zipurl );
								}
								else
								{
									Q_strncpy( desc, Description.String(), sizeof( desc ) );
								}

								bug.AddField( "Description", desc, true );

								int trackerBugId = -1;

								bool success = bug.CommitBugReport( trackerBugId );
								if ( success )
								{
									// The public UI handles uploading on it's own...
									// Fixup URL
									if ( zipurl[ 0 ] )
									{
										char zipurlfixed[ 512 ];
										char id[ 32 ];
										Q_snprintf( id, sizeof( id ), "%i", trackerBugId );

										// The upload destination
										Q_snprintf( zipurlfixed, sizeof( zipurlfixed ), BUG_REPOSITORY_FMT, database, id, basepath );
										Q_strlower( zipurlfixed );
										Q_FixSlashes( zipurlfixed );

										char admindir[ 512 ];
										Q_strncpy( admindir, admin, sizeof( admindir ) );
										Q_StripFilename( admindir );
										Q_strlower( admindir );
										Q_FixSlashes( admindir );


										char zipurllocal[ 512 ];
										Q_snprintf( zipurllocal, sizeof( zipurllocal ), "%s\\%s.zip", admindir, basepath );
										Q_strlower( zipurllocal );
										Q_FixSlashes( zipurllocal );

										// Get local copy of .zip file

										if ( GetBugZip( BugId.Int32(), admin ) )
										{
											struct _stat statbuf;

											if ( _stat( zipurllocal, &statbuf ) == 0 )
											{
												CreateDirHierarchy( zipurlfixed );

												MoveFile( zipurllocal, zipurlfixed );

												// Mark processed
												processed.AddToTail( BugId.Int32() );
											}
										}
										else
										{
											Warning( "Unable to retrieve bug file for %i\n", BugId.Int32() );
										}
									}
									else
									{
										processed.AddToTail( BugId.Int32() );
									}

								}
								else
								{
									Warning( "Unable to post bug report to database\n" );
								}
							}

							// Discard memory
							rows->Release();

							int c = processed.Count();
							for ( int i = 0; i < c; ++i )
							{
								Q_snprintf( q, sizeof( q ), "update bugreports set Processed=1 where BugId=%i;", processed[ i ] );

								retcode = mysql->Execute( q );
								if ( retcode != 0 )
								{
									Msg( "Query failed '%s'\n", q );
								}
							}
						}
					}
				}
				else
				{
					vprint( 0, "InitMySQL failed\n" );
				}

				mysql->Release();
			}
			else
			{
				vprint( 0, "Unable to connect via mysql_wrapper\n");
			}
		}
		else
		{
			vprint( 0, "Unable to get factory from mysql_wrapper.dll, not updating access mysql table!!!" );
		}

		Sys_UnloadModule( sql );
	}
	else
	{
		vprint( 0, "Unable to load mysql_wrapper.dll, not updating access mysql table!!!" );
	}
}

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

	uselogfile = true;
	verbose = true;

	if ( argc != 7 && argc != 9  )
	{
		PrintHeader();
		printusage();
	}

	CheckLogFile();

	PrintHeader();

	vprint( 0, "    Getting bugs...\n" );

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

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


	CBugReporter bugreporter;
	if ( !bugreporter.Init( argv[ 1 ] ) )
	{
		vprint( 0, "Couldn't init bug reporter\n" );
		return 0;
	}

	if ( argc == 9 )
	{
		GetBugInfo( bugreporter, argv[2], argv[3], argv[4], argv[5], argv[ 6 ], atoi( argv[ 7 ] ), atoi( argv[ 8 ] ) );
	}
	else
	{
		GetBugInfo( bugreporter, argv[2], argv[3], argv[4], argv[5], argv[ 6 ], -1, -1 );
	}


	bugreporter.Shutdown();
	CmdLib_TermFileSystem();

	return 0;
}