//																			Automated Build system for SCRUMs
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// This script can build the latest branch code, process assets and copy everything to a destination and also report errors 
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++




/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//+++++++++++++++++++++++++++++++++++++++++++|START|  *Globals*  |START|+++++++++++++++++++++++++++++++++++++++++++++++++++++
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


try
{
// This will disable all important actions(update&processing of files) 
// in the build which allows to debug the script locally
var DEBUG = false;

// Create Shell objects.
var WSHSHELL = WScript.CreateObject("WScript.Shell");
var FSO = WScript.CreateObject("Scripting.FileSystemObject");
var WSHNETWORK = WScript.CreateObject("WScript.Network");

// Save the "started in" path and the original script path
var startedInPath = WSHSHELL.CurrentDirectory ;
var workingScriptPath = WScript.ScriptFullName;
var scriptLocation = "";
var regExp = /(.*\\).*$/i;
if(workingScriptPath.search(regExp) != "-1")
{
	scriptLocation = RegExp.$1;
}
else
{
	WScript.Echo("Could not get the directory of the working script.");
	WScript.Quit(1);
}

// Save Computer name
var computerName = WSHNETWORK.ComputerName.toLowerCase();
var forReading = 1, forWriting = 2, forReading = 8;
var tristateUseDefault = -2, tristateTrue = -1, tristateFalse = 0;


var buildLog = "scrum_build.log"
var logFile = FSO.CreateTextFile( scriptLocation  + buildLog,true );

// Which Sever will be used for sending mails
var mailServer= "mail2.INTERN.CRYTEK.DE";
var aEmptyArr = new Array();

// ~~~~~~~~~~~~~~~~~~~~~~~*Customizeable variables*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var adminMail, aCompileErrorRecipient = new Array, aSuccessEmail = new Array, aFailEmail = new Array;
var buildDrive, buildFolderName, p4_Client, p4Source, solutionName, copyBuild, buildCode, getLatest;
var processAnimations, processTifs, finalStorageFolder, cppProjects;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

// Include the customized variables
var inlcudeProjectCustomActions = scriptLocation + "Customize.js";
if(FSO.FileExists( inlcudeProjectCustomActions ))
	eval(ReadFile(inlcudeProjectCustomActions, false));
else
	ErrorExit( "FATAL ERROR! The needed customize file \"" + inlcudeProjectCustomActions + "\" doesn't exists!", "" , aEmptyArr );

// Check wheter no variable was forgotten
if(adminMail == null || aCompileErrorRecipient == null || aSuccessEmail == null || aFailEmail == null || finalStorageFolder == null 
|| buildDrive == null || buildFolderName == null || p4_Client == null || p4Source == null || solutionName == null || cppProjects == null
|| copyBuild == null || buildCode == null || getLatest == null || processAnimations  == null || processTifs == null )
	ErrorExit( "FATAL ERROR! Not all needed variables are definied in the customize file \"" + inlcudeProjectCustomActions + "\"", "" , aEmptyArr );


var numLogs = 0;
var cppLog = scriptLocation + "Cpp.log";
var needforceSyncOfCodeFile = scriptLocation + "NeedforceSyncOfCode.txt";

// Create error variables  
var result = 0;
var error = false;
var errorString = "";
var aErrFiles = new Array(scriptLocation + buildLog);
var aScsFiles = new Array(scriptLocation + buildLog);

// Timings for stopping times how long what takes in the Buildprocess
var startTime,endTime;
var startGetTime,endGetTime;
var startCompileTime,endCompileTime;
var startProcessAnimationTime,endProcessAnimationTime;
var startProcessTifTime,endProcessTifTime;
var startCopyTime,endCopyTime;

// Start Timer for whole buildprocess
startTime = new Date();

// Indicates if project was compiled
var resultOfCompilingproject = "";

// Variables for check of changed data in P4
var lastChangelistNumber = 0;
var codeChanges = "";
var foundcodeChanges = true;
var codeChangelistNumberLoad = true;

// Programms for compiling the project
var buildConsole = "\"C:\\Program Files\\Xoreax\\IncrediBuild\\buildConsole.exe\""; 
var msDevStudio = "\"C:\\Program Files\\Microsoft Visual Studio 8\\Common7\\Ide\\devenv.exe\"";

if(vs_Version == 2003)
	msDevStudio = "\"C:\\Program Files\\Microsoft Visual Studio .NET 2003\\Common7\\Ide\\devenv.exe\"";

// Perforce.
var p4_User = "Build";

// ZipProgramm 
var winRar = "\"C:\\Program Files\\winRar\\winRar.exe\"";
var pkzipCmd = scriptLocation+"pkzipc.exe";

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//+++++++++++++++++++++++++|END|  Globals for every project  |END|++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	

	//-------------------------------------------------------------------------------------------------------------------------
	//-------------------|START|   *Process Command-Line parameters*   |START|-------------------------------------------------
	//-------------------------------------------------------------------------------------------------------------------------
	
	// Variables for catched Arguments
	var exitIfNocodeChanges = false;
	var emailAdditions = "";
	var p4Label = "";
	var useIncrediBuild = true;
	var buildConfig = "Profile";
	var fastBuild = true;
	var exitOnCompilingerror = true;
	var vs_Version = 2005;
	var rebuildCode = false;
	
	
	// Catch whether the code is needed to rebuild
	if (WScript.Arguments.Named.Exists("rebuildCode"))
	{	
		rebuildCode = true;
	}
	
	// Catching whether Build should continue on compilation error
	if (WScript.Arguments.Named.Exists("exitOnCompilingerror"))
	{
		var tempArg = WScript.Arguments.Named.Item("exitOnCompilingerror").toLowerCase();
		if( tempArg == "false")
			exitOnCompilingerror = false;
		else if(tempArg == "true")
			exitOnCompilingerror = true;
		else
			WScript.Echo("no correct boolean statement was given for exitOnCompilingerror. using default: "+exitOnCompilingerror);
	}
	
	// Catch which version of visual studio should be used for compiling
	if (WScript.Arguments.Named.Exists("VS03"))
	{
		vs_Version = 2003;
	}
	
	// Option for check of available Changes in Perforce
	if (WScript.Arguments.Named.Exists("ExitOnP4Check"))
	{
		if (WScript.Arguments.Named.Item("ExitOnP4Check") == "true")
			exitIfNocodeChanges = true;
		else if (WScript.Arguments.Named.Item("ExitOnP4Check") == "false")
			exitIfNocodeChanges = false;
		else
			WScript.Echo("no correct boolean statement was given for ExitOnP4Check using default: "+exitIfNocodeChanges);
	}
	
	
	// Additional Text to sent build Emails
	if (WScript.Arguments.Named.Exists("emailAdditions"))
	{
		emailAdditions = WScript.Arguments.Named.Item("emailAdditions");
	}
	// Replacing all "_" through spaces because Arguments cant have them inside
	do
	{
		emailAdditions = emailAdditions.replace("_", " ");
	}
	while(emailAdditions.search("_")!= -1)
	
		
	
	// Get the Label to which the data will be synced
	if (WScript.Arguments.Named.Exists("p4Label"))
	{
		p4Label = "@" + WScript.Arguments.Named.Item("p4Label");
	}
	
	
	// Option to enable/disable Incredibuild
	if (WScript.Arguments.Named.Exists("useIncrediBuild"))
	{
		if (WScript.Arguments.Named.Item("useIncrediBuild") == "true")
			useIncrediBuild = true;
		else if(WScript.Arguments.Named.Item("useIncrediBuild") == "false")
			useIncrediBuild = false;
		else
			WScript.Echo("no correct boolean statement was given for useIncrediBuild using default: "+useIncrediBuild);
	}
	
	
	// Option to enable/disable fastBuild
	if (WScript.Arguments.Named.Exists("fastBuild"))
	{
		if (WScript.Arguments.Named.Item("fastBuild") == "false")
			fastBuild = false;
		else if (WScript.Arguments.Named.Item("fastBuild") == "true")
			fastBuild = true;
		else
			WScript.Echo("no correct boolean statement was given for fastBuild using default: "+fastBuild);
	}
	
	// Get the buildConfig 
	if (WScript.Arguments.Named.Exists("buildConfig"))
	{
		buildConfig = WScript.Arguments.Named.Item("buildConfig");
	}		
	//-------------------------------------------------------------------------------------------------------------------------
	//--------------------------|END|   Process Command-Line parameters   |END|------------------------------------------------
	//-------------------------------------------------------------------------------------------------------------------------






/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//+++++++++++++++++++++++++++++++++|START|  *Derived Globals*  |START|+++++++++++++++++++++++++++++++++++++++++++++++++++++++
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//----------------------------------Derived variables-----------------------------------------
// for local debugging
if(DEBUG)
	buildDrive = "f:";

// Change source to label if one was given	
if(p4Label!="")
	p4Source=p4Label;
		
// Necessary build folders 
var buildingFolder = buildDrive + "\\" + buildFolderName;

// for local debugging
if(DEBUG)
{
	buildingFolder = "f:\\Game02_build";
}
		
var codeFolder = buildingFolder + "\\Code";
var bin32Folder = buildingFolder + "\\Bin32";
var bin64Folder = buildingFolder + "\\Bin64";
var toolsFolder = buildingFolder + "\\Tools";
var buildGameDataFolder = buildingFolder + "\\Game";

var resourceCompiler = bin32Folder+"\\rc\\rc.exe"; // Resource compiler.

// P4 sync command line
var p4_cmdLine = "p4" + " -c "+p4_Client + " -u " + p4_User + " sync";



var projectCppWorkspace = codeFolder + "\\Solutions\\" + solutionName;
//---------------------------------------------------------------------------------------------

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//+++++++++++++++++++++++++|END|  projectspecific Globals  |END|++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////






/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//+++++++++++++++++++++++++|START|  *FUNCTION DEFENITIONS*  |START|++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// For reading in other jscripts and then including them
function ReadFile(file, create)
{ 
	var str = "";
  var fileToRead = FSO.OpenTextFile(file, 1, create);
  if(!fileToRead.AtEndOfstream)
  	str = fileToRead.ReadAll();
  	
  fileToRead.Close();
  return str;
}


//+++++++++++++++++++++++++Checking wheter new changes are available in P4 or not+++++++++++++++++++++++++
function CheckForCodeChanges()
{		
		Log("Checking for changes in the Code database...");
		var lastCodeChangelistNumber = ReadChangelistNumberFromFile(scriptLocation + "code_changelist_number.txt");
		if(lastCodeChangelistNumber!="")
		{
			codeChanges = GetPerforceChanges(p4Source, lastCodeChangelistNumber);
			if(codeChanges!="")
			{	
				Log("Changes were found in the Code database, starting compiling.");
			}
			else
			{	
				Log("No changes were found in the Code database or maybe the wrong changelistnumber was given.");
				foundcodeChanges = false;
				if (exitIfNocodeChanges)
				{				
					WScript.Quit(0);
				}
			}
		}
		else
		{
			codeChangelistNumberLoad = false;
			aSuccessEmail.push(adminMail);
			emailAdditions += "\r\nCould not load the last changelistnumber from code_changelist_number.txt. Because of this the code_changes.txt was not updated";
			Log("Could not load the last changelistnumber from code_changelist_number.txt");
		}
}

//+++++++++++++++++Execute "P4 changes" with given parameters++++++++++++++++++
function GetPerforceChanges(PerforcePath, ChangelistNumber)
{
		//--check for new changes
		// Execute the command line which will give the changes
		var cmdline = "p4" + " -c "+p4_Client + " -u " + p4_User + " changes -l -t -s submitted " + PerforcePath + "...@" + ChangelistNumber + ",@now";
		var oExec = WSHSHELL.Exec( cmdline );
		var changeListInfo = oExec.StdOut.ReadAll();
		return(changeListInfo);
}

//++++++++++++++++++++++++++Save Last changelistnumber++++++++++++++++++++++++++
function SaveChangelistNumberToFile(filePath , changelistNumber)
{
	FSO.CreateTextFile( filePath,true)
	var changeListFile = FSO.GetFile( filePath );
	var stream = changeListFile.OpenAsTextstream( forWriting,tristateUseDefault );
	stream.Write(changelistNumber);
	stream.Close();
}

//++++++++++++++++++++++++++Read Last changelistnumber++++++++++++++++++++++++++
function ReadChangelistNumberFromFile( filePath )
{ 
	if(FSO.FileExists( filePath ))
	{	
		//--Read the Last changelistnumber of change which was in the latest build--
		var tempText = ReadFile(filePath, true);
		var output = parseInt(tempText);
		if (isNaN(output))
			output = ""; 
		else // Increment the changelistnumber so only new changes will be shown
			output++;
		
		return(output);
	}	
	else
	{
		return("");
	}
	
}


//+++++++++++++++++++++++++Get build number and create folderds+++++++++++++++++++++++++++++++
function Init()
{
	CheckAllDirLinks();

	// Log when build started.
	startTime = new Date();
	var logBuildStartedLine ="\r\n";
	// Change the loging line depending on kind of build
	if (fastBuild)
	{
		logBuildStartedLine += "Fast ";
	}
	
	// Add some Dates to logline
	logBuildStartedLine += " building started at " + GetDateString() + ", " + GetTimeString();
	Log(logBuildStartedLine);
		
	
	// Some output for the user of the sources and targets
	Log("\r\nCode Source = "+codeFolder);
	

	// Will check for enough space on all needed drives and free space on the storage drive if needed
	CheckHDSpace(false);

	// Delte the temporary files
	DeleteTemporary();
	
	// Make folders depending on which build is started
	if(!fastBuild && !DEBUG)
	{
		MakeFolder( buildingFolder );
		MakeFolder( buildGameDataFolder );
	}
}

// Collection of links to check
function CheckAllDirLinks()
{
	if(!DEBUG && !CheckLinkToDir(finalStorageFolder,false))
		FSO.CreateFolder(finalStorageFolder );
		
	if(fastBuild && !DEBUG)
	{
		if(!CheckLinkToDir(buildingFolder,false))
		{
			Log("ERROR! A major working path does not exists: " + buildingFolder); 
			Log("Changing build type to a NON fastbuild because of that!");
			fastBuild == false;
		}
	}
}

// Will check if link exists and quit if not
function CheckLinkToDir(path,errorExit)
{
	if(!FSO.FolderExists(path))
	{
		if(errorExit)
		{
			ErrorExit( "ERROR! A major working path does not exists: " + path + ".\n" , aFailEmail , aErrFiles );
			WScript.Quit(1);
		}
		else
			return false;
	}
	else 
		return true;
}



//+++++++++++++++++++++++++Will check for enough space on all needed drives and free space on the storage drive if needed+++++++++++++++++++++++++
function CheckHDSpace(silentMode)
{
	CheckBuildSpace();
}


//++++++++++++++++++++++Is sending mails and will interrupt the build if not enough space++++++++++++++++++++++
function CheckBuildSpace()
{
	// Get the available space
	var driveName = FSO.GetdriveName(buildDrive);
	var oDrive = FSO.GetDrive(driveName);
	var freeMb = Math.round(oDrive.FreeSpace/1024/1024);

	// Check if one gig is free
	if(freeMb < 1024)
	{
		// Check if free space is more than 200 Mb
		if(freeMb > 200)
		{
			// Send notification mail
			var mailMessage = "WARNING: Only " + freeMb + " Mb left on Drive " + driveName + ". Please free some space!";
			SendMail( aFailEmail,mailMessage,"LOW SPACE ON BUILD MACHINE WARNING!!",aEmptyArr,2 );
		}
		else
		{
			ErrorExit( "ERROR: Not enough space on drive " + driveName + " ! Couldn't contuniue building.Please free some space!" , aFailEmail , aErrFiles );
		}
	}	 	
}



//+++++++++++++++++++++++++Function to get latest code from perforce+++++++++++++++++++++++++
function PerforceSync(source, force)
{
	var options = "";
	if (force) options += " -f";
	
	
	Log("Start syncing with perforce with following command:");
	var cmdline = p4_cmdLine + options + " " + source + "...";
	Log( cmdline );
	WSHSHELL.Run( cmdline,1,true );
}

//++++++++++++++++++Will set/get whether a force sync is neeeded on next fast build++++++++++++++
function NeedforceSyncOfCode(ForceSyncInNextBuild)
{
	if(ForceSyncInNextBuild==true)
	{
		Log("Setting the flag for force sync of Code on next fast build");
		FSO.CreateTextFile( needforceSyncOfCodeFile,true );  
		var loadedneedforceSyncOfCodeFile = FSO.GetFile( needforceSyncOfCodeFile );   
		var stream = loadedneedforceSyncOfCodeFile.OpenAsTextstream( forWriting,tristateUseDefault );
		stream.Write("yes");
		stream.Close();
	}
	else if(ForceSyncInNextBuild==false)
	{
		Log("Deleting the flag for force sync of Code on next fast build");
		FSO.CreateTextFile( needforceSyncOfCodeFile,true );  
		var loadedneedforceSyncOfCodeFile = FSO.GetFile( needforceSyncOfCodeFile );   
		var stream = loadedneedforceSyncOfCodeFile.OpenAsTextstream( forWriting,tristateUseDefault );
		stream.Write("no");
		stream.Close();
	}
	else if(!ForceSyncInNextBuild)
	{
		if (FSO.FileExists( needforceSyncOfCodeFile ))
		{
			try
			{
				Log("Loading NeedforceSyncOfCode file from: " + needforceSyncOfCodeFile);
				var output = ReadFile(needforceSyncOfCodeFile, false);
				output = output.toLowerCase();
				if(output=="yes")
				{
					return(true);
				}
				else if(output=="no")
				{
					return(false);
				}
				else
				{
					Log("WARNING: Value in File \""+needforceSyncOfCodeFile+"\" is not valid and can't be handlet");
					return(true);
				}
			}
			catch(e)
			{
				Log("WARNING: This problem \"" + e.description + "\" occured during accessing the file \"" + needforceSyncOfCodeFile + "\"");
				return(true);
			}
			
		}
		else
		{
			Log("WARNING:Could not find flag file which says whether a force sync of code is needed from:"+needforceSyncOfCodeFile);
			return(true);
		}
	}
	else
		Log("ERROR: Wrong parameter was given to function NeedforceSyncOfCode. Only alowed values are true, false or no argument");
}


//+++++++++++++++++++++++++Compile project using Incredebuild/MSDevStudio+++++++++++++++++++++++++
function Compileproject(  workspace , projectToCompile , config , notifyOnExit , rebuildIntern)
{
	resultOfCompilingproject = 0;
	
	Log( "Compiling Solution: " + workspace + " In Configuration: " + config );
	var cmdLine;

	if (useIncrediBuild)
	{
		cmdLine = buildConsole + " " + workspace + " /Cfg=\"" + config + "\" /Prj=" + projectToCompile + " /Wait /BrowseInfo=OFF /Log=" + cppLog+ " /ALL";
		if(rebuildIntern)
			 cmdLine += " /Rebuild"
			 
		//cmdLine = buildConsole + " "+workspace+" /Cfg=\"" + config + "\" /Prj=* /BrowseInfo=OFF /Log=" + cppLog;
	}
	else
	{
		cmdLine = msDevStudio + " " + workspace + " /build \""+config + "\" /out " + cppLog;
		if(rebuildIntern)
			 cmdLine += " /Rebuild"
	}
	
	
	Log( cmdLine );
	// # //c/msdev/common/MsDev98/bin/msdev $CPP_PROJECT /MAKE RELEASE /OUT cppbuild.log
	var t0 = new Date();
	
	
	resultOfCompilingproject = WSHSHELL.Run( cmdLine,1,true );
		
	Log("Exit Code of Compiler: " + resultOfCompilingproject);
	
	if (resultOfCompilingproject != 0 )
	{	
		if(notifyOnExit)
		{	
			var temp = "Error while compiling Workspace: "+ workspace + " In Configuration: " + config;
			
			if(resultOfCompilingproject == 1)
				temp += "\r\nErrors were encountered during the compiliation. Please investigate the Problem and fix it.";
			else if(resultOfCompilingproject == 2)
				temp += "\r\nA fatal IncrediBuild error was encountered (Invalid parameters, input file not found etc.). Please investigate the Problem and fix it.";
			else if(resultOfCompilingproject == 3)
				temp += "\r\nThe operation was stopped by user before completing. Please investigate the Problem and fix it.";
			
			if(exitOnCompilingerror)
			{
				if(resultOfCompilingproject == 1)
				{				
					if(!DEBUG)
					{
						aFailEmail = aFailEmail.concat(aCompileErrorRecipient);
					}
				}			
				
				var aCompileLogFile = new Array(cppLog);
				MailFailure(temp , aFailEmail , aCompileLogFile );
			}
			else
			{
				emailAdditions += "\r\n\r\n" + temp;
			}
		}
		return false;
	}
	
	LogTime( t0 );
	return true;
}


//+++++++++++++++++++++++++Final Operation e.g logging the timings for the build+++++++++++++++++++++++++
function EndLog()
{
	endTime = new Date();
	
	Log("\r\n-------------------------------------------------------------------------");   	
	Log( "Build ended at " + GetDateString() + ", " + GetTimeString()  );

	if (getLatest)
	{	
		Log( "Code Retrieve Time: "+GettimeDiff(startGetTime,endGetTime) );
	}
	
	if(buildCode)
		Log( "Compile Time: "+GettimeDiff(startCompileTime,endCompileTime) );
	
	if(processAnimations)
		Log( "Compile Time: "+GettimeDiff(startProcessAnimationTime,endProcessAnimationTime) );
		
	if(processTifs)
		Log( "Compile Time: "+GettimeDiff(startProcessTifTime,endProcessTifTime) );
		
	if(copyBuild)
		Log( "Compile Time: "+GettimeDiff(startCopyTime,endCopyTime) );
	
	Log( "Total Build Time: "+GettimeDiff(startTime,endTime) );
	Log("Build succesfully finished.");
	Log("-------------------------------------------------------------------------\r\n");
	
	// Log Success event.
	WSHSHELL.LogEvent( 0, "Automated Build Completed Successfully" );											
	
	MailSuccess();
}

//+++++++++++++++++++++++++Sending mail on buildingsuccess+++++++++++++++++++++++++
function MailSuccess()
{
	// Send build confirmation Message.

	var subject = "Successfully completed ";
	var msg = "";
	
	// Change the loging line depending on kind of build
	if (fastBuild)
		msg += "Fast ";
	
	subject += "SCRUM Build";
	msg += "SCRUM Build started at "+GetDateString(startTime)+", "+GetTimeString(startTime);
	msg += " completed successfully at "+GetDateString(endTime)+", "+GetTimeString(endTime);
	msg += "\r\nTotal Build Time: "+GettimeDiff(startTime,endTime);
	
	if( emailAdditions != "")
	{
		msg += "\r\n\r\nAdditional Build informations:\r\n" + emailAdditions;
	}
	msg = msg + "\r\n\r\nLatest Build is Located at "+ finalStorageFolder+ " on computer " + computerName;
	
	SendMail( aSuccessEmail,msg,subject,aScsFiles,1, logFile );
}



//+++++++++++++++++++++++++Function for writing to the log+++++++++++++++++++++++++
function Log(str) 
{
	logFile.WriteLine( str );
	WScript.StdOut.WriteLine( str );
	//WScript.Echo(str);

	numLogs = numLogs + 1;
}

//+++++++++++++++++++++++++Logs an error and send a mail if something goes wrong+++++++++++++++++++++++++
function ErrorExit( ErrStr , aErrRecipients , aErrLogs )
{
	errorString =ErrStr;
	error = true;
	Log ( errorString );
	WSHSHELL.LogEvent( 1, "SCRUM Build Failed." );
	WSHSHELL.LogEvent( 1,errorString );
	if(aErrRecipients != "")
		MailFailure(ErrStr , aErrRecipients , aErrLogs, logFile );
		
	WScript.Echo (  errorString  );
	WScript.Quit(1);
}

//+++++++++++++++++++++++++Logs time of operation and elapsed time since start of build.+++++++++++++++++++++++++
function LogTime( t0 )
{
	var tnow = new Date();
	Log( "Done in "+GettimeDiff(t0,tnow) );
	Log( "Elapsed time since build start: "+GettimeDiff(startTime,tnow) );
	Log("");
}


//+++++++++++++++++++++++++Send a mail if the build failes+++++++++++++++++++++++++
function MailFailure( ErrMailStr , aErrMailRecipients , aErrMailLogs , scriptLogObjectForClose)
{
	// Send build failure Message.
	var str = "";
	
	if (fastBuild) str = "Fast ";
	var msg = str+"SCRUM build at "+GetDateString()+", "+GetTimeString()+" failed.";
	
	msg += "\r\n"+ErrMailStr;

	if( emailAdditions != "")
	{
		msg += "\r\n\r\nAdditional informations:\r\n" + emailAdditions;
	}
	var mailsubject = "Failed to complete SCRUM build!";
	
	SendMail( aErrMailRecipients,msg,mailsubject,aErrMailLogs,2 ,scriptLogObjectForClose);
}

//+++++++++++++++++++++++++Mainfunction for sending mails+++++++++++++++++++++++++
function SendMail( recipient,msg,subject,attachmentsArray,importance, scriptLogObjectForClose )
{
	Log( "Sending Mail to "+recipient+", With subject: "+subject );
	
	if(scriptLogObjectForClose != null)
		scriptLogObjectForClose.Close();

	var cdoConfig = new ActiveXObject("CDO.Configuration");
	cdoConfig.Fields("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2; //cdoSendUsingPort
	cdoConfig.Fields("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = 1; //cdoBasic
	cdoConfig.Fields("http://schemas.microsoft.com/cdo/configuration/smtpserver") = mailServer;
	cdoConfig.Fields("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25;
	cdoConfig.Fields.Update();

	var cdoMessage = new ActiveXObject( "CDO.Message" );
	cdoMessage.Configuration = cdoConfig;

	cdoMessage.subject = subject;
	cdoMessage.From = "Build@crytek.de";
	cdoMessage.To = recipient;
	cdoMessage.TextBody = msg;

	var i;
	for (i = 0; i < attachmentsArray.length; i++)
	{
		if (FSO.FileExists( attachmentsArray[i] ))
		{
			cdoMessage.AddAttachment( attachmentsArray[i] );
		}
	}

	try
	{
		cdoMessage.Send();
	}
	catch(e)
	{
		Log("The following error occured while trying to send a mail:\r\n"+e.description);
		Log("\r\nMail properties:\r\nSubject:\r\n" + subject + "\r\nMessage:\r\n"+msg+"\r\n\Recipient:\r\n"+recipient);
	}
	cdoConfig = null;
	cdoMessage = null;
}

//+++++++++++++++++++++++++Get difference between Time1 and Time2+++++++++++++++++++++++++
function GettimeDiff( t0,t1 )
{
	var timeDiff = t1.getTime() - t0.getTime();
	var secMilli = 1000;
	var minMilli = 1000 * 60;
	var hrMilli = minMilli * 60;
	var dyMilli = hrMilli * 24;
	
	var t,h,m,s;
	t = timeDiff;
	h = Math.floor( t / hrMilli);
	t = timeDiff % hrMilli;
	m = Math.floor( t / minMilli );
	t = t % minMilli;
	s = Math.floor( t / secMilli );
	var str = ""+h+"h:"+m+"m:"+s+"s";
	return str;
}

//+++++++++++++++++++++++++Get date+++++++++++++++++++++++++
function GetDateString(givenDate)
{
	var d = "";
	if(givenDate != null)
		d = givenDate;
	else
		d = new Date();
		
	return d.getDate() + "/" + (d.getMonth()+1)+"/"+d.getYear();
}



//+++++++++++++++++++++++++Get Time function+++++++++++++++++++++++++
function GetTimeString(givenDate)
{
	var d = "";
	var c = ":";
	
	if(givenDate != null)
		d = givenDate;
	else
		d = new Date();
	 
   var s = d.getHours() + c + d.getMinutes() + c + d.getSeconds();
   return(s);
}

//+++++++++++++++++++++++++Function for deleting files+++++++++++++++++++++++++
function DeleteFiles( FileSpec , recursive, errorExit )
{
	Log( "Delete Files: "+FileSpec );
	var cmdLine ="cmd /c del "+FileSpec+" /F /Q";
	if(recursive)
		cmdLine += " /S";
		
	result = WSHSHELL.Run( cmdLine, 1, true );
	if(result != 0 && errorExit)
		ErrorExit( "error: Deleting couldn't be finished correctly"  , aFailEmail , aErrFiles );
}

//+++++++++++++++++++++++++Function for making folders+++++++++++++++++++++++++
function MakeFolder( folder )
{
	//WSHSHELL.Run( "cmd /E:ON /C mkdir "+folder,1,true );
	if (FSO.FolderExists(folder) == false)
	{
		Log( "Making Folder: "+folder );
		try
		{
			FSO.CreateFolder(folder);
		}
		catch(e)
		{
			Log("Warning: "+folder+e.description);
		}
	}
}



//+++++++++++++++++++++++++Delete and check if it was succesful+++++++++++++++++++++++++
function DeleteAndCheck(path,isFile) 
{
	if(isFile==true)
	{
		if (FSO.FileExists( path ))
		{
			try
			{
			FSO.DeleteFile( path );
			Log("Successful deleted "+path );
			return true;
			}
			catch(e)
			{
				Log(e.description + " on: " + path);
				return false;
			}
		}
		else
		{
				Log("Could not find file for deleting:"+path);
				return true;
		}
	}
	else if(isFile==false)
	{
		if (FSO.FolderExists( path ))
		{
			try
			{
			FSO.DeleteFolder( path, true );
			Log("Successful deleted "+path );
			return true;
			}
			catch(e)
			{
				Log(e.description + " on: " + path);
				return false;
			}
		}
		else
		{
				Log("Could not find folder for deleting:"+path);
				return true;
		}
	}
	else
		Log("Function \"DeleteAndCheck\"Could not figure out of the argument given if path is an file or a folder")
}
	
//+++++++++++++++++++++++++Delete all temporary files.+++++++++++++++++++++++++
function DeleteTemporary() 
{
	Log("\r\nDeleting Temporary Files");
	DeleteAndCheck(cppLog,true);
		
	if (fastBuild || DEBUG)
		return;

	if(!DeleteAndCheck(buildingFolder,false))
		ErrorExit("Could not clean up the compile directory!(" + buildingFolder + ")\r\nThis can have several reasons e.g. a program is accessing the folder. Please solve the problem and restart the build.", aFailEmail , aErrFiles );

	Log("");
  return;
}

//+++++++++++++++++++++++++Function for executing commandlines+++++++++++++++++++++++++
function ExecuteCommandLine( cmdLine ) 
{
	Log( cmdLine );
	var newcmdLine = "cmd /c " + cmdLine;
	WSHSHELL.Run( cmdLine,1,true );
}

//+++++++++++++++++++++++++Moving folders so the converted animations will be in the right folder in the pak+++++++++++++++++++++++++
function PreliminaryRCAnimTasks()
{
	Log("Executing preliminary tasks for compiling animations");
	
	if(!FSO.FileExists(scriptLocation + "exclCAF.txt"))
	{
		//Create exclude file if not already exists
		var file = FSO.CreateTextFile( scriptLocation + "exclCAF.txt" );
		file.Write( ".caf" );
		file.Close();
	}
	
	Log("Moving animations from CVS to a backup dir");
	// Temporary move original files
	FSO.MoveFolder(animationsBasePath , animationsCVSPath);
	
	// Move converted files to the standart animations folder if 
	if ( FSO.FolderExists(animationsConvertedPath) )
	{
		Log("Moving converted animations to the base animation directory");
		FSO.MoveFolder(animationsConvertedPath , animationsBasePath);
	}
	
	// Clone folder structure and copy all but "caf" files there
	Copy ( animationsCVSPath , animationsBasePath + "\\" , true , true , true , scriptLocation +"exclCAF.txt" );
}

//+++++++++++++++++++++++++Moving folders so the original animations from CVS will be in the right folder for sync+++++++++++++++++++++++++
function RollbackRCAnimTasks()
{
	// after the processed file are packed move them to a backup dir and files from CVS to the original folder 
	Log("Moving converted animations to a backup directory");
	FSO.MoveFolder(animationsBasePath , animationsConvertedPath);
	Log("Moving animations from CVS to a the base animations directory");
	FSO.MoveFolder(animationsCVSPath , animationsBasePath);
}

//+++++++++++++++++++++++++Calling Resource Compiler to convert the formats(statistics are only working at a non fastbuild)+++++++++++++++++++++++++
//++++CleanUp is only for compressing Animations!!++++
function RunResourceCompiler(workingDir, fileSpec, convType, extArg)
{
	var t0 = new Date();
	// Remember current directory.
	var curDir = WSHSHELL.CurrentDirectory;
	
	var logNameExt = "";
  
	// Sets current directory to needed folder.
	WSHSHELL.CurrentDirectory = workingDir;

	
	var cmdline = resourceCompiler + " " + fileSpec;
	
	if(convType.toLowerCase() == "tif")
	{
		logNameExt = "_Tifs";
		cmdline += " /fallback";
		if (!fastBuild)
		{
			cmdline += " /statistics";
			cmdline += " /refresh";
		}
	}
	else if(convType.toLowerCase() == "animation")
	{
		logNameExt = "_Animations";
		cmdline += " /cleanupfast=1";
		if(!isBranchBuild)
		{
			if (!fastBuild)
			{
				cmdline += " /build=1";
			}
			else
				cmdline += " /update=1";
		}
	}			
	
	if( extArg != "")
	{
		cmdline += " " + extArg;
	}
	
	Log( cmdline );
	result = WSHSHELL.Run( cmdline,1,true );
	if(result != 0)
		ErrorExit( "error: Resource Compiler couldn't be finished correctly"  , aFailEmail , aErrFiles );
		
	if(!fastBuild && logNameExt != "")
	{
		FSO.MoveFile(bin32Folder+"\\rc\\rc_log.log" , bin32Folder+"\\rc\\rc_log" + logNameExt + ".log");
		FSO.MoveFile(bin32Folder+"\\rc\\rc_log_errors.log" , bin32Folder+"\\rc\\rc_log_errors" + logNameExt + ".log");
		FSO.MoveFile(bin32Folder+"\\rc\\rc_log_warnings.log" , bin32Folder+"\\rc\\rc_log_warnings" + logNameExt + ".log");
	}

	// Restore previous directory.
	WSHSHELL.CurrentDirectory = curDir;
 	
	LogTime( t0 );
}


//+++++++++++++++++++++++++Copying folders and files with arguments+++++++++++++++++++++++++
function Copy(Source,Destination,Recrusive,KeepAttributes,CopyHiddenFiles,Exclude)
{	
	var cmdLine = "xcopy ";
	cmdLine += Source + " " + Destination;
	cmdLine +=" /I /Y /C /R";
	if (Exclude != "") cmdLine +=" /exclude:"+Exclude;
	if (Recrusive) cmdLine +=" /E";
	if (KeepAttributes) cmdLine +=" /K";
	if (CopyHiddenFiles) cmdLine +=" /H";
	Log( "Start copying Folder "+Source+" to "+Destination);
	if(!DEBUG)
	{
		result = WSHSHELL.Run( cmdLine,1,true );
		if(result != 0)
		ErrorExit( "error: Copying couldn't be finished correctly"  , aFailEmail , aErrFiles );
	}
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//+++++++++++++++++++++++++++++|END|  FUNCTION DEFENITIONS  |END|++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////






/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//+++++++++++++++++++++++++++++++++++++++++|START|  *MAIN*  |START|++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Goto batch mode.
WScript.Interactive = true;

Log("Automated SCRUM build system");
Log("*******************************************");
Log("For bugs and comments: Denis@Crytek.de\r\n\r\n");

if(DEBUG)
	Log("\r\n---You are in DEBUG mode!!!!---\r\n");
	
//CheckForCodeChanges();

Pre_Init();

Init();

Post_Init();

//--------------------------Getting latest from Perforce--------------------------
if (getLatest)
{
	Log("\r\n-------------------------------------------------------------------------");
	startGetTime = new Date();
	
	Pre_GetLatest();
	
	// Get the flag which says if a force sync is needed
	var forceSyncOfCode = NeedforceSyncOfCode();
	
	// Do a force sync and set the flag to "no sync needed"
	if(!DEBUG)
	{
		if(forceSyncOfCode==true)
		{
			PerforceSync( p4Source , true);
			NeedforceSyncOfCode(false);
		}
		else
 			PerforceSync( p4Source, !fastBuild);
	}
	
	Post_GetLatest();
	
	endGetTime = new Date();
	Log("-------------------------------------------------------------------------\r\n");	
}

//----------------------------------Compile project------------------------------
if (buildCode)
{
	Pre_CompileCode();
	
	Log("\r\n-------------------------------------------------------------------------");
	Log("Compiling Code.");
	startCompileTime = new Date();

	if(!DEBUG)
	{	
		// Compile whole solution in 32 bit	
		Compileproject( projectCppWorkspace,cppProjects, buildConfig + "|Win32" , true , rebuildCode );
		
		// Compile whole solution in 64 bit
		Compileproject( projectCppWorkspace,cppProjects, buildConfig + "|x64" , true , rebuildCode );
	}
	
	endCompileTime = new Date();
	Log("-------------------------------------------------------------------------\r\n");
	
	Log("Deleting temporary files.");
	DeleteFiles( bin32Folder+"\\*.exp" , true, false );
	DeleteFiles( bin32Folder+"\\*.lib" , true, false );
	DeleteFiles( bin32Folder+"\\*.ilk" , true, false );
	DeleteFiles( bin64Folder+"\\*.exp" , true, false );
	DeleteFiles( bin64Folder+"\\*.lib" , true, false );
	DeleteFiles( bin64Folder+"\\*.ilk" , true, false );
	
	Post_CompileCode();
}

//----------------------------------Convert fiels with RC----------------------------------
if (processAnimations || processTifs)
{	
	Pre_ProcessAssets();
	
	Log("Checking for the Resource Compiler");
	if((processAnimations || processTifs ) && !FSO.FileExists( buildingFolder + "\\Bin32\\RC\\rc.exe"))
		ErrorExit( "ERROR! The Resource Compiler executable couldn't be found in : " + buildingFolder + "\\Bin32\\RC\\rc.exe" , aFailEmail , aErrFiles );
	
	Log("\r\n-------------------------------------------------------------------------");
	Log("Begin Resource Compiler");
	if(!DEBUG)
	{
		if(processAnimations)
		{
			startProcessAnimationTime = new Date();
			
			var animationsBasePath = buildGameDataFolder+"\\Animations";
			var animationsCVSPath = buildGameDataFolder + "\\Animations_FromCVS";
			var animationsConvertedPath = buildGameDataFolder+"\\Animations_converted";
			var animationsCBAFile = buildGameDataFolder + "\\Animations_FromCVS\\Animations.cba";
			
			PreliminaryRCAnimTasks();
						
			var rc_Image_Add_Arg = " /dest=\"" + animationsBasePath + "\"";
			
			// Compress the animations (Animations got from CVS(source) and processed animations(destination) can't be in the same folder.)
			RunResourceCompiler(buildingFolder, animationsCBAFile,  "Animation", rc_Image_Add_Arg );
			
			RollbackRCAnimTasks();
			
			endProcessAnimationTime = new Date();
		}
		
		if(processTifs)
		{
			startProcessTifTime = new Date();
			
			// Run on tifs last because of statistics
			RunResourceCompiler(buildingFolder, "*.tif", "Tif", "");
			
			endProcessTifTime = new Date();
		}
	}
	Log("End Resource Compiler");
	Log("-------------------------------------------------------------------------\r\n");
	
	Post_ProcessAssets();
}

if(copyBuild)
{
	Pre_CopyBuild();
	
	startCopyTime = new Date();
		
	if (buildCode)
	{		
		//Copy all needed data from build folder to temporary copy directory
		Copy ( bin32Folder , finalStorageFolder+"\\Bin32\\" , true , true , true , scriptLocation +"exclude.lst" );
		Copy ( bin64Folder , finalStorageFolder+"\\Bin64\\" , true , true , true , scriptLocation +"exclude.lst" );	
		if(FSO.FolderExists(buildGameDataFolder+"\\Shaders"))
			Copy ( buildGameDataFolder+"\\Shaders" , finalStorageFolder+"\\Game\\Shaders\\" , true , true , true ,scriptLocation +"exclude.lst"  );
		if(FSO.FolderExists(buildGameDataFolder+"\\Scripts"))
			Copy ( buildGameDataFolder+"\\Scripts" , finalStorageFolder+"\\Game\\Scripts\\" , true , true , true ,scriptLocation +"exclude.lst"  );
	}
	
	if (processAnimations)
	{
		Copy ( buildGameDataFolder+"\\Animations" , finalStorageFolder+"\\Game\\Animations\\" , true , true , true ,scriptLocation +"exclude.lst"  );
	}
	
	if (processTifs)
	{
		if(FSO.FolderExists(buildGameDataFolder+"\\Config"))
			Copy ( buildGameDataFolder+"\\Config" , finalStorageFolder+"\\Game\\Config\\" , true , true , true ,scriptLocation +"exclude.lst"  );
		if(FSO.FolderExists(buildGameDataFolder+"\\Entities"))
			Copy ( buildGameDataFolder+"\\Entities" , finalStorageFolder+"\\Game\\Entities\\" , true , true , true ,scriptLocation +"exclude.lst"  );
		if(FSO.FolderExists(buildGameDataFolder+"\\Fonts"))
			Copy ( buildGameDataFolder+"\\Fonts" , finalStorageFolder+"\\Game\\Fonts\\" , true , true , true ,scriptLocation +"exclude.lst"  );
		if(FSO.FolderExists(buildGameDataFolder+"\\Languages"))
			Copy ( buildGameDataFolder+"\\Languages" , finalStorageFolder+"\\Game\\Languages\\" , true , true , true ,scriptLocation +"exclude.lst"  );
		if(FSO.FolderExists(buildGameDataFolder+"\\Levels"))
			Copy ( buildGameDataFolder+"\\Levels" , finalStorageFolder+"\\Game\\Levels\\" , true , true , true ,scriptLocation +"exclude.lst"  );
		if(FSO.FolderExists(buildGameDataFolder+"\\Libs"))
			Copy ( buildGameDataFolder+"\\Libs" , finalStorageFolder+"\\Game\\Libs\\" , true , true , true ,scriptLocation +"exclude.lst"  );
		if(FSO.FolderExists(buildGameDataFolder+"\\Materials"))
			Copy ( buildGameDataFolder+"\\Materials" , finalStorageFolder+"\\Game\\Materials\\" , true , true , true ,scriptLocation +"exclude.lst"  );
		if(FSO.FolderExists(buildGameDataFolder+"\\Music"))
			Copy ( buildGameDataFolder+"\\Music" , finalStorageFolder+"\\Game\\Music\\" , true , true , true ,scriptLocation +"exclude.lst"  );
		if(FSO.FolderExists(buildGameDataFolder+"\\Objects"))
			Copy ( buildGameDataFolder+"\\Objects" , finalStorageFolder+"\\Game\\Objects\\" , true , true , true ,scriptLocation +"exclude.lst"  );
		if(FSO.FolderExists(buildGameDataFolder+"\\Prefabs"))
			Copy ( buildGameDataFolder+"\\Prefabs" , finalStorageFolder+"\\Game\\Prefabs\\" , true , true , true ,scriptLocation +"exclude.lst"  );
		if(FSO.FolderExists(buildGameDataFolder+"\\Sounds"))
			Copy ( buildGameDataFolder+"\\Sounds" , finalStorageFolder+"\\Game\\Sounds\\" , true , true , true ,scriptLocation +"exclude.lst"  );
		if(FSO.FolderExists(buildGameDataFolder+"\\Textures"))
			Copy ( buildGameDataFolder+"\\Textures" , finalStorageFolder+"\\Game\\Textures\\" , true , true , true ,scriptLocation +"exclude.lst"  );
		if(FSO.FolderExists(buildGameDataFolder+"\\Editor"))
			Copy ( buildGameDataFolder+"\\Editor" , finalStorageFolder+"\\Game\\Editor\\" , true , true , true ,scriptLocation +"exclude.lst"  );
		if(FSO.FolderExists(buildGameDataFolder+"\\Tools"))
			Copy ( buildGameDataFolder+"\\Tools" , finalStorageFolder+"\\Game\\Tools\\" , true , true , true ,scriptLocation +"exclude.lst"  );
	}
	
	endCopyTime = new Date();
	
	Post_CopyBuild();
}

EndLog();

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//+++++++++++++++++++++++++++++++++++++++++|END|  ~MAIN~  |END|++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


}
catch(e)
{
	WScript.Echo("error: "+e);
	WScript.Echo("error number: "+e.number & 0xFFFF);
	WScript.Echo("error description: "+e.description);
	logFile = FSO.CreateTextFile(  scriptLocation + "SCRUM_build_ERROR.log",true );
	logFile.WriteLine("error: "+e);
	logFile.WriteLine("error number: "+e.number & 0xFFFF);
	logFile.WriteLine("error description: "+e.description);
	WScript.Quit(1);
}
