//---This script will start the precaching of shaders if needed---
//---Written by Denis Barth(Denis@crytek.de)

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");

var forReading = 1, forWriting = 2, forAppending = 8;
var tristateUseDefault = -2, tristateTrue = -1, tristateFalse = 0;

NoGUIApplication();

// Specify here your mail if you want to get error mails
var adminMail = "Denis@Crytek.de";

// Folder where the buildfiles are located
var computerName = WSHNETWORK.ComputerName.toLowerCase();
var startedInPath = WSHSHELL.CurrentDirectory ;
var workingScriptPath = WScript.ScriptFullName;
var scriptName = "";
var scriptLocation = "";
var regExp = /(.+)\\(.+)\..+$/i;
if(workingScriptPath.search(regExp) != "-1")
{
	scriptLocation = RegExp.$1;
	scriptName = RegExp.$2;
}
else
{
	ErrorExit("Could not get the directory or script name of the working script.");
}

// Create a Log file
var logFile = scriptLocation  + "\\" + scriptName + ".log";
var openedLogFile = FSO.CreateTextFile( logFile ,true );


// Path for the get build script
var getBuildScript = "\\\\storage\\builds\\Build_tools\\get_latest_build.js";
var startGetBuildScriptOptions = "/Cleanup /Latest /exclude:ShaderCompilation.txt";

var SPCPath = scriptLocation + "\\SPC";
var buildDestinationPath = SPCPath + "\\SPC";

if(DEBUG)
{
	var a_failRecipient = new Array("Denis@Crytek.de");
}
else
{
	var a_failRecipient = new Array("FP_Build@crytek.de","Timur@crytek.de","Denis@Crytek.de");
}

var a_logFiles = new Array(logFile);
var a_emptyFile = new Array();


///////////////////////////////////////////////////////////////////////////////
// Main.
///////////////////////////////////////////////////////////////////////////////

Log("Start of precaching of shaders if it's needed");
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");

// Get the current Date as custom formated string
startScriptTime = GetConvertedDate();
Log("Started at " + startScriptTime);


var serverBuildNr = "";
var localBuildNr = "";

var localBuildFile = buildDestinationPath + "\\buildname.txt";
var serverBuildFile = "\\\\storage\\builds\\Build_tools\\Build_links\\Latest_build.txt";


// Get the local build name
var localBuildName = ReadFile(localBuildFile, false);
// Get the latest build name
var serverBuildName = ReadFile(serverBuildFile, false);


// output for debug
Log("\r\nserverBuild source: " + serverBuildFile);
Log("localBuildName source: " + localBuildFile + "\r\n\r\n");

Log("\r\nserverBuildName: " + serverBuildName);
Log("localBuildName: " + localBuildName + "\r\n\r\n");


// Isolate build number
var regExp = /\((\d+)\)_\d+_\d+.*/i;
if(serverBuildName.search(regExp)!= "-1")
	serverBuildNr = parseInt(RegExp.$1);
	
if(localBuildName.search(regExp)!= "-1")
	localBuildNr = parseInt(RegExp.$1);

	
Log("localBuildNr: " + localBuildNr);
Log("serverBuildNr: " + serverBuildNr + "\r\n\r\n");


// compare
if(isNaN(localBuildNr) || isNaN(serverBuildNr) || localBuildNr == "" || serverBuildNr == "")
{
	var msg = "Couldn't determine whether to run the precaching or not. Local or server build name source is corrupt!";
	Log(msg);
	msg += "\r\n\r\nPS: Precaching was started although, because this is the default behaviour.";
	var subject = "Shader cache compilation INFO";
	SendMail( a_failRecipient, msg, subject, a_emptyFile, 1, false);
}
else if(localBuildNr < serverBuildNr)
{
	Log("Newer build is available. Starting precaching of shaders.");
}
else
{
	Log("No newer build available");
	WScript.Quit(1);
}
	
if(!Execute("CloseShares.bat", true ))
	ErrorExitWithMail( "Failed to close shares."  , a_failRecipient , a_logFiles );

if(!Execute("cscript " + getBuildScript + " /BuildDir:" + "\"" +buildDestinationPath + "\" " + startGetBuildScriptOptions, true ))
	ErrorExitWithMail( "Failed to copy the build. Please check the get latest build script."  , a_failRecipient , a_logFiles );
	
if(!fsoCopy(scriptLocation + "\\system.cfg" , buildDestinationPath + "\\system.cfg" , false, true ))
	ErrorExitWithMail( "Failed to copy system files. Please check the log."  , a_failRecipient , a_logFiles );
	
if(!fsoCopy("\\\\mastercd\\ShaderCache\\version_1\\ShaderList.txt" , SPCPath + "\\ShaderList.txt" , false, true ))
	ErrorExitWithMail( "Failed to copy system files. Please check the log."  , a_failRecipient , a_logFiles );
	
if(!DEBUG)
{
	// Start precaching in a new window without to care about exit code
	Log("DJS_CompileShaders.bat");
	WSHSHELL.Run( "DJS_CompileShaders.bat", 1, true );
	
	CheckFolderForFilesNotRecursive(SPCPath + "\\ShaderCache" ,true);
	
	Log("PakShaders.bat");
	WSHSHELL.Run( "PakShaders.bat", 1, true );
	Log("cscript DistributeShaders.js /SPC:" + SPCPath + " /StartTime:\"" + startScriptTime + "\"");
	WSHSHELL.Run( "cscript DistributeShaders.js /SPC:" + SPCPath + " /StartTime:\"" + startScriptTime + "\"", 1, true );
}
else
	WScript.Sleep(120000);

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////////
// Functions.
///////////////////////////////////////////////////////////////////////////////

//+++++++++++++++++++++++++Logs an error and send a mail if something goes wrong+++++++++++++++++++++++++
function ErrorExitWithMail( ErrStr , aErrRecipients , aErrLogs )
{
	errorString = ErrStr;
	Log ( errorString );
	WSHSHELL.LogEvent( 1,errorString );
	MailFailure(ErrStr , aErrRecipients , aErrLogs );
	ErrorExit(errorString);
}

function ErrorExit(errStr)
{
	Log(errStr);
	Log("<ReturnCode:1>");
	WScript.StdErr.Write("<ReturnCode:1>");
	WScript.Sleep(5000);
	WScript.Quit(1);
}

//+++++++++++++++++++++++++Send a mail if the build failes+++++++++++++++++++++++++
function MailFailure( ErrMailStr , aErrMailRecipients , aErrMailLogs )
{
	// Send build failure Message.
	var str = " ";
	var msg = "Automated Shader cache compilation started at " + startScriptTime + " failed.";
	
	msg += "\r\n"+ErrMailStr;

	var mailsubject = "Shader cache compilation failed!";
	SendMail( aErrMailRecipients,msg,mailsubject,aErrMailLogs,2 ,openedLogFile);
}

//+++++++++++++++++++++++++Will read in a hole file and return an empty string if something goes wrong+++++++++++++++++++++++++
function CheckFolderForFilesNotRecursive(folderspec, errorExit)
{
  var filesEnum, folderObj;
  CheckLink(folderspec, true, errorExit);
  folderObj = FSO.GetFolder(folderspec);
  filesEnum = new Enumerator(folderObj.files);
	if(filesEnum.atEnd())
	{
		if(errorExit)
		{
			ErrorExitWithMail( "ERROR! A major working path does not have any files inside it: " + folderspec + ".\n" , a_failRecipient , a_logFiles );
			WScript.Quit(1);
		}
		else
	  	return(false);
	}
	else
		return(true);
}

//+++++++++++++++++++++++++Will check if link exists and quit if not+++++++++++++++++++++++++
function CheckLink(path,isFolder, errorExit)
{
	if(isFolder)
	{
		if(!FSO.FolderExists(path))
		{
			if(errorExit)
			{
				ErrorExitWithMail( "ERROR! A major working path does not exists: " + path + ".\n" , a_failRecipient , a_logFiles );
				WScript.Quit(1);
			}
			else
				return false;
		}
		else 
			return true;
	}
	else
	{
		if(!FSO.FileExists(path))
		{
			if(errorExit)
			{
				ErrorExitWithMail( "ERROR! A major working file does not exists: " + path + ".\n" , a_failRecipient , a_logFiles );
				WScript.Quit(1);
			}
			else
				return false;
		}
		else 
			return true;
	}
}

//+++++++++++++++++++++++++Log,Copy and catch error+++++++++++++++++++++++++
function fsoCopy(source, destination , isDir, overwrite )
{
	if(!DEBUG)
	{
		Log("Copying: " + source + " to: " + destination );		
		try
		{	
			if(isDir)
				FSO.CopyFolder( source, destination, overwrite );
			else
				FSO.CopyFile( source, destination, overwrite );
				
			return true;
		}
		catch(e)
		{
			Log("Error: Couldn't copy: " + source + " to: " + destination + " because of: " + e.description);
			return false;
		}
	}
	else
	{
		Log("(Copying_test_logging: " + source + " to: " + destination );	
		return true;
	}
}

// Execute something
function Execute(cmdLine, catchErrors)
{
	Log("Executing: " + cmdLine);
	if(catchErrors)
		Log("With catching errors");
	else
		Log("Without catching errors");
	
	if(!DEBUG)
	{
		var oExec = WSHSHELL.Exec( cmdLine );
		
		while (oExec.Status == 0)
	  {
	  	// Sleep one second
	    WScript.Sleep(1000);
	  }
			  
		if(catchErrors)
		{
			var regExpError = /<ReturnCode:(\d)>/i;
			var stdErr = oExec.StdErr.ReadAll();
			if(stdErr.search(regExpError) != "-1")
			{
				var customReturnCode = RegExp.$1;
				if(customReturnCode == 0)
					return(true);
				else
					return(false);
			}
					
			var exitCode = oExec.ExitCode;
			if(exitCode == 0)
					return(true);
				else
					return(false);
		}
	}
	
	return(true);
}

//+++++++++++++++++++++++++Get converted date+++++++++++++++++++++++++
function GetConvertedDate()
{
	var jscriptDate = new Date();
	var convertedDate = jscriptDate.getYear()+"-"+(jscriptDate.getMonth()+1)+"-"+jscriptDate.getDate()+" "+jscriptDate.getHours()+":"+jscriptDate.getMinutes()+":"+jscriptDate.getSeconds();
	return(convertedDate);
}

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


//++++++++++++++++++++++Prevents this script to be started without a dos window+++++++++++++
function NoGUIApplication()
{  
	var scriptHost = WScript.FullName;
	var regExp = /cscript.exe/i
	if(scriptHost.search(regExp) == "-1")
	{
		WSHSHELL.Popup( "Invalid script Host - This application is made for commandline use only\n"+
	                    "Please start the script with \"cscript\" before the scriptname e.g.: cscript <ScriptName>\n",
	                    0,"Error",0);
	                    
	 	ErrorExit("Script was started with the wrong host");
	}
}


//+++++++++++++++++++++++++Function for writing to the log+++++++++++++++++++++++++
function Log(str) 
{
	var couldNotLog = "   (Could not write this line to log file)";
	
	if(openedLogFile != null)
	{
		try
		{
			openedLogFile.WriteLine( str );
		}
		catch(e)
		{
			str += couldNotLog;
		}
	}
	else 
		str += couldNotLog;
		
	WScript.Echo( str );
}

//+++++++++++++++++++++++++Mainfunction for sending mails+++++++++++++++++++++++++
function SendMail( recipient,msg,subject,attachmentsArray,importance, closeLogFile)
{
	Log( "Sending Mail to "+recipient+", With subject: "+subject );	
	
	if(closeLogFile)
		openedLogFile.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") = "mail2.INTERN.CRYTEK.DE";
	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] );
		}
	}

	cdoMessage.Send();
	cdoConfig = null;
	cdoMessage = null;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

}
catch(e)
{
	WScript.Echo("error: "+e);
	WScript.Echo("error number: "+e.number & 0xFFFF);
	WScript.Echo("error description: "+e.description);
	errorLogFile = FSO.CreateTextFile(  scriptLocation + "\\"  + scriptName + "_ERROR.log",true );
	errorLogFile.WriteLine("error: "+e);
	errorLogFile.WriteLine("error number: "+e.number & 0xFFFF);
	errorLogFile.WriteLine("error message: "+e.message);
	errorLogFile.WriteLine("error description: "+e.description);
	errorLogFile.WriteLine("Trying to send out a mail with the log as last step");
	errorLogFile.Close();
	a_logFiles.push(scriptLocation + "\\"+ scriptName + "_ERROR.log");
	SendMail( a_failRecipient,"Shader cache compilation failed because of an error in build script!","Shader cache compilation failed",a_logFiles,2 ,openedLogFile);
	ErrorExit("Internal problem in script");
}