int main(int argc, char* argv[])
{
	char cmd[512];
	int ret;

	if( argc < 2 )
	{
		return PrintUsage();
	}


	// All output gets redirected to this file.
	SECURITY_ATTRIBUTES fileAttribs;
	fileAttribs.nLength = sizeof(fileAttribs);
	fileAttribs.lpSecurityDescriptor = NULL;
	fileAttribs.bInheritHandle = TRUE;

	g_hOutputFile = CreateFile( 
		"vmapbuilder.out", 
		GENERIC_WRITE,
		FILE_SHARE_READ,
		&fileAttribs,
		CREATE_ALWAYS,
		FILE_FLAG_WRITE_THROUGH|FILE_ATTRIBUTE_NORMAL,
		NULL );


	// Low priority..
	SetPriorityClass( GetCurrentProcess(), IDLE_PRIORITY_CLASS );

	bool bSave = !FindArg( "-nosave" );
	bool bNoVis = !!FindArg( "-novis" );
	bool bNoRad = !!FindArg( "-norad" );

	char const *pCFGFile = argv[1];
	while( 1 )
	{
		DWORD startTime = GetTickCount();

		CConfigFile file;
		if( file.Read( pCFGFile ) && file.m_Entries.Size() > 0 )
		{
			// Move the first entry in the file to the end.
			CConfigFile::Entry entry = file.m_Entries[0];
			file.m_Entries.Remove( 0 );
			file.m_Entries.AddToTail( entry );
			if( bSave )
			{
				file.Write( pCFGFile );
			}


			char ssCmd[256];
			_snprintf( ssCmd, sizeof(ssCmd), "%s\\win32\\ss.exe", file.m_SSDatabase );

			// Grab the SourceSafe tree.			  
			AppPrint( "\n\n-------------------------------------------------------------\n" );
			AppPrint( "Processing %s\n", entry.m_Filename );
			AppPrint( "-------------------------------------------------------------\n" );
			AppPrint( "Grabbing resources in %s\n", file.m_SSDatabase );
			
			sprintf( cmd, "ssdir=%s", file.m_SSDatabase );
			ret = _putenv( cmd );
			
			// Get the VMF.
			char vmfFilename[512];
			sprintf( vmfFilename, "%s/%s.vmf", entry.m_VMFPath, entry.m_Filename );
			sprintf( cmd, "%s get %s -I-", ssCmd, vmfFilename );
			ret = RunProcess( cmd );

			// Check the timestamp (don't reprocess if it's up-to-date).
			sprintf( vmfFilename, "%s.vmf", entry.m_Filename );
			struct _stat fileStat;
			ret = _stat( vmfFilename, &fileStat );
			long vmfTime = fileStat.st_mtime;
			if( vmfTime == entry.m_VMFTime )
			{
				AppPrint( "%s is up-to-date\n", vmfFilename );
			}
			else
			{
				char localBSPFilename[512];
				sprintf( localBSPFilename, "%s.bsp", entry.m_Filename );
				
				// Attrib the bsp if it exists.
				sprintf( cmd, "attrib -r %s", localBSPFilename );
				RunProcess( cmd );
				
				sprintf( cmd, "%s cp %s", ssCmd, file.m_SSResourcePath );
				ret = RunProcess( cmd );
				
				if( !FindArg( "-noget" ) )
				{
					sprintf( cmd, "%s get * -R -I-", ssCmd );
					ret = RunProcess( cmd );
				}

				// run each tool.
				char vbspCommand[256], vradCommand[256], vvisCommand[256];
				sprintf( vbspCommand, "bin\\vbsp -low %s", entry.m_Filename );
				
				if( entry.m_bFastVis )
					sprintf( vvisCommand, "bin\\vvis -fast -low %s", entry.m_Filename );
				else
					sprintf( vvisCommand, "bin\\vvis -low %s", entry.m_Filename );

				sprintf( vradCommand, "bin\\vrad -low %s", entry.m_Filename );
				if( !RunProcess( vbspCommand ) && 
					(bNoVis || !RunProcess( vvisCommand )) && 
					(bNoRad || !RunProcess( vradCommand )) )
				{
					// Check in the BSP file.
					char bspFilename[512];
					sprintf( bspFilename, "%s/%s.bsp", file.m_SSBSPPath, entry.m_Filename );

					// First, try to add it to SS because it may not exist yet.
					sprintf( cmd, "%s cp %s", ssCmd, file.m_SSBSPPath );
					RunProcess(cmd);
					sprintf( cmd, "%s add %s.bsp -I-", ssCmd, entry.m_Filename );
					ret = RunProcess(cmd);

					// Store off the BSP file temporarily..
					char tempFilename[512];
					sprintf( localBSPFilename, "%s.bsp", entry.m_Filename );
					
					sprintf( tempFilename, "%s.TEMP", entry.m_Filename );
					sprintf( cmd, "del /f %s", tempFilename );
					system( cmd );

					sprintf( cmd, "attrib -r %s", localBSPFilename );
					system( cmd );
					ret = MoveFile( localBSPFilename, tempFilename );
					if( ret )
					{
						char undoCmd[512];
						sprintf( undoCmd, "%s undocheckout %s -I-", ssCmd, bspFilename );

						sprintf( cmd, "%s checkout %s -I-", ssCmd, bspFilename );
						ret = RunProcess( cmd );
						if( !ret )
						{
							// Copy the new BSP file over.
							DeleteFile( localBSPFilename );
							ret = MoveFile( tempFilename, localBSPFilename );
							if( ret )
							{
								sprintf( cmd, "%s checkin %s -I-", ssCmd, bspFilename );
								ret = RunProcess( cmd );
								if( !ret )
								{
									while( !file.Read( pCFGFile ) )
									{
										Sleep( 300 );
									}

									if( bSave )
									{
										CConfigFile::Entry *pEntry = file.FindEntryByFilename( entry.m_Filename );
										if( pEntry )
										{
											pEntry->m_VMFTime = vmfTime;
											while( !file.Write( pCFGFile ) )
											{
												Sleep( 300 );
											}
										}
									}
									
									// Update the timestamp in the config file.
									AppPrint( "Completed '%s' successfully!\n", entry.m_Filename );

									// Send emails.
									char computerName[256] = {0};
									DWORD len = sizeof(computerName);
									GetComputerName( computerName, &len );

									DWORD elapsed = GetTickCount() - startTime;
									char timeStr[256];
									BuildTimeDurationString( elapsed, timeStr );

									char subject[1024];
									_snprintf( subject, sizeof(subject), "[vmapbuilder] completed '%s' on '%s' in %s", entry.m_Filename, computerName, timeStr );
									for( int e=0; e < entry.m_nEMailAddresses; e++ )
									{
										char *pAddr = entry.m_EMailAddresses[e].m_EMailAddress;
										if( !SendMail( pAddr, subject, subject ) )
										{
											AppPrint( "Unable to send confirmation email to %s\n", pAddr );
										}
									}
								}
								else
								{
									AppPrint( "ERROR: Can't checkin %s\n", bspFilename );
									RunProcess( undoCmd );
								}
							}
							else
							{
								AppPrint( "ERROR: Can't copy back the BSP file %s\n", localBSPFilename );
								RunProcess( undoCmd );
							}
						}
						else
						{
							AppPrint( "ERROR: Can't checkout %s\n", bspFilename );
						}
					}
					else
					{
						AppPrint( "ERROR: Can't create temporary file %s\n", tempFilename );
					}
				}
				else
				{
					AppPrint( "Command '%s' failed\n", cmd );
				}
			}
		}
		else
		{
			AppPrint( "Can't read maplist file: %s\n", pCFGFile );
		}

		// Sleep for a bit in case all maps are processed so we don't kill the network.
		Sleep( 2000 );
	}

	CloseHandle( g_hOutputFile );
	return 0;
}