/*!
**********************************************************************
@file systemCheck.js

Copyright 2003-2006 Adobe Systems Incorporated.                     
All Rights Reserved.                                                
                                                                    
NOTICE: All information contained herein is the property of Adobe   
Systems Incorporated.                                                                                                                    

***********************************************************************
*/

/**
Implement a Wizard Page in three easy steps:

 1. Subclass WizardPage
 2. Extend with page-specific method overrides
 3. Return the WizardPage subclass name

*/


/***********************************************************************
1. Subclass WizardPage
*/
function systemCheck_wp(inController)
{
	this.SetController(inController);
	this.param = {
		k_systemCheckRunningAppsListId: 'errorCheck',
		hasPayloadError: false,
		hasWarningAppConflict: false,
		hasBlockingAppConflict: false,
		hasWarningConflictingProcess: false,
		hasBlockingConflictingProcess: false,
		hasSystemRequirementError: false,
		hasSystemRequirementHardStop: false,
		hasSystemRequirementHardStopConflicts: false,
		hasPayloadPolicyError: false,
		hasPayloadPolicyErrorHardStop: false
	};
}

systemCheck_wp.prototype = new WizardPage("systemCheck");

/***********************************************************************
2. Extend/override with page-specific logic
*/

systemCheck_wp.prototype.GetPageTitleDisplayName = function()
{
	return this.localization.GetString("locTitle", "System Check");
}

systemCheck_wp.prototype.GetNavDisplayName = function()
{
	return this.localization.GetString("locNavTitle", "System Check");
}

/**
Creates a series of blocking and warning apps in an array (blocking[], warning[]) if they exist.
*/
systemCheck_wp.prototype.updateRunningAppsList = function(inSession)
{
	var blockingAppNames = new Array();
	var warningAppNames = new Array();
	var blockingAppNamesNoDupes = new Array();
	var warningAppNamesNoDupes = new Array();
	
	try
	{
		var allRunningApps = inSession.GetRunningApplications();
		var currentPayload = null;
		var setupCount = 0;
		
		// For each payload...
		for (var p in inSession.payloadMap)
		{
			// If it has conflicting processes listed at all...
			currentPayload = inSession.payloadMap[p];
			
			if (currentPayload.ConflictingProcesses && currentPayload.ConflictingProcesses[0])
			{
				inSession.LogInfo("The List of conflicting process for the paylaod with name - " + currentPayload.ProductName + "-are: " );
				// See if the running application matches for Blocking or Warning
				for (var regexIndex = 0; regexIndex < currentPayload.ConflictingProcesses.length; ++regexIndex) 
				{	
					inSession.LogInfo(currentPayload.ConflictingProcesses[regexIndex]["pattern"]);
					// For each running application...
					for (var appIndex=0; appIndex < allRunningApps.Applications.length; ++appIndex)
					{
						// Check to see if the application name matches the known pattern
						if (allRunningApps.Applications[appIndex].osName.match(currentPayload.ConflictingProcesses[regexIndex]["pattern"]))
						{
							// If it is blocking, not as an error
							if ("1" == currentPayload.ConflictingProcesses[regexIndex]["blocking"]) {
								blockingAppNames.push(allRunningApps.Applications[appIndex].friendlyName);
							} 
							else 
							{
								warningAppNames.push(allRunningApps.Applications[appIndex].friendlyName);
							}
						}
					}
				}
			}
		}		
		
		// Check for dupe of setup, out of band and regardless of the above search
		/*
		for (var appIndex=0; appIndex < allRunningApps.Applications.length; ++appIndex)
		{		
			if (allRunningApps.Applications[appIndex].friendlyName == "Setup" || allRunningApps.Applications[appIndex].friendlyName == "Adobe Setup" )
				setupCount++;
		}

		
		if (setupCount > 1)
			blockingAppNames.push("Setup"); 
		*/
			
		// Remove duplicate entries in both lists	
		var blockingAssoc = new Object;
		for (var i = 0; i < blockingAppNames.length; i++)   
			blockingAssoc[blockingAppNames[i]] = blockingAppNames[i];

		for (var k in blockingAssoc) 
		   blockingAppNamesNoDupes.push(blockingAssoc[k]);
		
		var warningAssoc = new Object;
		for (var i = 0; i < warningAppNames.length; i++)   
			warningAssoc[warningAppNames[i]] = warningAppNames[i];

		for (var k in warningAssoc) 
		   warningAppNamesNoDupes.push(warningAssoc[k]);	
	
	}
	catch(e)
	{
		inSession.LogWarning("Exception while testing application run states.");
	}
	
	return new Array(blockingAppNamesNoDupes, warningAppNamesNoDupes);
}


systemCheck_wp.prototype._stringForUI = function(inStringOrObj)
{
	var result = inStringOrObj;
	if (inStringOrObj.Translate && typeof inStringOrObj.Translate == "function")
	{
		result = inStringOrObj.Translate(this.wizardControl.session.localization);
	}
	return result;
}


// used by silent...
systemCheck_wp.prototype._stringForLog = function(inStringOrObj)
{
	var result = inStringOrObj;
	if (inStringOrObj.Translate && typeof inStringOrObj.Translate == "function")
	{
		result = inStringOrObj.Translate(gSession.localization, "en_US"); //FIXME gSession
	}
	return result;
}

/**
Fabricate the DOM for a system check error/warning
*/
systemCheck_wp.prototype._createErrorListElement = function(inClass, inText)
{
	var session = this.wizardControl.session;

	var isArray = function(inObj)
	{
		return typeof inObj == "object" && inObj.length && inObj.concat && inObj.join;
	};

	var isLocalizedString = function(inObj)
	{
		return typeof inObj == "object" && inObj.Translate && typeof inObj.Translate == "function";
	};

	var resolveString = function(inObj, forLog)
	{
		if (isLocalizedString(inObj))
		{
			if (forLog)
				return inObj.Translate(session.localization, 'en_US');
			else
				return inObj.Translate(session.localization);
		}
		return inObj;
	};

	var contentList = inText;
	if (!isArray(inText))
		contentList = new Array(inText);
		
	var errorDiv = document.createElement("div");
	if (inClass)
		errorDiv.className = inClass;

	for (var i in contentList)
	{
		var item = contentList[i];
		if (isArray(item))
		{
			if (item.length > 0)
			{
				var listElement = document.createElement("ul");
				for (var si in item)
				{
					var li = document.createElement("li");
					li.appendChild(document.createTextNode(resolveString(item[si])));
					listElement.appendChild(li);
					if (inClass == "alertCritical")
						session.LogError(" - " + resolveString(item[si], true));
					else
						session.LogWarning(" - " + resolveString(item[si], true));
				}
				errorDiv.appendChild(listElement);
			}
		}
		else
		{
			var p = document.createElement("p");
			p.appendChild(document.createTextNode(resolveString(item)));
			errorDiv.appendChild(p);
			if (inClass == "alertCritical")
				session.LogError(resolveString(item, true));
			else
				session.LogWarning(resolveString(item, true));
		}
	}

	return errorDiv;
}




/**
Create and displayd an ErrorListElement.  Arguments are the same as for createErrorListElement().
Errors are identified by a string ID that can be used to remove the error.  If you add an error
that already exists, the existing one will be replaced with the new content.

inLabel, inOptErrorEnd and the members of inOptErrorList may be either strings or LocalizedString objects.
LocalizedString objects are recommended to ensure that the UI gets the proper localization and the
log file gets en_US without having to think about it when you generate the string.
*/
systemCheck_wp.prototype.AddError = function(inId, inClass, inText)
{
	if (!this._errorDOMs)
	{
		this._errorDOMs = new Object();
	}

	var newDom = this._createErrorListElement(inClass, inText);
	
	if (this.contentBox && newDom)
	{
		var oldDom = this._errorDOMs[inId];
		if (oldDom && oldDom.parentNode == this.contentBox)
		{
			this.contentBox.replaceChild(newDom, oldDom);
			oldDom = null;
		}
		else
		{
			this.contentBox.appendChild(newDom);
		}
	}

	this._errorDOMs[inId] = newDom;
}


/**
Remove an error added with AddError().
*/
systemCheck_wp.prototype.RemoveError = function(inId)
{
	if (this._errorDOMs && this._errorDOMs[inId] && this._errorDOMs[inId].parentNode)
	{
		this._errorDOMs[inId].parentNode.removeChild(this._errorDOMs[inId]);
		delete this._errorDOMs[inId];
	}
}


/**
Check for system requirements
*/
systemCheck_wp.prototype.runSystemRequirementsCheck = function(inSession, inOptPayloadList)
{
	// Early return if we are in maintenance mode
	if (inSession.IsMaintenanceMode())
	{
		return new Array();	
	}
	return RunSystemRequirementsCheck(inSession, inOptPayloadList);
}


/**
Check for manifest errors and warnings
*/
systemCheck_wp.prototype.checkManifestErrors = function(inSession)
{
	var payloadWithErrors = null;
	var ecount = 0;
	
	for (var p in inSession.payloadMap)
	{
		payloadWithErrors  = inSession.payloadMap[p];
		if (1 == payloadWithErrors.hasManifestErrors)
			ecount++;
	}
	
	return ecount;
}


/**
Check payload policy for co-existence violations.
*/
systemCheck_wp.prototype.checkPayloadPolicy = function()
{
	var result = true;

	if (!this.wizardControl.session.IsMaintenanceMode())
	{
		this.wizardControl.session.PayloadPolicyInit();
		var driver = this.wizardControl.session.GetDriverPayload();
		if (driver)
		{
			driver = this.wizardControl.session.sessionPayloads[driver.AdobeCode];
			if (driver)
			{
				var ppo = driver.policyNode;
				if (ppo)
				{
					// Generic driver constraint handle.  The driver normally has a Intrinsic.Driver constraint.
					// If it doesn't, that means a dependency has some other intrinsic constraint that override's
					// the driver's normal constraint.
					if (!(ppo.GetConstraintClass() == kPolicyClass.Intrinsic && ppo.GetConstraintCode() == kIntrinsicPolicyCode.Driver))
					{
						this.param.hasPayloadPolicyError = true;
						this.param.hasPayloadPolicyErrorHardStop = (ppo.GetAction() == kPolicyActionNo);

						// Enumerate the details if available, but skip over Intrinsic.Sysreq as we
						// present an aggregate of those elsewhere.
						var detailCount = 0;
						for (var error in ppo._message.detail)
						{
							var detail = ppo._message.detail[error];
							if (!(detail.dependentConstraintClass == kPolicyClass.Intrinsic && detail.dependentConstraintCode == kIntrinsicPolicyCode.Sysreq) && detail.text)
							{
								this.AddError("policyDriver." + error, detail.className, detail.text);
								detailCount++;
							}
						}

						// If no details, at least try for a note
						var blockingListErrors = new Array();
						if (detailCount == 0 && ppo._message.note && !(ppo._message.dependentConstraintClass == kPolicyClass.Intrinsic && ppo._message.dependentConstraintCode == kIntrinsicPolicyCode.Sysreq))
						{
							blockingListErrors.push(ppo._message.note);
							this.AddError("policyDriver", this.param.hasPayloadPolicyErrorHardStop ? "alertCritical" : "alertWarning",
							 	[new LocalizedString("locDriverCannotInstall", "[productName] cannot be installed because:"),
							 	[ppo._message.note]]);
						}

						result = false;
					}

					// But not all business rules are encoded as constraints, so handle those here.
					else
					{
					}
				}
			}
		}
	}
	return result;
}


/**
Rebuild the conflicting processes list information
*/
systemCheck_wp.prototype.updateConflictingProcesses = function()
{
	var runningAppWarningsAndErrors = this.updateRunningAppsList(this.wizardControl.session);
	this.param.hasBlockingConflictingProcess  = false;
	this.param.hasWarningConflictingProcess = false;
	
	if (runningAppWarningsAndErrors[0] || runningAppWarningsAndErrors[1]) 
	{
		this.param.hasBlockingConflictingProcess  = runningAppWarningsAndErrors[0].length > 0;
		this.param.hasWarningConflictingProcess = runningAppWarningsAndErrors[1].length > 0;
		
		// Show contents of running apps list
		this._refreshConflictingProcessesAlert(runningAppWarningsAndErrors);
	}
}


/**
Recreate the conflicting processes error alert
*/
systemCheck_wp.prototype._refreshConflictingProcessesAlert = function(inArrBlockingAndWarningsArray)
{
	if (inArrBlockingAndWarningsArray[0].length > 0)
	{
		this.AddError("blockingProcesses", "alertCritical",
			[new LocalizedString("systemPageAppCloseError"),
			inArrBlockingAndWarningsArray[0],
			new LocalizedString("systemPageAppCloseError2")]);
	}
	else
	{
		this.RemoveError("blockingProcesses");
	}

	if (inArrBlockingAndWarningsArray[1].length > 0)
	{
		this.AddError("warningProcesses", "alertWarning",
			[new LocalizedString("systemPageAppCloseWarning"),
			inArrBlockingAndWarningsArray[1]]);
	}
	else
	{
		this.RemoveError("warningProcesses");
	}
}


/**
Check for errors and warnings once on page load.
*/
systemCheck_wp.prototype.GUISystemCheck = function()
{
    var fatalErrorList = new Array();
    
	// Check and report manifetst errors ================================================
	var manifestErrorCount = this.checkManifestErrors(this.wizardControl.session);
	
	if (manifestErrorCount > 0)
	{
		this.param.hasPayloadError = true;
	}
	
	if (this.param.hasPayloadError == true)
	{			
		// Not localized because this situation should never make it past QE.
		var blockingListErrors = new Array();
		blockingListErrors[0] = "Number of payloads with errors: " + manifestErrorCount;
		this.AddError("manifestError", "alertCritical", ["Manifest errors were found.", blockingListErrors]);
	} 
	else 
	{
		var divOuterID = this.getElementById("content");
		if (divOuterID && this.getElementById("manifestError"))
		{
			divOuterID.removeChild(this.getElementById("manifestError"));
		}	
	}

	// Check for and report fatal pre-installation errors ================================================
	if (this.wizardControl.session.sessionErrorMessages && this.wizardControl.session.sessionErrorMessages[0])
	{
		this.param.hasSystemRequirementHardStopConflicts = true;
		this.param.hasBlockingAppConflict = true;
		
		for (var i = 0; i < this.wizardControl.session.sessionErrorMessages.length; i++)
		{
			// TODO: have sessionErrorMessages be LocalizedString objects to begin with.
			fatalErrorList.push(new LocalizedString(this.wizardControl.session.sessionErrorMessages[i][0], this.wizardControl.session.sessionErrorMessages[i][1]));
		}

		this.AddError("sessionErrors", "alertCritical",
			[new LocalizedString("systemPageSessionErrorStart", "Critical errors were found in setup:"),
			fatalErrorList,
			new LocalizedString("systemPageSessionErrorEnd", "Please see the log file for details.")]);
		return;
	} 
	
	// Check and report on system requirements ================================================

	// Assemble a list of driver & dependencies
	var hiddenPayloads = new Array();
	var driver = this.wizardControl.session.GetDriverPayload();
	if (driver)
	{
		driver = this.wizardControl.session.sessionPayloads[driver.AdobeCode];
		if (driver)
		{
			var required = driver.GetDependentsArray();
			for (var p in required)
			{
				hiddenPayloads.push(required[p]);
			}
			hiddenPayloads.push(driver);
		}
	}
	
	// If we have some, do a requriements check on just that set.
	if (hiddenPayloads.length > 0)
	{
		var systemRequirementResults = this.runSystemRequirementsCheck(this.wizardControl.session, hiddenPayloads);

		if (systemRequirementResults[0] || systemRequirementResults[1] || systemRequirementResults[2])
		{
			this.param.hasSystemRequirementError = ((systemRequirementResults[0].length > 0)
			 										|| (systemRequirementResults[1].length > 0)
													|| (systemRequirementResults[2].length > 0)) ? true : false;
		
			var hasHardStop = false;
		
			// Errors showing requirements
			if (systemRequirementResults[0])
			{
				if (systemRequirementResults[0].length > 0)
				{
					hasHardStop = true;
					this.AddError("sysreqError", "alertCritical",
					 	[new LocalizedString("systemPageSysReqErrorStart"),
					 	systemRequirementResults[0],
					 	new LocalizedString("systemPageSysReqErrorEnd")]);
				}
			}
		
			// Errors showing problems with the system
			if (systemRequirementResults[2])
			{
				if (systemRequirementResults[2].length > 0)
				{
					hasHardStop = true;
					this.AddError("sysreqExclude", "alertCritical",
						[new LocalizedString("systemPageSysReqExcludedErrorStart"),
						systemRequirementResults[2],
						new LocalizedString("systemPageSysReqExcludedErrorEnd")]);
				}
			}
		
			// Warnings showing requirements
			if (systemRequirementResults[1])
			{
				if (systemRequirementResults[1].length > 0)
				{
					this.AddError("sysreqWarning", "alertWarning",
					 	[new LocalizedString("systemPageSysReqWarningStart"),
					 	systemRequirementResults[1],
					 	new LocalizedString("systemPageSysReqWarningEnd")]);
				}
			}
		
			// Set hasSystemRequirementHardStop to match whether or not there were any hard stops
			this.param.hasSystemRequirementHardStop = hasHardStop;
			//return;
		}
	}

	// Check for and report fatal payload policy errors ================================================
	if (!this.checkPayloadPolicy())
	{
		// And short circuit
		return;
	}

	// Check and report running applications ================================================
	this.updateConflictingProcesses();
	
}


/**
Setup things for GUI presentation
*/
systemCheck_wp.prototype.onResourcesLoaded = function()
{
	this.contentBox = this.getElementById("errorCheck");
	RemoveAllChildren(this.contentBox);
}


/**
Only participate if there are errors or warnings to show.
Otherwise, bypass.
*/
systemCheck_wp.prototype.onSwitchTo = function()
{		
	RemoveAllChildren(this.contentBox);
	this.GUISystemCheck();
	
	return (this.param.hasWarningAppConflict
			|| this.param.hasBlockingAppConflict
			|| this.param.hasWarningConflictingProcess
			|| this.param.hasBlockingConflictingProcess
			|| this.param.hasPayloadError
			|| this.param.hasSystemRequirementError
			|| this.param.hasPayloadPolicyError
			|| this.param.hasPayloadPolicyErrorHardStop);
}

systemCheck_wp.prototype.onNext = function()
{
	// If we're doing this because there's a blocking conflict, then run that test now
	if (this.param.hasBlockingConflictingProcess)
	{
		this.updateConflictingProcesses();
		if (!this.param.hasBlockingConflictingProcess)
		{
			return true;
		}
	}
	else
	{
		this.updateRunningAppsList(this.wizardControl.session);
		this.checkManifestErrors(this.wizardControl.session);
		this.runSystemRequirementsCheck(this.wizardControl.session);
	}
		
	return (!this.param.hasBlockingAppConflict && !this.param.hasBlockingConflictingProcess);
}

systemCheck_wp.prototype.RunCustomActions = function(inSession)
{
    for(var adobecode in inSession.sessionPayloads)
    {
        var payload = inSession.sessionPayloads[adobecode];
        inSession.LogInfo("Calling pre-system check custom code for payload " + adobecode );
        var caRet = inSession.CallCustomActionCode(adobecode, payload.GetInstallerAction(), payload.GetPayloadOverrideProperties(inSession.properties), "sys", 0);
        inSession.LogInfo("Pre-system check custom code for payload " + adobecode + " returned " + caRet);
    }    
}


systemCheck_wp.prototype.onShow = function()
{	
	this.setWindowTitle();	
	this.wizardControl.session.UISetCloseBoxEnabled(1);
	
	if (this.param.hasSystemRequirementHardStop || this.param.hasSystemRequirementHardStopConflicts || this.param.hasPayloadPolicyErrorHardStop)
	{
		this.wizardControl.nextButton.Disable();
		this.wizardControl.nextButton.Hide();
		this.wizardControl.quitButton.button.focus();
		this.wizardControl.SetFocus(this.wizardControl.quitButton.button);
	} 
	else if (this.param.hasBlockingConflictingProcess)
	{
		this.wizardControl.nextButton.SetLabel(this.localization.GetString("systemPageRefreshButtonLabel", "Refresh"));	
		this.wizardControl.nextButton.button.focus();
		this.wizardControl.SetFocus(this.wizardControl.nextButton.button);
	}
	else
	{
		this.wizardControl.nextButton.button.focus();
		this.wizardControl.SetFocus(this.wizardControl.nextButton.button);
	}	
}


systemCheck_wp.prototype.onCancel = function()
{	
	this.wizardControl.session.UIExitDialog();
}

/***********************************************************************
3. Last, and very important, return the name of the class.
*/
"systemCheck_wp";
