//---This script will copy shaders to the destribution servers---
//---Written by Denis Barth(Denis@crytek.de)

//---'S' for Source and 'D' for Destination

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  + "\\DistributeShaders.log";
var openedLogFile = FSO.CreateTextFile( logFile ,true );


// Defintion of empty vars which should have a extern given value
var spcDirS  = "";
var shaderCompileStartDateAsString  = "";

// Catch spc dir
if (WScript.Arguments.Named.Exists("SPC"))
{
	spcDirS = WScript.Arguments.Named.Item("SPC");
}
else
{
	WScript.Echo("No SPC directory was given!");
	WScript.Quit(1);
}

// Catch start time
if (WScript.Arguments.Named.Exists("StartTime"))
	shaderCompileStartDateAsString = WScript.Arguments.Named.Item("StartTime");
else
{
	Log("No shader compile start date was given!");
	WScript.Quit(1);
}


var spcShaderCacheS = spcDirS + "\\ShaderCache";
var shaderCachePakS = spcShaderCacheS + "\\ShaderCache.pak";
var spcBuildS = spcDirS + "\\SPC";
var buildName = "";
var buildNameS = spcBuildS + "\\buildname.txt";
var shadersPakS = spcBuildS + "\\Game\\shaders.pak";
var shaderListS = spcDirS + "\\shaderlist.txt";

var destributionServerD = "\\\\Mastercd\\ShaderCache\\version_1";
var procedurallyBuildsD = "\\\\storage\\Builds\\procedurally_generated_builds"; 
var buildD = "";
var gameBuildD = "";

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

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



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

Log("Shader distribution started");
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");

var startScriptTime = new Date();
Log("Started at " + startScriptTime.toString());

Log("spcDir is: " + spcDirS);
Log("shader compile start date as string is: " + shaderCompileStartDateAsString);

CheckLink(spcDirS, true, true);
CheckLink(shaderCachePakS, false, true);
CheckLink(buildNameS, false, true);

buildName = ReadFile(buildNameS, false);

buildD = procedurallyBuildsD + "\\" + buildName;

gameBuildD = buildD + "\\Game";

if(!FSO.FolderExists(buildD))
	ErrorExitWithMail("The destination build folder for shaders doesn't exist: " + buildD, a_failRecipient , a_logFiles );

var updatedShadersInBuild = true;
var clearedServerShaders = true;
var updatedShadersOnServer = true;
var updatedShaderBuildNumberOnServer = true;
var updatedShaderBuildNumberInBuild = true;


// Copy the shaders into the build
if(!fsoCopy(shaderCachePakS, gameBuildD + "\\ShaderCache.pak" , false, true ))
	updatedShadersInBuild = false;
	
if(!fsoCopy(buildNameS, buildD + "\\ShaderCacheBuildNumber.txt" , false, true ))
	updatedShaderBuildNumberInBuild = false;
	


// Deletes only the content of the shared server shader cache direcotry
if(!DeleteSafely(destributionServerD + "\\ShaderCache.pak",true))
	clearedServerShaders = false;

// Copy over shaders	
if(!fsoCopy(shaderCachePakS, destributionServerD + "\\ShaderCache.pak" , false, true ))
	updatedShadersOnServer = false;
	
if(!fsoCopy(shadersPakS, destributionServerD + "\\Shaders.pak" , false, true ))
	updatedShadersOnServer = false;
	
if(!fsoCopy(buildNameS, destributionServerD + "\\ShaderCacheBuildNumber.txt" , false, true ))
	updatedShaderBuildNumberOnServer = false;
	

// Deactivated
//var backuped = BackupShaderList();

SendSuccessMail();

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




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


//++++++++++++++++++++++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");
	}
}



//+++++++++++++++++++++++++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;
	}
}



//+++++++++++++++++++++++++Will read in a hole file and return an empty string if something goes wrong+++++++++++++++++++++++++
function ReadFile(file, create)
{ 
	Log("Reading file: " + file);
	var str = "";
	if(FSO.FileExists(file))
	{
  	var fileToRead = FSO.OpenTextFile(file, 1, create);
  	if(!fileToRead.AtEndOfstream)
  		str = fileToRead.ReadAll();
  	
  	fileToRead.Close();
  }
  return str;
}


//+++++++++++++++++++++++++Log,Create and catch error+++++++++++++++++++++++++
function fsoCreateFolder(path)
{
	if(!DEBUG)
	{
		Log("Creating: " + path );		
		try
		{	
			FSO.CreateFolder( path );
			return true;
		}
		catch(e)
		{
			Log("Error: Couldn't create: " + path + " because of: " + e.description);
			return false;
		}
	}
	else
	{
		Log("(Creatings_test_logging: " + path );	
		return true;
	}	
}

//+++++++++++++++++++++++++Delete,unlock and check if it was succesful+++++++++++++++++++++++++
function DeleteSafely(givenPath,isFile) 
{
	var regExpStar = /(.+)\\.*\*+/i;
	
	if(givenPath.search(regExpStar) != "-1")
	{
		var rootPath = RegExp.$1;
		if( !FSO.FolderExists( rootPath ))
		{
			Log("Could not find root path for deleting:"+rootPath);
			return true;
		}
	}		
	
	if(isFile==true)
	{
		if (FSO.FileExists( givenPath ) || rootPath != "")
		{
			try
			{
				FSO.DeleteFile( givenPath );
				Log("Successful deleted file "+givenPath );
				return true;
			}
			catch(e)
			{
				Log(e.description + " on: " + givenPath);
				return false;
			}
		}
		else
		{
				Log("Could not find file for deleting:"+givenPath);
				return true;
		}
	}
	else if(isFile==false)
	{
		if (FSO.FolderExists( givenPath ) || rootPath != "")
		{
			try
			{
				FSO.DeleteFolder( givenPath, true );
				Log("Successful deleted folder "+givenPath );
				return true;
			}
			catch(e)
			{
				Log(e.description + " on: " + givenPath);
				return false;
			}
		}
		else
		{
				Log("Could not find folder for deleting:"+givenPath);
				return true;
		}
	}
	else
		throw new Error(0,"Function \"DeleteSafely\"Could not figure out what to do. Pls check the arguments")
}

//+++++++++++++++++++++++++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;
	}
}


//+++++++++++++++++++++++++Only temporary for tracking a bug if accurs+++++++++++++++++++++++++
function BackupShaderList() 
{
	Log("Backing up the shaderlist");
	var backupDir = "C:\\ShaderListBackup";
	fsoCreateFolder(backupDir);
	
	var tempDate = new Date();
	var shaderListBackupHelpDate = tempDate.getDate() + "d_" + (tempDate.getMonth() + 1) + "m_" + tempDate.getYear() + "y_" + tempDate.getHours() + "h_" + tempDate.getMinutes() + "m_" + tempDate.getSeconds() + "s";
	
	var returnState = fsoCopy(shaderListS, backupDir + "\\ShaderList_" + buildName + "_" + shaderListBackupHelpDate + ".txt" , false, true );
		
		
	// Get the available space
	var driveName = FSO.GetdriveName("C:");
	var oDrive = FSO.GetDrive(driveName);
	var freeMb = Math.round(oDrive.FreeSpace/1024/1024);

	// Check if one gig is free
	if(freeMb < 500)
	{
		var mailMsg = "Only " + freeMb + "Mb left on the backup drive of the shaderlist";
		Log(mailMsg);
		var mailSub = "Shader cache distribution INFO";
		SendMail( a_failRecipient, mailMsg, mailSub , a_emptyFile, 1, false );
	}
	
	return returnState;
}


//+++++++++++++++++++++++++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 );
}

//+++++++++++++++++++++++++Logs an error and send a mail if something goes wrong+++++++++++++++++++++++++
function ErrorExitWithMail( errStr , a_errRecipients , a_errLogs )
{
	var errorString = errStr;
	Log ( errorString );
	WSHSHELL.LogEvent( 1,errorString );
	SendMail( a_errRecipients,errorString,"Shader cache distribution failed!",a_errLogs,1 , true );
	ErrorExit(errStr);
}

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

//+++++++++++++++++++++++++Summery of job execution+++++++++++++++++++++++++
function SendSuccessMail()
{	
	var a_mailRecipient = a_sucRecipient;
	
	var mailSub = "Shader cache distribution";

	if(updatedShadersInBuild && updatedShadersOnServer)
	{
		mailSub += " was successful";		
	}
	else if(!updatedShadersInBuild && !updatedShadersOnServer)
	{
		mailSub += " failed";
		a_mailRecipient = a_failRecipient;
	}
	else
	{
		mailSub += " was partially successful";
		a_mailRecipient = a_failRecipient;
	}
	
	mailSub += " with build \"" + buildName + "\"";
	
	var mailMsg = mailSub;
	
	if(!updatedShadersInBuild)
	{
		Log("The update of the shaders in the build failed");
		mailMsg += "\n\n The update of the shaders in the build failed";
	}
	
	if(!updatedShadersOnServer)
	{
		Log("The update of the shaders on the server failed");
		mailMsg += "\n\n The update of the shaders on the server failed";
	}
	
	if(!clearedServerShaders)
	{
		a_mailRecipient = a_failRecipient;
		Log("Could not cleanup the sever shader directory");
		mailMsg += "\n\n Could not cleanup the sever shader directory";
	}

	if(!updatedShaderBuildNumberOnServer || !updatedShaderBuildNumberInBuild)
	{
		a_mailRecipient = a_failRecipient;
		if(!updatedShaderBuildNumberOnServer)
		{
			Log("Could not update the ShaderBuildNumber on the Server");	
			mailMsg += "\n\n WARNING! Could not update the ShaderBuildNumber on the Server";
		}
		if(!updatedShaderBuildNumberInBuild)
		{
			Log("Could not update the ShaderBuildNumber in the build " + buildName);	
			mailMsg += "\n\n Could not update the ShaderBuildNumber in the build " + buildName;
		}
	}

// Deactivated
//	if(!backuped)
//	{
//		a_mailRecipient = a_failRecipient;
//		Log("Could not backup the shadlerlist.txt");	
//		mailMsg += "Could not backup the shadlerlist.txt";
//	}
	
	var shaderCompileStartDate = DateObjectFromDateString(shaderCompileStartDateAsString);
		
	if(shaderCompileStartDate != null)
	{
		var shaderCompileEndDate = new Date();
		mailMsg += "\n\nThe shader compilation was started at " + shaderCompileStartDateAsString;
		mailMsg += "\nTotal shader compilation time: " + GetTimeDiff(shaderCompileStartDate,shaderCompileEndDate);
	}
		
	SendMail( a_mailRecipient, mailMsg, mailSub , a_logFiles, 1, true );
}


//+++++++++++++++++++++++++Mainfunction for sending mails+++++++++++++++++++++++++
function DateObjectFromDateString( dateString)
{
	var regExp = /(\d+)-(\d+)-(\d+)\s(\d+):(\d+):(\d+)$/i;
	if(dateString.search(regExp) != "-1")
	{
		var year = RegExp.$1;
		var month = RegExp.$2;
		var day = RegExp.$3;
		var hours = RegExp.$4;
		var minutes = RegExp.$5;
		var seconds = RegExp.$6;
		var dateObj = new Date(year, month - 1, day, hours, minutes, seconds) ;
	}
	else
	{
		var dateObj = null;
	 	Log("Could not convert date string to date object! string is: "+dateString);
	}
	
	return dateObj;
}

//+++++++++++++++++++++++++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;
}


//+++++++++++++++++++++++++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 distribution failed because of an error in the script!","Shader distribution failed",a_logFiles,2 ,openedLogFile);
	WScript.StdErr.Write("<ReturnCode:1>");
	WScript.Quit(1);
}