/*!
**********************************************************************
@file InstallerPayload.js

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

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

/**
Creates a new InstallerPayload instance. This object must
be further initialized with calls to SetSessionData() and
SetCAPSData(). Finally, it should be annealed with initialization
calls to AddConflictingPayload(), AddPayloadToUpgradeFrom(),
and AddPayloadToUpgradeTo() and additional graph edge creation
methods to fully form the payload graph.
*/
function InstallerPayload(inAdobeCode)
{
	this._adobeCode = inAdobeCode;
	
	this._conflictingPayloads = new Object;
	this._payloadsUpgradingThis = new Object;
	this._payloadsThisUpgrades = new Object;
	this._collectionRecords = new Object;
	this._operationResult = null;
	this._sessionData = null;
	this._installedStatus = null;
	
	/** 
	Listing of the payload dependents by category that failed to install
	*/
	this._criticalPayloadFailures = null;
	this._requiredPayloadFailures = null;
	this._recommendedPayloadFailures = null;

	// Does at least one payload recommend us. If this is true, we must always be visible on UI
	this.isRecommended = false;
	
	/**
	Dependencies in the form of product family, product name,
	optional product version that are not explicitly resolved
	to specific payloads. Instead, they are bound during session
	creation to sets of payloads. Prior to complete session
	initialization, this member gets initialized by
	GetUnresolvedDependencies() to the dependency data
	coming from the payload's proxy file with an additional
	owningPayload property that refers to this payload.  After
	initialization by the session done with AddDependencySatisfier()
	these dependencies will also contain a list of payloads
	that could satisfy the dependency in a property map
	of AdobeCode to InstallerPayload instances called
	satisfyingPayloads.
	*/
	this._unresolvedDependencies = null;
	
	/**
	Array of dependencies that this payload would satisfy if it
	were installed. Dependencies are added to this list when
	this payload is a parameter to AddDependencySatisfier() during
	session initialization.
	*/
	this.satisfiesDependencies = new Array;
	
	/**
	Payload selection policy
	*/
	this.policyNode = null;
	
	/**
	A place for methods to stash stuff.
	*/
	this._cache = new Object;
	
	/** 
	Type of payload - Top, branded, unbranded, ... 
	*/
	this._payloadType = kPayloadTypeUnknown;

	// Object to keep track of system requirements we are inerested in
	this.failedSystemRequirements = new Object;
	
	this.UIChildPayloads = new Array;
}


/**
Payload data was set and it did not conflict
with any data previously set for the payload. This
is primarily used as a function result for SetSessionData()
and SetCAPSData().
*/
InstallerPayload.prototype.kIntegrityOK = 0;

/**
The payload data attempting to be set did not match
the previously set data.
*/
InstallerPayload.prototype.kIntegrityBuildMismatch = 1;

/**
Returns the AdobeCode of the payload
@return AdobeCode of the payload
*/
InstallerPayload.prototype.GetAdobeCode = function()
{
	return this._adobeCode;
}


/**
Returns the session data used to initialize the
payload.
*/
InstallerPayload.prototype.GetSessionData = function()
{
	return this._sessionData;
}


/**
Initializes the payload's session data to the specified data.
The function result indicates whether or not the data
conflicted with any previously set CAPS data. If a mismatch
was found, the payload instance is left unchanged.
@param inSessionData the session data to initialize the payload with
@retval this.kIntegrityOK the data was stored successfully
@retval this.kIntegrityBuildMismatch the data conflicts with pre-existing payload data
@sa InstallerPayload.prototype.kIntegrityBuildMismatch
*/
InstallerPayload.prototype.SetSessionData = function(inSessionData)
{
    var result = this.kIntegrityOK;
	
	if (this._sessionData)
		throw "Session data already initialized for payload " + this._adobeCode;
	
	if (inSessionData.AdobeCode != this._adobeCode)
		throw "Session data being set for payload has wrong AdobeCode, expected "
		  	  + this._adobeCode + ", session data contained " + inSessionData.AdobeCode;
	
	result = this.CheckSessionCAPSDataIntegrity(inSessionData, this._capsData);
	
	if (this.kIntegrityOK == result)
	{
		this._sessionData = inSessionData;
		this.SetInstallerAction(kInstallerActionNone);
	}
	
	return result;
}


/**
Returns the CAPS data used to initialize the
payload.
*/
InstallerPayload.prototype.GetCAPSData = function()
{
	return this._capsData;
}


/**
Initializes the payload's CAPS data to the specified data.
The function result indicates whether or not the data
conflicted with any previously set session data. If a mismatch
was found, the payload instance is left unchanged.
@param inCAPSData the CAPS data to initialize the payload with
@retval this.kIntegrityOK the data was stored successfully
@retval this.kIntegrityBuildMismatch the data conflicts with pre-existing payload data
@sa InstallerPayload.prototype.kIntegrityBuildMismatch
*/
InstallerPayload.prototype.SetCAPSData = function(inCAPSData)
{
	var result = this.kIntegrityOK;
	
	if (this._capsData)
		throw "CAPS data already initialized for payload " + this._adobeCode;
	
	if (inCAPSData.payloadID != this._adobeCode)
		throw "CAPS data being set for payload has wrong AdobeCode, expected "
			  + this._adobeCode + ", CAPS data contained " + inCapsData.payloadID;
	
	result = this.CheckSessionCAPSDataIntegrity(this._sessionData, inCAPSData);
	
	if (this.kIntegrityOK == result)
		this._capsData = inCAPSData;
	
	return result;
}


/**
Utility method to determine whether the session data and CAPS data describe
the same exact payload build.
@param inSessionData session data to check
@param inCAPSData CAPS data to check
@retval this.kIntegrityOK the session and CAPS data do not conflict
@retval this.kIntegrityBuildMismatch the session and CAPS data are from different payload builds
*/
InstallerPayload.prototype.CheckSessionCAPSDataIntegrity = function(inSessionData, inCAPSData)
{
	var result = this.kIntegrityOK;
	
	if (inSessionData && inCAPSData)
	{
		if (inSessionData.BuildInfo.Created != inCAPSData.version)
		{
			// FIXME: the data necessary for this isn't present in the CAPS data
			//result = this.kIntegrityBuildMismatch;
		}
	}
	
	return result;
}


/**
Returns the product name of the payload. If session data is present, that is
returned, otherwise CAPS data is used. If neither is present, null
is returned.
@return product name of payload or null if none is available
*/
InstallerPayload.prototype.GetProductName = function()
{
	var productName = null;
	
	if (this._sessionData)
		productName = this._sessionData.ProductName;
	
	if (!productName && this._capsData)
		productName = this._capsData.productName;
	
	return productName;
}


/**
Returns the product name of the payload for use in the UI, which may be the name
of another payload, namely an already installed payload that upgrades this.
@return product name of payload or null if none is available
*/
InstallerPayload.prototype.GetUIProductName = function(inSession)
{
	var installState = this.GetPhysicalInstallState(inSession);

	if (installState && installState.effectiveAdobeCode)
	{
		var payload = inSession.allPayloads[installState.effectiveAdobeCode];
		if (payload && payload.GetProductName())
			return payload.GetProductName();
	}

	return this.GetProductName();
}


/**
Returns the location of the product icon specified in the proxy of the payload
@return product icon of payload or null if none is available
*/
InstallerPayload.prototype.GetProductIcon = function()
{

	var productIcon = this._cache["_ProductIcon"];
	if (null == productIcon)
	{
	    var newImagePath = null;
	    var validResults = null;
    	
	    if (this._adobeCode)
	    {
		    validResults = gSession.GetStreamsForAdobeCode(this._adobeCode, "ProductIcon");
	    } 

	    if (validResults && validResults["Streams"]) 
	    {
		    if (validResults["Streams"][0]) 
		    {
			    for (var i = 0; i < validResults["Streams"].length; i++) 
			    {
				    if (validResults["Streams"][i]["name"] == "ProductIcon")
				    {
					    newImagePath = validResults["Streams"][i]["path"];
					    break;
				    }
			    }
		    }	
	    }		

	    // Set the background image if we found one.
	    if (newImagePath)
	    {
		    gSession.LogDebug("ProductIcon specified for project. Using icon from " + newImagePath);
	        this._cache["_ProductIcon"] = newImagePath;
		    productIcon = newImagePath;
	    }
	}

	return productIcon;
}

/**
Returns the path of the executable to launch and arguments(if specified in the project)
@return path of the executable
*/
InstallerPayload.prototype.GetAppLaunchPathAndArguments = function()
{
    var bShouldReturnAppLaunch = true;
    var appPathAndArguments = new Object();
    
    appPathAndArguments["AppLaunchPath"] = "";
    appPathAndArguments["AppLaunchArguments"] = "";
    
    /*
    // #1834953 - AppLaunch should not be displayed on Vista
    // Platform detection code replicated from RunSystemRequirementsCheck() in utils.js
    var systemInfo = gSession.systemInfo;
    if(systemInfo.Windows)
    {
	    var currentWinOSMajor = parseInt(systemInfo.Windows.VersionMajor);
	    var currentWinOSMinor = parseInt(systemInfo.Windows.VersionMinor);
	    var windowsType = parseInt(systemInfo.Windows.ProductType);
	    if ((currentWinOSMajor == 6) && (currentWinOSMinor == 0))
	    {
		    if (1 == windowsType)
		    {
		        // Vista
		        bShouldReturnAppLaunch = false;
		    }	
			else
			{
			 	// Server2008				 			
			}	
	    }
    }    
    
    if(bShouldReturnAppLaunch)
    {
        if(this.GetSessionData().AppLaunchPath)
            appPathAndArguments["AppLaunchPath"] = this.GetSessionData().AppLaunchPath;
        
        if(this.GetSessionData().AppLaunchArguments)
            appPathAndArguments["AppLaunchArguments"] = this.GetSessionData().AppLaunchArguments;
    }
    */	
	return appPathAndArguments;
}

/**
Returns an array of all the AdobeCodes this payload's data
indicates it conflicts with. Each AdobeCode will only
appear once in the resulting array. The resulting array
elements are in no particular order. This method always
returns an array instance, though it may be empty.
@return Array of AdobeCodes this payload's data lists as conflicts 
*/
InstallerPayload.prototype.GetRawConflicts = function()
{
	var result = new Array;
	var rawConflicts = new Object;
	
	if (this._sessionData && this._sessionData.Conflicts)
	{
		for (var index = 0; index < this._sessionData.Conflicts.length; ++index)
			rawConflicts[this._sessionData.Conflicts[index]] = 1;
	}
	
	if (this._capsData && this._capsData.Conflicts)
	{
		for (var index = 0; index < this._capsData.Conflicts.length; ++index)
			rawConflicts[this._capsData.Conflicts[index]] = 1;
	}
	
	for (var aConflictAdobeCode in rawConflicts)
		result.push(aConflictAdobeCode);
	
	return result;
}


/**
Utility method to add a conflicting payload edge on the payload graph.
This does not add the edge to both payloads, the caller is responsible for
creating both edges.
@param inPayload the InstallerPayload instance this payload conflicts with
*/
InstallerPayload.prototype.AddConflictingPayload = function(inPayload)
{
	this._conflictingPayloads[inPayload.GetAdobeCode()] = inPayload;
}


/**
Returns all payload instances with which this payload conflicts. The
payload graph must have had all conflicts initialized using
AddConflictingPayload() before this method will return a proper set.
@return Map of AdobeCode to InstallerPayload instances with which
		this payload conflicts.
*/
InstallerPayload.prototype.GetConflictingPayloads = function()
{
	return this._conflictingPayloads;
}


/**
Returns an array of AdobeCodes for payloads that this
payload upgrades according to its data. The result will
be an array, though it may be empty.
@return Array of AdobeCodes this payload's data lists as upgrades 
*/
InstallerPayload.prototype.GetRawUpgradeList = function()
{
	var result = new Array;
	var rawUpgradeCodes = new Object;
	
	if (this._sessionData && this._sessionData.Upgrades)
	{
		for (var index = 0; index < this._sessionData.Upgrades.length; ++index)
			rawUpgradeCodes[this._sessionData.Upgrades[index]] = 1;
	}
	
	if (this._capsData && this._capsData.Upgrades)
	{
		for (var index = 0; index < this._capsData.Upgrades.length; ++index)
			rawUpgradeCodes[this._capsData.Upgrades[index]] = 1;
	}
	
	for (var anUpgradeCode in rawUpgradeCodes)
		result.push(anUpgradeCode);
	
	return result;
}


/**
Utility method to add an upgrade from payload edge on the payload graph.
This does not add an upgrade to edge to the parameter payload. That is
the responsibility of the caller.
@param inPayload the InstallerPayload instance this payload upgrades from
*/
InstallerPayload.prototype.AddPayloadToUpgradeFrom = function(inPayload)
{
	this._payloadsThisUpgrades[inPayload.GetAdobeCode()] = inPayload;
}


/**
Returns all payload instances that this payload upgrades. The
payload graph must have been fully initialized before this method
will return a complete result.
@return Map of AdobeCode to InstallerPayload instances that this
		payload upgrades.
*/
InstallerPayload.prototype.GetPayloadsToUpgradeFrom = function()
{
	return this._payloadsThisUpgrades;
}


/**
Utility method to add an upgrade to payload edge on the payload graph.
This does not add an upgrade from edge to the parameter payload. That is
the responsibility of the caller.
@param inPayload the InstallerPayload instance that upgrades this payload
*/
InstallerPayload.prototype.AddPayloadToUpgradeTo = function(inPayload)
{
	this._payloadsUpgradingThis[inPayload.GetAdobeCode()] = inPayload;
}


/**
Returns all payload instances that upgrade this payload. The
payload graph must have been fully initialized before this method
will return a complete result.
@return Map of AdobeCode to InstallerPayload instances that upgrade
		this payload.
*/
InstallerPayload.prototype.GetPayloadsToUpgradeTo = function()
{
	return this._payloadsUpgradingThis;
}

/**
Get the payload install state object
@param inSession the session object
*/
InstallerPayload.prototype.GetPhysicalInstallState = function(inSession)
{
	var retValue = this._installedStatus;
	if (!retValue)
	{
		if (inSession)
		{
			this._installedStatus = inSession.GetPayloadPhysicalInstallState(this.GetAdobeCode());
			retValue = this._installedStatus;
		}
	}
	return retValue;
}
/**
Returns an Array of payload Dependency data specified by
family, product, type, etc...
@return Array of Dependency family, product, etc... records, which may be empty
*/
InstallerPayload.prototype.GetUnresolvedDependencies = function()
{
	if (null == this._unresolvedDependencies)
	{
		if (this._sessionData && this._sessionData.Dependencies)
		{
			this._unresolvedDependencies = this._sessionData.Dependencies;
			for (var dependenciesIndex = 0; dependenciesIndex < this._unresolvedDependencies.length; ++dependenciesIndex)
			{
				this._unresolvedDependencies[dependenciesIndex].owningPayload = this;
			}
		}
		else
		{
			this._unresolvedDependencies = new Array;
		}
	}
	
	return this._unresolvedDependencies;
}

InstallerPayload.prototype.PropogateOperationResult = function(inSession)
{
    var retVal = 1;
    var failedDependentsArray = new Array();
    for( var p1 in this.satisfiesDependencies)
    {
        var dependencytype = this.satisfiesDependencies[p1].type;
        
		if (dependencytype == kDependencyTypeCritical)
		{
		        var criticalPayloadFailures = this.satisfiesDependencies[p1].owningPayload;
				if (!criticalPayloadFailures._criticalPayloadFailures)
				{
						criticalPayloadFailures._criticalPayloadFailures = new Array();
				}
				criticalPayloadFailures._criticalPayloadFailures.push(this);
				if(this.satisfiesDependencies[p1].owningPayload.policyNode.GetAction())
				    failedDependentsArray.push(criticalPayloadFailures.GetUIProductName(inSession));
				inSession.LogDebug("Adding failed payload to critical list");

		}
		else if (dependencytype == kDependencyTypeRequired)
		{
		        var requiredPayloadFailures = this.satisfiesDependencies[p1].owningPayload;
				if (!requiredPayloadFailures._requiredPayloadFailures)
				{
						requiredPayloadFailures._requiredPayloadFailures = new Array();
			    }
				requiredPayloadFailures._requiredPayloadFailures.push(this);
				if(this.satisfiesDependencies[p1].owningPayload.policyNode.GetAction())
				    failedDependentsArray.push(requiredPayloadFailures.GetUIProductName(inSession));
				inSession.LogDebug("Adding failed payload to required list");
		}
		else if (dependencytype == kDependencyTypeRecommended)
		{
		        var recommendedPayloadFailures = this.satisfiesDependencies[p1].owningPayload;
				if (!recommendedPayloadFailures._recommendedPayloadFailures)
				{
					recommendedPayloadFailures._recommendedPayloadFailures = new Array();
				}
				recommendedPayloadFailures._recommendedPayloadFailures.push(this);
				inSession.LogDebug("Adding failed payload to recommended list");
		}
		else
		{
					throw "Unknown dependency type.";
		}	
    }

    if(inSession.UIHosted() && failedDependentsArray.length > 0)
    {
		var button1 = {label:inSession.localization.GetString("locBtnQuit", "Quit"), left:"187px", top:"306px", returnCode:"0", hotkey:"Q", defaultOption:"0"};
		var button2 = {label:inSession.localization.GetString("locBtnContinue", "Continue"), left:"325px", top:"306px", returnCode:"1", hotkey:"C", defaultOption:"7"};

	    var buttonsArray = new Array(button1,button2);
	    
	    var midStreamFailureDOM = _createMidStreamFailureElement(inSession, failedDependentsArray);

	    var errorTitle = inSession.localization.GetString("locProductName","Adobe Product")
						+" "
						+inSession.localization.GetString("locInstaller", "Installer")
						+" - "
						+inSession.localization.GetString("locInstallationError", "Installation Error");
	    
	    var errorSubTitle = inSession.localization.GetString("locInstallationError", "Installation Error");
	    
	    if(inSession._hasUserCancelled == false)
	    {
	        if  (inSession.IsSuiteMode())
	        {
	            var _WE = new WizardError1(inSession, errorTitle, midStreamFailureDOM, buttonsArray, "error", errorSubTitle);
	            if(_WE.returnValue=="1")
	            {
	                //Continue
			        inSession.LogDebug("Mid stream failure: User decided to continue");
	                retVal = 1;
	            }
	            else
	            {
	                //Fail the complete installer
			        inSession.LogDebug("Mid stream failure: User decided to quit");
	                retVal = 0;
	            }
	        }
	        else
	        {
	            retVal = 1;
	        }
	    }
        else
        {
            retVal = 0;
        }
	}
	return retVal;
}




/**
When a payload fails and it satisfies a dependency of this payload, track the result so that 
the UI can later determine how to present the results
@param	inSatisfyingPayload		Payload that should satisfy 1 of this payload's raw dependencies that has a failed operation
@param inSession                Session for logging
@param driverPayload            driver payload
@retval	true					The dependent operation result was logged
@retval false					The dependent operation result could not be stored
*/
InstallerPayload.prototype.SetDependentOperationFailureForDriver = function(inSatisfyingPayload, inSession , driverPayload)
{
	var retValue = false;
	var arrFailureArray = null;	
	// Find the contract that this payload satisfies and then save the result in the appropriate
	// array.  It's an error if inSatisfyingPayload doesn't satisfy one of this object's dependencies
	inSession.LogDebug("Setting dependent operation for payload: " + this.LogID());

	var dependencyData = driverPayload.GetUnresolvedDependencies();
	if (dependencyData)
	{
		inSession.LogDebug("Dependency length: " + dependencyData.length);
		
		if (dependencyData.length == 0)
			retValue = true;
		
		for (var index=0; index < dependencyData.length; ++index)
		{
			var depObj = dependencyData[index];
			inSession.LogDebug("Testing dependency: " + depObj.productName + " with relationship " + depObj.type);
			
			if (inSatisfyingPayload.SatisfiesDependency(depObj))
			{		
				if (depObj.type == kDependencyTypeCritical)
				{
					if (!this._criticalPayloadFailures)
					{
						this._criticalPayloadFailures = new Array();
					}
					arrFailureArray = this._criticalPayloadFailures;
					inSession.LogDebug("Adding failed payload to critical list");

				}
				else if (depObj.type == kDependencyTypeRequired)
				{
					if (!this._requiredPayloadFailures)
					{
						this._requiredPayloadFailures = new Array();
					}
					arrFailureArray = this._requiredPayloadFailures;
					inSession.LogDebug("Adding failed payload to required list");
				}
				else if (depObj.type == kDependencyTypeRecommended)
				{
					if (!this._recommendedPayloadFailures)
					{
						this._recommendedPayloadFailures = new Array();
					}
					arrFailureArray = this._recommendedPayloadFailures;
					inSession.LogDebug("Adding failed payload to recommended list");
				}
				else
				{
					throw "Unknown dependency type.";
				}
				retValue = true;
				break;
			}
			else
			{
				inSession.LogDebug("Doesn't satisfy:" + depObj.productName);
			}
		}	
	}
	else
	{
		inSession.LogDebug("Payload has no dependencies");
	}
	
	if (!retValue)
	{
		throw "Attempt to set dependent operation result for payload that does not require it";
	}
	
	if (arrFailureArray)
	{
		arrFailureArray.push(inSatisfyingPayload);
	}
	return retValue;
}




/** 
Can this payload be attempted to install given the history of dependent operation results?
@retval 	true		Yes, the operation can be attempted
@retval 	false		No, the operation cannot be attempted
*/

InstallerPayload.prototype.CanOperationBeAttempted = function(inSession)
{   
	var retValue = false;
	if((this._criticalPayloadFailures == null) || (this._criticalPayloadFailures.length <= 0))
	{
		retValue = true;
		
	}
	else
	{
		return false;
	}
	
	// this contains the payload whose operation attempt needs to be checked.
	// 1. If this is driver. go to the sessionData and get all (n,0) and check their critical payload Failures length
	
	var driverAdobeCode = inSession.sessionCollection.driverPayloadID;

	if(this._adobeCode == driverAdobeCode)
	{
		var allFailed = false;
	    retValue = true;
		// the current payload is driver so lets get all those paylaods which have the following (requires,satisifies) = (n,0) types. For each such paylaod which will be top level paylaod and which is not again driver
		// check to see if any of its critical dependencies is failing.
		// if all top level paylaods critical dependencies are failing, lets return false for driver payload with a reason.
		var sessionPayloadList = inSession.orderedSessionPayloads;
		for (var payload1 in sessionPayloadList)
		{
			if(((sessionPayloadList[payload1].satisfiesDependencies == null ) || (sessionPayloadList[payload1].satisfiesDependencies.length < 1)) && (sessionPayloadList[payload1]._adobeCode != driverAdobeCode) && (sessionPayloadList[payload1]._sessionData.isExtensionPayload !="1"))
			{
				// we are at the top level payloads excluding the driver and so lets return this back as 
				allFailed = true;
				if((sessionPayloadList[payload1]._criticalPayloadFailures == null) || (sessionPayloadList[payload1]._criticalPayloadFailures.length <= 0))
				{
					allFailed = false;
					break;
				}
					

			}

		}
		if (allFailed == true)
		{
			retValue = false;
		}

	}
	else
	{   
	    retValue = false;
	    if (this.satisfiesDependencies.length == 0)
	        retValue = true;
	    else
	    {
			var satisfiesDependenciesWithNoActionCount = 0;
	        for (var payload1 in this.satisfiesDependencies)
		    {       
			    // we are at the top level payloads excluding the driver and so lets return this back as 
			    if((this.satisfiesDependencies[payload1].owningPayload._criticalPayloadFailures == null) || (this.satisfiesDependencies[payload1].owningPayload._criticalPayloadFailures.length <= 0))
			    {
					if (this.satisfiesDependencies[payload1].owningPayload.GetInstallerAction() != "none")
					{
						retValue = true;
						break;
					}
					else
					{
						satisfiesDependenciesWithNoActionCount++;
					}
			    }	
		    }
		    if(this.policyNode.mode == kWorkflowModeUninstall && satisfiesDependenciesWithNoActionCount == this.satisfiesDependencies.length)
		    {
				// We have an orphaned payload. Allow removal
				retValue = true;
			}
		}
	}
	


	return retValue;
					
}


/**
Returns true if this payload satisfies the specified dependency data. This does
NOT rely on the data stored using AddDependencySatisfier(), but instead is
used to determine whether or not such an addition would be appropriate.
@param inDependencyData dependency data returned by GetUnresolvedDependencies().
@retval true this payload meets the specified dependency
@retval false this payload does not meet the dependency
*/
InstallerPayload.prototype.SatisfiesDependency = function(inDependencyData)
{
	var doesSatisfy = false;
	
	// Get the satisfies info
	var satisfiesData;
	if (this._sessionData)
		satisfiesData = this._sessionData.Satisfies;
	
	if (satisfiesData)
	{
		// test the data
		if (satisfiesData.family == inDependencyData.family &&
			satisfiesData.productName == inDependencyData.productName)
		{
			if (inDependencyData.minVersion.length <= 0 ||
				compareVersions(inDependencyData.minVersion, satisfiesData.version) >= 0)
			{
				doesSatisfy = true;
			}
		}
	}
	
	return doesSatisfy;
}


/**
Adds the specified payload as a payload that satisfies the specified
dependency. The dependency should be a dependency returned
by a prior call to this.GetUnresolvedDependencies(). This also adds
the dependency to the array of dependencies that the satisfying
payload satisfies.
@param ioDependency dependency returned from this.GetUnresolvedDependencies()
@param ioSatisfyingPayload the payload satisfying the dependency
*/
InstallerPayload.prototype.AddDependencySatisfier = function(ioDependency, ioSatisfyingPayload)
{
	if (!ioDependency.satisfyingPayloads)
		ioDependency.satisfyingPayloads = new Object;
	
	ioDependency.satisfyingPayloads[ioSatisfyingPayload.GetAdobeCode()] = ioSatisfyingPayload;
	
	ioSatisfyingPayload.satisfiesDependencies.push(ioDependency);
}


/**
Returns an Array of AdobeCodes that the payload is specifically dependent on. It
does not include those dependencies that result from resolving Requires proxy
information.
@return Array of AdobeCodes that this payload specifically depends upon, may be empty
*/
InstallerPayload.prototype.GetRawFixedDependencies = function()
{
	var result;
	
	if (this._capsData)
		result = this._capsData.Requires;
	
	if (!result)
		result = new Array;
	
	return result;
}


/**
Get the dependency tree of a payload looking down (who I depend on)
flattened into an array.
*/
InstallerPayload.prototype.GetDependentsArray = function()
{
	if (!this._dependentsArray)
	{
		// We return an array for easy counting
		this._dependentsArray = new Array();

		// But accumulate in a map for uniqueness
		var dependentsMap = new Object();

		// For each of my direct dependencies...
		var req1 = this.GetUnresolvedDependencies();
		for (var p1 in req1)
		{
			// get and record the satisfying payloads, and recurse
			var req2 = req1[p1].satisfyingPayloads;
			AccumulatePayloads(dependentsMap, req2);
			
			// Removing the recursion as part of fix for 1778679
			/*
			for (var p2 in req2)
			{
				AccumulatePayloads(dependentsMap, req2[p2].GetDependentsArray());
			}
			*/
		}
		
		// Repackage as an array
		for (var p in dependentsMap)
		{
			this._dependentsArray.push(dependentsMap[p]);
		}
	}

	return this._dependentsArray;
}


/**
Get the dependency tree of a payload looking up (who depends on me)
flattened into an array.
*/
InstallerPayload.prototype.GetSatisfiedArray = function()
{
	if (!this._satisfiedArray)
	{
		// We return an array for easy counting
		this._satisfiedArray = new Array();

		// But accumulate in a map for uniqueness
		var satisfiedMap = new Object();

		// For each of those who depend on me...
		for (var p1 in this.satisfiesDependencies)
		{
			// Recored their satisfaction and recurse
			satisfiedMap[this.satisfiesDependencies[p1].owningPayload.GetAdobeCode()] = this.satisfiesDependencies[p1].owningPayload;
			
			// Removing the recursion as part of fix for 1778679
			//AccumulatePayloads(satisfiedMap, this.satisfiesDependencies[p1].owningPayload.GetSatisfiedArray());
			
			//dependencytypemap[this.satisfiesDependencies.....adobeCode+","+this.adobeCode]=this.satisfiesDependencies[]...type;
		}

		// Repackage as an array
		for (var p in satisfiedMap)
		{
			this._satisfiedArray.push(satisfiedMap[p]);
		}
	}
	return this._satisfiedArray;
}




/**
Get the dominant dependency type of a payload 
*/
InstallerPayload.prototype.GetDominantDependencyType = function()
{


	this._dominantType = "non-critical";		
	
	
	// For each of those who depend on me...
	for (var p1 in this.satisfiesDependencies)
	{
	dependencyData = this.satisfiesDependencies[p1].owningPayload.GetUnresolvedDependencies();
	if (dependencyData)
	{

		for (var index=0; index < dependencyData.length; ++index)
		{
			var depObj = dependencyData[index];
			
			
			if (this.SatisfiesDependency(depObj))
			{		
				if (depObj.type == kDependencyTypeCritical)
				{
					this._dominantType = "critical"

				}
				else if (depObj.type == kDependencyTypeRequired)
				{
				
				}
				else if (depObj.type == kDependencyTypeRecommended)
				{
					
				
				}
				retValue = true;
				break;
			}
		
		}	
	}

	}
	return this._dominantType;
}





/**
Adds the specified collection record to this payload's map of
collections it is a member of.
@param inCollectionRecord the payload collection record describing the relationship
*/
InstallerPayload.prototype.AddCollectionRecord = function(inCollectionRecord)
{
	if (inCollectionRecord.payload != this)
		throw "Attempt to add a collection record to the wrong payload.";
	
	this._collectionRecords[inCollectionRecord.collection.collectionID] = inCollectionRecord;
}

/**
Returns a Map of collectionID to collection records for the collections
this payload is a member of. The returned map are those collection
records stored using AddCollectionRecord().
@return Map of collectionID to collection records
*/
InstallerPayload.prototype.GetCollectionRecords = function()
{
	return this._collectionRecords;
}


/**
Set the installer action for the session payload.
NOTE: It is possible to set invalid actions via this method!
*/
InstallerPayload.prototype.SetInstallerAction = function(inAction)
{
	if (this._sessionData) // must be in the session
	{
		if (inAction == kInstallerActionNone
			|| inAction == kInstallerActionInstall
			|| inAction == kInstallerActionRepair
			|| inAction == kInstallerActionRemove)
		{
			this._installerAction = inAction;
		}
		else
		{
			throw "Invalid installer action \"" + inAction + "\" for " + inAdobeCode;
		}
	}
}


/**
Get the current installer action for the session paylooad.
NOTE: The current installer action may not be valid!
*/
InstallerPayload.prototype.GetInstallerAction = function()
{
	return this._installerAction;
}


/**
Convenience method to determine if the payload supports the specified language.
*/
InstallerPayload.prototype.SupportsLanguage = function(inIsoCode)
{
	if (this.GetSessionData())
	{
		if (this.GetSessionData().isLanguageIndependent == 1)
		{
			return true;
		}
		else
		{
			var curLanguages;
			curLanguages = this.GetSessionData().Languages;
			for (var j = 0; j < curLanguages.length; ++j)
			{
				if (curLanguages[j] == inIsoCode)
				{
					return true;
				}
			}
			
			
			var satisfies = this.GetSessionData().Satisfies;
			if (!(( this.isExtensionPayload()) && (this.GetSessionData().Extends.type=="langPack")))
			{
			    var satisfiesStr = satisfies.family + satisfies.productName;
			    //This payload does not support the given language
			    //Check if it is supported by a language pack
			    var payloadMap = this.policyNode.session.sessionData.payloadMap;
			    for (var adobeCode in payloadMap)
			    {
				    var aPayload = payloadMap[adobeCode];
				    if((aPayload.isExtensionPayload == "1") && (aPayload.Extends.type=="langPack") && (satisfiesStr == (aPayload.Extends.family + aPayload.Extends.productName)))
				    {
					    var curLanguages;
					    curLanguages = aPayload.Languages;
					    for (var j = 0; j < curLanguages.length; ++j)
					    {
						    if (curLanguages[j] == inIsoCode)
						    {
							    return true;
						    }
					    }		
				    }
			    }
			}
		}
	}
	return false;
}


/**
Operation sensitive estimated disk space impact.

Returns an object with a totalBytes property indicating the total system wide space impact,
and a map of install roots with a size for each destination. 

If inIgnoreMachineState is set, this ignores the selected operation and the installed state
of the payload, effectively returning the payload size plus the fudge factor for log file.
*/
InstallerPayload.prototype.OperationSize = function(inIgnoreMachineState)
{
	var result = {
		totalBytes: 0,
		roots: {}
	}

	// This is guaranteed to be where log files end up on Windows.  Almost certain on Mac.
	var logRoot = "[Common]";

	// How the log size for the payload relates to the payloads size.  This is very crude.
	var logFactor = 0.10; 

	if (this.GetSessionData())
	{
		var installDestData = this.GetSessionData().InstallDestinationData;
	    if(installDestData == null)
	        installDestData = new Object();
		for (var destination in installDestData)
		{
			var destinationObj = this.GetSessionData().InstallDestinationData[destination];

			var payloadBytes = 0;
			var logBytes = 0;

			if (inIgnoreMachineState)
			{
				payloadBytes = Number(destinationObj.totalSize);
				logBytes = Number(destinationObj.totalSize) * logFactor;
			}
			else
			{
				switch (this.GetInstallerAction())
				{
					case kInstallerActionRemove:
						if (this.GetInstallationRefCount() == 1)
						{
							payloadBytes = 0 - Number(destinationObj.totalSize);
							logBytes = Number(destinationObj.totalSize) * logFactor;
						}
						break;
					case kInstallerActionRepair:
						payloadBytes = Number(destinationObj.totalSize);
						logBytes = Number(destinationObj.totalSize) * logFactor;
						break;
					case kInstallerActionInstall:
						if (this.GetInstallationRefCount() == 0)
						{
							payloadBytes = Number(destinationObj.totalSize);
							logBytes = Number(destinationObj.totalSize) * logFactor;
						}
						break;
				}
				
				// FIXME: Hack to display correct total size. 
				// Currently a LangPack is always set to install if the session allows. It's action is only calculated on the Next button click
				// If a langpacks parent is not set to install, set it's required size = 0
				if(this.isExtensionPayload() && this.GetSessionData().Extends.type == "langPack" )
				{
					if(this.parentPayload)
					{
						if(this.parentPayload.GetInstallerAction() != kInstallerActionRepair && this.parentPayload.GetInstallerAction() != kInstallerActionInstall)
						{
							payloadBytes = 0;
							logBytes = 0;
						}
					}
				}
			}

			logBytes = Math.round(logBytes);
			result.totalBytes += payloadBytes + logBytes;

			if (payloadBytes > 0)
			{
				if (result.roots[destinationObj.root])
					result.roots[destinationObj.root] += payloadBytes;
				else
					result.roots[destinationObj.root] = payloadBytes;
			}

			if (logBytes > 0)
			{
				if (result.roots[logRoot])
					result.roots[logRoot] += logBytes;
				else
					result.roots[logRoot] = logBytes;
			}
		}
	}

	return result;
}



/**
Set the operation result for this payload in the current session.  
@param	inOperationResult		The operation result structure that represents this payloads result.
@retval	true					The operation result could be set
@retval false					The operation result could not be set.  
@sa		GetOperationResult		
*/
InstallerPayload.prototype.SetOperationResult = function(inOperationResult)
{
	this._operationResult = null;
	if (inOperationResult)
	{
		var localClone = function(inSource, inTarget)
		{
			for (var eachProp in inSource)
			{
				if (typeof(inSource[eachProp]) == 'object')
				{
					inTarget[eachProp] = new Object();
					localClone(inSource[eachProp], inTarget[eachProp]);
				}
				else
				{
					inTarget[eachProp] = inSource[eachProp].valueOf();
				}					
			}		
		}
		this._operationResult = new Object();
		localClone(inOperationResult, this._operationResult);
	}
	return true;
	
}

/**
Get the operation result for this payload in the current session.  
@param	inOperationResult		The operation result structure that represents this payloads result.
@retval	this._operationResult (possibly null)	 
@sa		SetOperationResult		
*/
InstallerPayload.prototype.GetOperationResult = function()
{
	return this._operationResult;	
}

/**
Returns true iff this payload would be installable given the
specified installation property map. This does not check for
dependencies, but instead returns only whether or not this
payload in particular has a problem.
@param inInstallProperties property map to test the payload against
@retval true the payload can be installed in the context of the given property map
@retval false the payload cannot be installed with the given properties
*/
InstallerPayload.prototype.CanBeInstalledWithProperties = function(inInstallProperties)
{
	var hasPropertyConflict = false;
	
	if (!inInstallProperties[gConstants.kPropInstallLanguage])
		hasPropertyConflict = true;
	
	if (!hasPropertyConflict && !this.SupportsLanguage(inInstallProperties[gConstants.kPropInstallLanguage]))
		hasPropertyConflict = true;
	
	return !hasPropertyConflict;
}


/**
Returns the number of collections for which this payload is already installed
@return count of collections for which this payload is installed
*/
InstallerPayload.prototype.GetInstallationRefCount = function()
{
	var refCount = 0;
	
	for (var aCollectionID in this._collectionRecords)
	{
		var aCollectionRecord = this._collectionRecords[aCollectionID];
		if (kCapsInstallStateInstalled == aCollectionRecord.installState)
			++refCount;
	}
	
	return refCount;
}


/**
Returns the installState for the collection. This returns the direct
install state and does not follow any upgrade relationships.
@return install state of the payload for the speicified collection
*/
InstallerPayload.prototype.GetInstallStateForCollection = function(inCollection)
{
	var installState = kCapsInstallStateUninstalled;
	
	var payloadCollectionJoin = this._collectionRecords[inCollection.collectionID];
	if (payloadCollectionJoin && (null != payloadCollectionJoin.installState))
	{
		installState = payloadCollectionJoin.installState;
	}
	
	return installState;
}


/**
Returns whether this payload is the driver payload for the current session
@return true if this is the driver, else false
*/
InstallerPayload.prototype.IsDriverForSession = function(inSession)
{
	var driver = inSession.GetDriverPayload();
	if (driver)
	{
		driver = inSession.sessionPayloads[driver.AdobeCode];
		if (driver)
		{
			// Am I the driver?
			if (driver.GetAdobeCode() == this.GetAdobeCode())
			{
				return true;
			}
		}
	}
	return false;
}

/**
Returns whether this payload is a patch payload
@return true if this is a patch payload, else false
*/
InstallerPayload.prototype.isPatchPayload = function()
{
    var sData = this.GetSessionData();
    if ( sData && sData.isExtensionPayload == "1" &&  sData.Extends && sData.Extends.type == "patch")	
        return true;
    return false;
}

/**
Returns whether this payload is an extension payload
@return true if this is an extension payload, else false
*/
InstallerPayload.prototype.isExtensionPayload = function()
{
    var sData = this.GetSessionData();
    if ( sData && sData.isExtensionPayload == "1")	
        return true;
    return false;
}

/**
Returns whether this payload has defined a UIParent
This does not confirm that it's UIParent has been included in the current session
*/
InstallerPayload.prototype.hasUIParent = function()
{
    var sData = this.GetSessionData();
    if ( sData && sData.hasUIParent == "1")
        return true;
    return false;
}


/**
Walk up the dependency chain to see if we are a dependent on the specified payload.
*/
InstallerPayload.prototype.SatisfiesPayload = function(inInstallerPayload)
{
	if (inInstallerPayload && inInstallerPayload.GetAdobeCode)
	{
		// We satisfy ourself
		if (this.GetAdobeCode() == inInstallerPayload.GetAdobeCode())
		{
			//alert(this.GetAdobeCode() + " self satisfies " + inInstallerPayload.GetAdobeCode());
			return true;
		}
	
		// We don't satisfy anyone, we hit a dead end.
		if (!this["satisfiesDependencies"])
		{
			//alert(this.GetAdobeCode() + " doesn't satisfy anyone");
			return false;
		}
	
		// Check out if anyone we satisfy satisfies the target
		for (var satisfiesIndex = 0; satisfiesIndex < this.satisfiesDependencies.length; satisfiesIndex++)
		{
			if (this.satisfiesDependencies[satisfiesIndex].owningPayload.SatisfiesPayload(inInstallerPayload))
			{
				//alert(this.GetAdobeCode() + " indirectly satisfies " + inInstallerPayload.GetAdobeCode());
				return true;
			}
		}
		//alert(this.GetAdobeCode() + " satisfies some payloads but not " + inInstallerMode.GetAdobeCode());
	}
	else
	{
		//alert("Bad arguments");		
	}

	return false;
}


/**
Determine if this payload should be visible in the installer UI 
*/
InstallerPayload.prototype.GetUIVisibilityForSession = function(inSession)
{
	var actionMap = this.GetUISelectionAction(inSession);
	return actionMap.visible;
}


/**
Determine if this payload should be selectable in the installer UI 
*/
InstallerPayload.prototype.GetUISelectabilityForSession = function(inSession)
{
	var actionMap = this.GetUISelectionAction(inSession);
	return actionMap.selectable;
}
/**
Does this payload have dependents that have failed?
*/
InstallerPayload.prototype.HasDependentFailures = function()
{
    return ((this._criticalPayloadFailures && this._criticalPayloadFailures.length > 0)
        ||(this._requiredPayloadFailures && this._requiredPayloadFailures.length > 0)
        ||(this._recommendedPayloadFailures && this._recommendedPayloadFailures.length > 0)
    )
}

/**
Set the installer action as "checked" or "unchecked" taking into account various constraints
encountered in UI mode such as dependencies, serial numbers and the like.  Returns a map with
information the UI can use to refresh its state.

@param inSession the session object
@param inCheckState optional boolean value as to whether the payload is "checked" or not.
*/
InstallerPayload.prototype.GetUISelectionAction = function(inSession, inCheckState)
{
	if (null == this.policyNode)
	{
		// Make sure the payload policy graph is setup.
		inSession.PayloadPolicyInit();
	}
	return this.policyNode.GetUIPolicy(inCheckState);
}



/**
Utility to report a payload's identity in a log file.
*/
InstallerPayload.prototype.LogID = function()
{
	return this.GetAdobeCode() + " " + this.GetProductName();
}

/**
Overrides the session properties with payload specific properties for custom action hooks
*/
InstallerPayload.prototype.GetPayloadOverrideProperties = function(inProperties)
{
    var outProps = new Object();
    for(var prop in inProperties)
    {
        outProps[prop] = inProperties[prop];
    }
    
    outProps["AdobeCode"] = this.GetAdobeCode();
    outProps["productName"] = this.GetSessionData().ProductName;
    if(outProps["eula_EPIC_EULA_SELECTED"] == null)
    {
        outProps["eula_EPIC_EULA_SELECTED"] = "0";   
    }
    
    if(outProps["eula_EPIC_EULA_ACCEPTED"] == null)
    {
        outProps["eula_EPIC_EULA_ACCEPTED"] = "0";   
    }
    
    if(this.GetSessionData().MediaInfo && this.GetSessionData().MediaInfo.path)
    {
        outProps["mediaPath"] = this.GetSessionData().MediaInfo.path;   
    }
    
    return outProps;
}

/**
Checks the bitness of the payload.
If this payload is 64-bit and current OS is 32-bit, return false.
*/
InstallerPayload.prototype.CheckBitnessForInclusion = function(Is64Bit)
{
	var bRet = true;
	try
	{
		if(this.GetSessionData().BuildInfo.ProcessorFamily == "x64")
		{
			if(Is64Bit)
			{	
				bRet = true;
				
			}
			else
			{
				bRet = false;
				
			}
			
		}
	}
	catch (ex)
	{
		
	}
	return bRet;
}

/**
Set the type of payload
*/
InstallerPayload.prototype.SetPayloadType = function(inPayloadType)
{
	this._payloadType = inPayloadType;
}


/**
Compute the session-static UI parameters of the payload

Each payload in the session has a policyNode object.  This object manages the static
and dynamic constraints on the payload that affect the possible and actual payload
operations.

A node exists in one of three states:

  F = Free -- I have no constraints.
  D = Dependent -- Not personally constrained, but some other payload constrains me.
  I = Intrinsic -- I am personally constrained.

A node operation is either:

  Y = operate
  N = don't operate

So a node will be in on of these states:

  FN, FY, DN, DY, IN, IY

The NodeCode() method will return this for logging convenience.

From the outside, only FN or FY nodes can be manipulated either by the user or programattically.

Most intrinsic constraints are static and only computed once.  The two dynamic intrinsic
constraints are install language and serialization.  If either of those two conditions change,
the Recompute() method needs to be called.  Likewise, the constraints are installer mode
dependent and Recompute() is needed if you change from, say, uninstall mode to repair mode.

*/

/**
Constraint classes
*/
var kPolicyClass = { 
    Free : 0, /*< Node is freely selectable */
    Dependent : 1,/*< Node is constrained by a dependency */
    Intrinsic : 2 /*< Node is constrained */
};


/**
Specific constraints
*/

// Free payload - This is here just for the sake of consistency
var kFreePolicyCode = { Free : 0 };

// Constrained by a dependency
var kDependentPolicyCode = { 
    Unknown : 0, /*< Unknown dependency */
    Recommended : 1, /*< Recommended dependency */
    Required : 2, /*< Required dependency */
    Critical : 3 /*< Critical dependency */
};

// Constrained by an intrinsic constraint
var kIntrinsicPolicyCode = { 
    Language : 4, /*< Language not supported */
    Serial : 5, /*< Serial number required */
    Installed : 6, /*< Identical payload installed */
    NotInstalled : 7, /*< Payload not installed (Remove workflow)*/
    Obsolete : 8, /*< An upgraded payload is installed */
    Upgrade : 9, /*< This upgrades an installed payload */
    Conflict : 10, /*< This payloads conflicts with something installed */
    Driver : 11, /*< This payload is a driver */
    Sysreq : 12, /*< This payload has an un-met system requirement */
    NotOwner : 13, /*< This session is not the owner of this payload (remove) */
    ProtectedPayloadNotInstallable : 14 /*< This session is not the owner of this payload (remove) */
};

/**
Types of actions
*/
var kPolicyActionYes = true;
var kPolicyActionNo = false;

/**
Determines whether the payload has been installed as a driver in a previous installation
*/
InstallerPayload.prototype.HasBeenInstalledAsDriverElsewhere = function()
{

    var payloadCapsData = this.GetCAPSData();
    if(payloadCapsData)
    {
        if(payloadCapsData.Collections)
        {
            for (var i in payloadCapsData.Collections)
            {
                // Check if this payload had been installed as a driver in another session
                if(payloadCapsData.Collections[i].collection.driverPayloadID == this._adobeCode && gSession.properties["sessionID"] != payloadCapsData.Collections[i].collectionID)
                    return true;
            }
        }
    }
    
	return false;
}

/**
Returns if the payload is third-party and has raw conflicts installed
*/
InstallerPayload.prototype.ThirdPartyHasRawConflicts = function()
{
    var retVal = false;
    var payloadSessionData = this.GetSessionData();
    if (payloadSessionData.ThirdPartyCapabilities
				&& payloadSessionData.ThirdPartyCapabilities.hasConflicts == "1")
	    retVal = true;
	    	    
	return retVal;			    
}

function PayloadPolicyNode(inSession, inPayload)
{
	// Our universe
	this.session = inSession;
	this.payload = inPayload;
	this.mode = kWorkflowModeInstall;

	// Have we looked at our Universe yet?
	this.valid = false;

	// We are born FN
	this.constraintClass = kPolicyClass.Free;
	this.constraintCode = kFreePolicyCode.Free;
	this.action = new AdobeProperty(kPolicyActionNo);


	// What we do when we become F?  See Init() for the actual default.
	this.affinity = kPolicyActionNo;
	
	// Do we expose ourself?  This is static.
	this.visible = true;
	
	// Constraint messages.
	this._message = {
		note: null,
		detail: new Object()
	};
	
	// Synchronize the payload operation with our action
	var thisCB = this;
	this.action.Bind(function (val) { thisCB._SetPayloadAction(val); });

	// Existing install status
	this.installedStatus = this.payload.GetPhysicalInstallState(this.session);
	this.installedStatus.logicallyInstalled = this.installedStatus.logicallyInstalled != "0";
	this.installedStatus.physicallyInstalled = this.installedStatus.physicallyInstalled != "0";
	this.installedOrUpdated = (this.installedStatus.logicallyInstalled
		|| this.installedStatus.physicallyInstalled
		|| this.installedStatus.effectiveAdobeCode != this.payload.GetAdobeCode());
	this.installedForThisSession = this.payload.GetInstallStateForCollection(this.session.sessionCollection) == kCapsInstallStateInstalled;
	this.installedAsDriverElseWhere =  this.payload.HasBeenInstalledAsDriverElsewhere();
	this.updated = this.installedStatus.effectiveAdobeCode != this.payload.GetAdobeCode();

	// Static visibility: Dynamic conditions that may override this but those are pushed downstream
    // #1637521: By default always be visible unless we are the driver
	this.driver = this.session.GetDriverPayload();
	if (this.driver)
	{
		this.driver = this.session.sessionPayloads[this.driver.AdobeCode];
		if (this.driver)
		{
		    if(this.driver._adobeCode == this.payload._adobeCode)
	            this.visible = false;
	        else
	        {
	            if ( inPayload.isExtensionPayload())
	                this.visible = false;
	            else 
	                this.visible = true;
	        }
	    }
	}
	    
	// For quick reference....
	this.isSessionDriver = this.payload.IsDriverForSession(this.session);

	// Payload Note
	this.payloadDependencies = new Object();
	this.payloadDependencies["payloadNoteRequires"] = new Array();
	this.payloadDependencies["payloadNoteRecommends"] = new Array();
	this.payloadDependencies["payloadNoteRequiredBy"] = new Array();
	this.payloadDependencies["payloadNoteRecommendedBy"] = new Array();
	
	this.payloadNote = "";

	// Cache
	this._cache = new Object();
}


/**
Initialize the node in the graph context.  When we are born, we know only of ourself.
This introduces the node to the universe, possibly taking it from FN to some other 
state.
*/
PayloadPolicyNode.prototype.Init = function(inOptInstallerMode)
{
    if (!this.valid || (null != inOptInstallerMode && inOptInstallerMode != this.mode))
	{
		// Set the affinity
		if (inOptInstallerMode == kWorkflowModeUninstall || (null == inOptInstallerMode && this.mode == kWorkflowModeUninstall))
		{
			if( this.IsProtected(this.payload._adobeCode))
				this.SetAffinity(kPolicyActionNo);
			else
			// Gravitate toward removal if unconstrained.
			this.SetAffinity(kPolicyActionYes);
		}
		else
		{
			// Top level payloads, those with no dependents, auto-select while dependencies
			// auto-deselect when given a chance.  Except already installed payloads, per
			// business rules.
			if( this.IsProtected(this.payload._adobeCode))
				this.SetAffinity(kPolicyActionNo);
			else if (this._UpTree().length == 0 && !(this.visible && (this.installedOrUpdated || ContainerNotEmpty(this.payload.GetPayloadsToUpgradeFrom()))))
				this.SetAffinity(kPolicyActionYes);
			// Extension payloads auto-select by default
			else if(this.payload.isExtensionPayload())
		        this.SetAffinity(kPolicyActionYes);
            else		  
				this.SetAffinity(kPolicyActionNo);

			if(this.payload.hasUIParent() && this.payload.uiParentPayload)
				this.SetAffinity(kPolicyActionNo);
		}

        // Write the payload notes for downtree dependencies
		var downTree = this._DownTree();
		for (var ppni in downTree)
		{
			var ppn = downTree[ppni].policynode;

			switch (downTree[ppni].dependencyType)
			{
			    case kDependencyTypeCritical:
			        //this.payloadDependencies["payloadNoteRequires"].push(ppn.payload.GetUIProductName(gSession));
			        break;
			    case kDependencyTypeRequired:
			        //this.payloadDependencies["payloadNoteRequires"].push(ppn.payload.GetUIProductName(gSession));
			        break;
			    case kDependencyTypeRecommended:
			        this.payloadDependencies["payloadNoteRecommends"].push(ppn.payload.GetUIProductName(gSession));
			        break;
			    default:  
			        break;
			}
		}

        // Write the payload notes for uptree dependencies
		var upTree = this._UpTree();
		for (var ppni in upTree)
		{
			var ppn = upTree[ppni].policynode;

			switch (upTree[ppni].dependencyType)
			{
			    case kDependencyTypeCritical:
			        //this.payloadDependencies["payloadNoteRequiredBy"].push(ppn.payload.GetUIProductName(gSession));
			        break;
			    case kDependencyTypeRequired:
			        //this.payloadDependencies["payloadNoteRequiredBy"].push(ppn.payload.GetUIProductName(gSession));
			        break;
			    case kDependencyTypeRecommended:
					this.payload.isRecommended = true;
			        this.payloadDependencies["payloadNoteRecommendedBy"].push(ppn.payload.GetUIProductName(gSession));
			        break;
			    default:  
			        break;
			}
		}

        var depNote = null;
        // removed the braces for fixes in bug 1678103
        for (depNote in this.payloadDependencies)
        {
            if(this.payloadDependencies[depNote].length > 0)
            {
	            this.payloadNote = this.payloadNote + (new LocalizedString(depNote, "Some dependency specified for")).Translate(gSession.localization) + ": ";
			    for(var i = 0; i < this.payloadDependencies[depNote].length - 1; i++)
			    {
	                this.payloadNote = this.payloadNote + this.payloadDependencies[depNote][i] + ((i==this.payloadDependencies[depNote].length - 2)?" & ":", ");
			    }
	            this.payloadNote = this.payloadNote + this.payloadDependencies[depNote][i];
			    //this.payloadNote = this.payloadNote + ")";
			    this.payloadNote = this.payloadNote + "<br>";
			}
        }

        this.alreadyInstalledPayloadNote = null;
        
        if( this.IsProtected(this.payload._adobeCode))
        {
            this.isProtectedPayloadInstallable = false;
            if (inOptInstallerMode != kWorkflowModeUninstall)
            {
                if(gSession.IsProtectedPayloadInstallable(this.payload._adobeCode).isValid == "1")
                {
                    this.isProtectedPayloadInstallable = true;
                }
            }
        }

		// Calculate our constraints, either intrinsic or dependent
	    this.Recompute(inOptInstallerMode);
	
		// Set our default action
		this.SetAction(this.affinity);
		this.valid = true;
	}
}


/**
Like Init but doesn't mess with the affinity or gratuitiously set the action to the affinity.
*/
PayloadPolicyNode.prototype.Recompute = function(inOptInstallerMode)
{
	if (null != inOptInstallerMode && inOptInstallerMode != this.mode)
	{
		this.mode = inOptInstallerMode;
		if (this.mode == kWorkflowModeMaintenance
			&& this.payload.GetInstallStateForCollection(this.session.sessionCollection) != kCapsInstallStateInstalled)
		{
			this.mode = kWorkflowModeInstall;
		}
	}
	this.ComputeConstraint();
}


/**
Get the constraint type of this node.
*/
PayloadPolicyNode.prototype.GetConstraintClass = function()
{
	return this.constraintClass;
}

/**
Get the constraint type of this node.
*/
PayloadPolicyNode.prototype.GetConstraintCode = function()
{
	return this.constraintCode;
}


/**
Get the action type of this node.
*/
PayloadPolicyNode.prototype.GetAction = function()
{
	return this.action.Get();
}


/**
Return shorthand representation of the node state.
*/
PayloadPolicyNode.prototype.NodeCode = function()
{
	var constraint = null;
	if (this.GetConstraintClass() == kPolicyClass.Free)
		constraint = "F"
	else if (this.GetConstraintClass() == kPolicyClass.Dependent)
		constraint = "D"
	else
		constraint = "I";
	return constraint  + (this.GetAction() == kPolicyActionYes ? "Y" : "N");
}


/**
Change the affinity of the node.  This can be used by the UI to
"remember" a user-selected state and return to it when a dependent
node becomes free.
*/
PayloadPolicyNode.prototype.SetAffinity = function(inOptAction)
{
    if (null == inOptAction)
		this.affinity = this.GetAction();
	else
		this.affinity = inOptAction;
	
	// replicate our affinity to payloads like us.
	var doppelgangers = this._Doppelgangers();
	for (var pi in doppelgangers)
	{
		if (doppelgangers[pi].policyNode)
		{
			doppelgangers[pi].policyNode.affinity = this.affinity;
		}
	}
}


/**
Set the action.  Returns false if the action cannot be set, for example
this isn't an F node.
*/
PayloadPolicyNode.prototype.SetAction = function(inAction)
{
	// Short circuit free or no action change, except for IY nodes
	// Also when we are the driver and no action being taken
	if (((this.GetConstraintClass() != kPolicyClass.Free && !(this.GetConstraintClass() == kPolicyClass.Dependent)) || this.GetAction() == inAction)
	 	&& !(this.GetConstraintClass() == kPolicyClass.Intrinsic && this.GetAction() == kPolicyActionYes && inAction == kPolicyActionYes)
	 	&& !((this.GetConstraintClass() == kPolicyClass.Intrinsic && this.GetConstraintCode() == kIntrinsicPolicyCode.Driver) && this.GetAction() != inAction)
	    )
	{
		return false;
	}

	var startCode = this.NodeCode();

	// Set the action
	this.action.Set(inAction);

	// Logic inversion for remove
	var goalAction = (this.mode == kWorkflowModeUninstall) ? kPolicyActionNo : kPolicyActionYes;
	
	// Always update the driver payload action in Suite mode
    if(this.session.IsSuiteMode())
    {
        // Don't recurse while setting the driver action
        if((this.driver) && (this.driver._adobeCode != this.payload._adobeCode))
        {
            var allSelected = true;
            var atLeastOneSelected = false;
            for (var p in this.session.allPayloads)
            {
                aPayload = this.session.allPayloads[p]
                if((this.driver) && (this.driver._adobeCode != p) && (aPayload._payloadType == kTopLevelPayload) && ((aPayload.policyNode.GetUIPolicy()).selectable))
                {
                    atLeastOneSelected = atLeastOneSelected || aPayload.policyNode.GetAction();
                    allSelected = allSelected && aPayload.policyNode.GetAction();
                }
            }
            
            if(this.mode == kWorkflowModeUninstall)
            {
                if(allSelected)
                {
                    this.driver.policyNode.SetAction(kPolicyActionYes);
                }
                else
                {
                    this.driver.policyNode.SetAction(kPolicyActionNo);
                }
            }
            else
            {
				// Driver action must always be set as true
                this.driver.policyNode.SetAction(kPolicyActionYes);
            }
        }
	}

	// Update the graph
	if (this.GetAction() == goalAction)
	{
		// FN to FY
		// Mark down-tree F nodes as D and coerce action.
		var downTree = this._DownTree();
		for (var ppni in downTree)
		{
			var ppn = downTree[ppni].policynode;
			// Visible modify nodes are already installed so we don't care about them.
            // var oldConstraintCode = ppn.GetConstraintCode();
            // ppn.constraintCode = kDependentPolicyCode.Unknown;

			if (ppn.GetConstraintClass() == kPolicyClass.Free || ppn.GetConstraintClass() == kPolicyClass.Dependent)
			{
				var ppnBefore = ppn.NodeCode();
				ppn.constraintClass = kPolicyClass.Dependent;
				// Save the highest level of dependency in the policy code
				switch (downTree[ppni].dependencyType)
				{
				    case kDependencyTypeCritical:
				        ppn.constraintCode = kDependentPolicyCode.Critical;
				        break;
				    case kDependencyTypeRequired:
				        if (oldConstraintCode != kDependentPolicyCode.Critical)
				            ppn.constraintCode = kDependentPolicyCode.Required;
				        break;
				    case kDependencyTypeRecommended:
				        if (ppn.constraintCode != kDependentPolicyCode.Critical && ppn.constraintCode != kDependentPolicyCode.Required)
				            ppn.constraintCode = kDependentPolicyCode.Recommended;
				        break;
				    default:  
				        ppn.constraintCode = kDependentPolicyCode.Unknown;
				        break;
				}

                // if( ppn.constraintCode == kDependentPolicyCode.Unknown)
                //        ppn.constraintCode = oldConstraintCode;

				if(!(this.payload.IsDriverForSession(this.session)) || ((this.payload.IsDriverForSession(this.session)) && ppn.constraintCode != kDependentPolicyCode.Recommended))
					ppn.SetAction(this.GetAction());

				ppn._message.note = new LocalizedString("locPayloadSelectRequired", "required");
				this.session.LogDebug("PayloadPolicyNode.SetAction: " + ppnBefore + "->" + ppn.NodeCode() + " for dependency " + ppn.payload.LogID());
			}
			else if (ppn.GetAction() != kPolicyActionYes && !ppn._HaveFreeDoppelganger())
			{
				this.session.LogWarning("PayloadPolicyNode.SetAction: " + ppn.NodeCode() + " payload " + ppn.payload.LogID() + " is required by " + this.payload.LogID() + " but isn't free.");
			}
		}
		if(this.mode == kWorkflowModeUninstall)
		{
			var cpn;
			for(cpn in this.payload.UIChildPayloads)
			{
				this.payload.UIChildPayloads[cpn].policyNode.SetAction(inAction);
			}
		}

	}
	else
	{
		// FY to FN 		
		// Re-evaluate down-tree D nodes up-tree constraints to see if they remain D or transition to Fa.
		var downTree = this._DownTree();
		for (var ppni in downTree)
		{
			var dependent = downTree[ppni].policynode;

			if (dependent.GetConstraintClass() == kPolicyClass.Dependent)
			{
                var oldConstraintCode = dependent.GetConstraintCode;
                dependent.constraintCode = kDependentPolicyCode.Unknown;

				var dependentBefore = dependent.NodeCode();
				// Look up from here to see if we are still required by someone other than this.
				var stillRequired = false;
				var upTree = dependent._UpTree();
				for (var dependentParenti in upTree)
				{
					var dependentParent = upTree[dependentParenti].policynode;
					if (dependentParent.GetAction() == goalAction && !(this.mode == kWorkflowModeUninstall && (dependentParent.constraintClass == kPolicyClass.Intrinsic && ((dependentParent.constraintCode == kIntrinsicPolicyCode.NotInstalled) || (dependentParent.constraintCode == kIntrinsicPolicyCode.NotOwner)))))
					{

					    switch (upTree[dependentParenti].dependencyType)
					    {
					        case kDependencyTypeCritical:
					            dependent.constraintCode = kDependentPolicyCode.Critical;
					            break;
					        case kDependencyTypeRequired:
					            if (dependent.constraintCode != kDependentPolicyCode.Critical)
					                dependent.constraintCode = kDependentPolicyCode.Required;
					            break;
					        case kDependencyTypeRecommended:
					            if (dependent.constraintCode != kDependentPolicyCode.Critical && dependent.constraintCode != kDependentPolicyCode.Required)
					                dependent.constraintCode = kDependentPolicyCode.Recommended;
					            break;
					        default:  
					            dependent.constraintCode = kDependentPolicyCode.Unknown;
					            break;
					    }

						stillRequired = true;
						// break;
					}
				}
				
	            if( this.constraintCode == kDependentPolicyCode.Unknown)
	                    this.constraintCode = oldConstraintCode;
				
				// Not required, FREE!
				if (!stillRequired)
				{
			 		dependent.constraintClass = kPolicyClass.Free;
			 		dependent.constraintCode = kFreePolicyCode.Free;
					dependent.SetAction(inAction);
					dependent._message.note = null;
					this.session.LogDebug("PayloadPolicyNode.SetAction: " + dependentBefore + "->" + dependent.NodeCode() + " for dependency " + dependent.payload.LogID());
				}
	            
			}
		}
		var cpn;
		for(cpn in this.payload.UIChildPayloads)
		{
			this.payload.UIChildPayloads[cpn].policyNode.SetAction(inAction);
		}
	}
	
	this.session.LogDebug("PayloadPolicyNode.SetAction: " + startCode + "->" + this.NodeCode() + " for " + this.payload.LogID());
    return true;
}


/**
The hairball.  Compute the constraints of this node.  This may set the action as
a side effect.
*/
PayloadPolicyNode.prototype.ComputeConstraint = function()
{
   
	var staticIntrinsic = this._cache["ComputeConstraint." + this.mode];
	var oldConstraintClass = this.GetConstraintClass();
	var oldConstraintCode = this.GetConstraintCode();
	this.constraintClass = kPolicyClass.Free;
	this.constraintCode = kFreePolicyCode.Free;
	
	// Compute and cache static intrinsics
	if (!staticIntrinsic)
	{
		var staticIntrinsic = {
			constraintClass: kPolicyClass.Free,
			constraintCode: kFreePolicyCode.Free,
			action: kPolicyActionNo,
			note: null,
			detail: new Object()
		};

		this._cache["ComputeConstraint." + this.mode] = staticIntrinsic;

		//
		// Install specific static intrinsic constraints.
		//
		if (this.mode == kWorkflowModeInstall)
		{
			if (this.payload.IsDriverForSession(this.session))
			{
				// Special for the driver
				if (this.installedOrUpdated)
				{
					// Upgraded
					if (this.installedStatus.effectiveAdobeCode != this.payload.GetAdobeCode())
					{
						staticIntrinsic.constraintClass = kPolicyClass.Intrinsic;
						staticIntrinsic.constraintCode = kIntrinsicPolicyCode.Obsolete;
					}
					else
					{
						staticIntrinsic.constraintClass = kPolicyClass.Intrinsic;
						staticIntrinsic.constraintCode = kIntrinsicPolicyCode.Installed;
					}
					staticIntrinsic.action = kPolicyActionNo;
					staticIntrinsic.detail["InstallerPayload.Installed"] = {
						className: "alertCritical",
						text: [new LocalizedString("locDriverInstalled", "<br/>[productName] is already installed on this machine.<br/><br/><br/><br/>To reinstall, please run the installer which performed the original installation.")]};
				}
				else
				{
					// The case where we have options--think suite--and our driver upgrades another
					// driver--think suite again--so we block.
					var driverUpgradeDriver = false;

					// Do we have non-required payloads?
					var sessionCount = 0;
					for (var p in this.session.sessionPayloads)
						sessionCount++;
						
					var dependentsCount = this.payload.GetDependentsArray().length + 1;

					if (sessionCount > dependentsCount)
					{
						// Who are we upgrading
						var obsoletePayloads = this.driver.GetPayloadsToUpgradeFrom();
						for (var opi in obsoletePayloads)
						{
							// What collectinos are they in?
							var op = obsoletePayloads[opi];
							var collections = op.GetCollectionRecords();
							for (var ci in collections)
							{
								// Are they a driver?
								var collectionDriver = collections[ci].collection.driverPayload;
								if (collectionDriver)
								{
									if (collectionDriver.GetAdobeCode() == op.GetAdobeCode())
									{
										driverUpgradeDriver = true;
										break;
									}
								}
							}
							if (driverUpgradeDriver)
							{
								break;
							}
						}
					}
					
					if (driverUpgradeDriver)
					{
						staticIntrinsic.constraintClass = kPolicyClass.Intrinsic;
						staticIntrinsic.constraintCode = kIntrinsicPolicyCode.Upgrade;
						staticIntrinsic.action = kPolicyActionNo;

						// Uninstall methods are platform specific
						var uninstallMethods = new Array();
						//uninstallMethods.push(new LocalizedString("locRemoveMethodOriginal", "Launch its original installer"));
						if (this.session.properties["platform"] == "OSX")
							uninstallMethods.push(new LocalizedString("locRemoveMethodOSX", "Use the Uninstall Utility in [installerPath]", {installerPath: "/Applications/Utilities/Adobe Installers"}));
						if (this.session.properties["platform"] == "Win32")
							uninstallMethods.push(new LocalizedString("locRemoveMethodWin32", "Use the Add/Remove Programs Utility"))

						// Assemble the message
						staticIntrinsic.detail["InstallerPayload.DriverUpgrade"] = {
							className: "alertCritical",
							text: [
								new LocalizedString("locDriverUpgrade", "An older version of [productName] is already installed, and must be removed before installation of a newer version can proceed."),
								new LocalizedString("locToRemoveDo", "To remove this software do the following:"),
								uninstallMethods
							]};
					}
					if(this.IsProtected(this.payload._adobeCode))
					{
					    if(this.isProtectedPayloadInstallable == false)
				        {
					        // Driver is protected and not installable
					        staticIntrinsic.constraintClass = kPolicyClass.Intrinsic;
					        staticIntrinsic.constraintCode = kIntrinsicPolicyCode.ProtectedPayloadNotInstallable;
					    staticIntrinsic.detail["InstallerPayload.ProtectedPayloadNotInstallable"] = {
						    className: "alertCritical",
						    text: [new LocalizedString("locProtectedPayloadNotInstallable", "Please serialize the product and then run this installer.")]};
				        }
				    }
				}

				if (staticIntrinsic.constraintClass != kPolicyClass.Intrinsic)
				{
					// Normal driver constraint...force install
					staticIntrinsic.constraintClass = kPolicyClass.Intrinsic;
					staticIntrinsic.constraintCode = kIntrinsicPolicyCode.Driver;
					staticIntrinsic.action = kPolicyActionYes;
				}
			}

			// Are we an upgrade?  Just a note, not a constraint.
			if (null == staticIntrinsic.note && ContainerNotEmpty(this.payload.GetPayloadsToUpgradeFrom()))
			{
				staticIntrinsic.note = new LocalizedString("locPayloadSelectUpgrade", "update");
			}


			// Conflicts
			if (ContainerNotEmpty(this.payload.GetConflictingPayloads()))
			{
				// If we conflict with something OUTSIDE our session that is installed, that
				// is a static constraint.  Conflicts with payloads IN our session is handled
				// dynamically.
				var conflictPayloads = this.payload.GetConflictingPayloads();
				var conflictNames = new Array();
				for (var cp in conflictPayloads)
				{
					if (this.session.sessionPayloads[cp] == null && conflictPayloads[cp].GetInstallationRefCount() > 0)
					{
						// We have an out-of-session payload that conflicts with us.  Check if it upgrades
						// an in-session payload.
						var indirectInternalConflict = false;
						var cpUpgrades = conflictPayloads[cp].GetPayloadsToUpgradeFrom();
						for (var cpUgrade in cpUpgrades)
						{
							if (this.session.sessionPayloads[cpUpgrade] != null)
							{
								indirectInternalConflict = true;
								break;
							}
						}
						
						// Only post a static conflict constraint if for external conflicts.
						if (!indirectInternalConflict)
						{
							conflictNames.push(conflictPayloads[cp].GetUIProductName(this.session));
						}
					}
				}

				// If we conflict with something installed, the show stops here.
				if (conflictNames.length > 0)
				{
					staticIntrinsic.constraintClass = kPolicyClass.Intrinsic;
					staticIntrinsic.constraintCode = kIntrinsicPolicyCode.Conflict;
					staticIntrinsic.action = kPolicyActionNo;
					staticIntrinsic.note = new LocalizedString("locPayloadSelectConflict", "conflict");
					staticIntrinsic.detail["InstallerPayload.Conflict." + this.payload.GetAdobeCode()] = {
						className: "warning",
						text: [new LocalizedString("locPayloadSelectConflictDetail", "[payloadName] conflicts with: [conflictList]", { payloadName: this.payload.GetUIProductName(this.session), conflictList: "" }),
							conflictNames, new LocalizedString("locPayloadSelectConflictDetailEnd", "Please uninstall these products, restart your computer and then run this installer again.")]};
				}
			}

			// Do I have system requirements problems?
			if (staticIntrinsic.constraintClass != kPolicyClass.Intrinsic)
			{
				var haveSysRequirements = false;
				var systemRequirementResults = RunSystemRequirementsCheck(this.session, [this.payload]);

				// Errors showing requirements
				if (systemRequirementResults[0] && systemRequirementResults[0].length > 0)
				{
					haveSysRequirements = true;
					staticIntrinsic.constraintClass = kPolicyClass.Intrinsic;
					staticIntrinsic.constraintCode = kIntrinsicPolicyCode.Sysreq;
					var detailID = "InstallerPayload.sysreqError." + this.payload.GetAdobeCode();
					staticIntrinsic.detail[detailID] = {
						className: "alertCriticalNoBlock",
						text: [new LocalizedString("systemPageSysReqErrorStart", null, { productName: this.payload.GetUIProductName(this.session) }), systemRequirementResults[0]] };
				}

				// Errors showing problems with the system
				if (systemRequirementResults[2] && systemRequirementResults[2].length > 0)
				{
					haveSysRequirements = true;
					staticIntrinsic.constraintClass = kPolicyClass.Intrinsic;
					staticIntrinsic.constraintCode = kIntrinsicPolicyCode.Sysreq;
					var detailID = "InstallerPayload.sysreqExclude." + this.payload.GetAdobeCode();
					staticIntrinsic.detail[detailID] = {
						className: "alertCriticalNoBlock",
						text: [new LocalizedString("systemPageSysReqExcludedErrorStart", null, { productName: this.payload.GetUIProductName(this.session) }), systemRequirementResults[2]] };
				}

				// Warnings showing requirements
				if (systemRequirementResults[1] && systemRequirementResults[1].length > 0)
				{
					haveSysRequirements = true;
					var detailID = "InstallerPayload.sysreqWarning." + this.payload.GetAdobeCode();
					staticIntrinsic.detail[detailID] = {
						className: "warning",
						text: [new LocalizedString("systemPageSysReqWarningStart", null, { productName: this.payload.GetUIProductName(this.session) }), systemRequirementResults[1]] };
				}

				if (haveSysRequirements)
				{
					if (staticIntrinsic.constraintClass == kPolicyClass.Intrinsic && staticIntrinsic.constraintCode == kIntrinsicPolicyCode.Sysreq)
					{
					    if(this.payload.failedSystemRequirements["CPUErrorSSE2"] && this.payload.failedSystemRequirements["CPUErrorSSE2"] == true)
					    {
						    this.payloadNote = (new LocalizedString("systemPageCPUPPCNotValid", "This product does not support PowerPC architecture CPU.")).Translate(gSession.localization);
						    staticIntrinsic.action = kPolicyActionNo;
					    }
					    else
					    {
						    this.payloadNote = (new LocalizedString("payloadNoteFailsSysCheck", "System requirements not satisfied for this product.")).Translate(gSession.localization);
						    staticIntrinsic.action = kPolicyActionNo;
						}
					}
					staticIntrinsic.note = new LocalizedString("locPayloadSelectSystemRequirement", "system check");
				}
			}
		}

		//
		// Repair specific static intrinsic constraints.
		//
		if (this.mode == kWorkflowModeMaintenance)
		{
		    if (this.payload.IsDriverForSession(this.session))
			{				
				if (staticIntrinsic.constraintClass != kPolicyClass.Intrinsic)
				{
					// Normal driver constraint...force install
					staticIntrinsic.constraintClass = kPolicyClass.Intrinsic;
					staticIntrinsic.constraintCode = kIntrinsicPolicyCode.Driver;
					staticIntrinsic.action = kPolicyActionYes;
				}
			}
			    var language = this.session.properties[gConstants.kPropInstallLanguage];
			    if (!language)
				    language = this.session.properties["defaultLanguage"];
			    if (!language)
				    language = "en_US";	

			// Language support
			if (this.GetConstraintClass() != kPolicyClass.Intrinsic)
			{
			    if(!this.payload.SupportsLanguage(language))
			    {
				    // No need for a note, these go stealth in UI
		            this.constraintClass = kPolicyClass.Intrinsic;
				    this.constraintCode = kIntrinsicPolicyCode.Language;
				    this.action.Set(kPolicyActionNo);
			    }
    		    else
			    {
			        if(this.payload.isExtensionPayload())
			        {
		                this.constraintClass = kPolicyClass.Free;
				        this.constraintCode = kFreePolicyCode.Free;
				        this.SetAction(kPolicyActionYes);
				    }
			    }
			 }
		}

		//
		// Remove specific static intrinsic constraints.
		//
		if (this.mode == kWorkflowModeUninstall)
		{
			// We have to be installed to uninstall.
			if (this.payload.GetInstallStateForCollection(this.session.sessionCollection) != kCapsInstallStateInstalled)
			{
				staticIntrinsic.constraintClass = kPolicyClass.Intrinsic;
				staticIntrinsic.constraintCode = kIntrinsicPolicyCode.NotInstalled;
				staticIntrinsic.action = kPolicyActionNo;
				staticIntrinsic.note = new LocalizedString("locPayloadSelectNotInstalled", "not installed");
			}
			
			// Business rules...
			// If we are visible and have been upgraded by a driver in another session, block uninstall.
			if (staticIntrinsic.constraintCode != kPolicyClass.Intrinsic && this.visible)
			{
				var effectivePayload = this.session.allPayloads[this.installedStatus.effectiveAdobeCode];
				if (effectivePayload)
				{
					var payloadCollections = effectivePayload.GetCollectionRecords();
					for (var ci in payloadCollections)
					{
						var collectionDriver = payloadCollections[ci].collection.driverPayload;
						if (collectionDriver)
						{
							if ((collectionDriver.GetAdobeCode() == effectivePayload.GetAdobeCode())&& (payloadCollections[ci].collectionID != gSession.properties["sessionID"]))
							{
				                staticIntrinsic.constraintClass = kPolicyClass.Intrinsic;
								staticIntrinsic.constraintCode = kIntrinsicPolicyCode.NotOwner;
								staticIntrinsic.action = kPolicyActionNo;
								staticIntrinsic.note = new LocalizedString("locPayloadSelectUpgraded", "see below");
								staticIntrinsic.detail["InstallerPayload.PayloadSelectNotOwner"] = // singleton
								{
									className: "alertCriticalNoBlock",
									text: new LocalizedString("locPayloadSelectUpgradedDetail", "This software has been updated since it was originally installed. To remove this software you must first run its update installer and remove the update.  Once the update is removed, re-launch this installer to completely remove the software.")
								};
								break;
							}
						}
					}
				}
			}
		}
		
		// Report our constraints
		this.session.LogDebug("PayloadPolicyNode.ComputeConstraint: Static " 
			+ this.mode
			+ " mode constraint is "
			+ staticIntrinsic.constraintClass
			+ staticIntrinsic.constraintCode
			+ ","
			+ (staticIntrinsic.action ? "Y" : "N")
			+ " for "
			+ this.payload.LogID());
	}
	
	// Stash whatever notes and details we have from the static constraints.
	this._message.note = staticIntrinsic.note;
	this._message.detail = new Object();
	for (var di in staticIntrinsic.detail)
		this._message.detail[di] = staticIntrinsic.detail[di];

	// If we have static intrinsics, set constraint accordingly.
	if (staticIntrinsic.constraintClass != kPolicyClass.Free)
	{
		this.constraintClass = staticIntrinsic.constraintClass;
		this.constraintCode = staticIntrinsic.constraintCode;
		this.action.Set(staticIntrinsic.action);
	}

	// Otherwise have a looksee at the dynamical constraints
	else
	{
		if (this.mode == kWorkflowModeInstall)
		{
		    if( this.IsProtected(this.payload._adobeCode))
		    {
				if(((this.session.properties["pers_EPIC_SERIAL"]) || (this.isProtectedPayloadInstallable)))
					this.action.Set(kPolicyActionYes);
				else
					this.action.Set(kPolicyActionNo);
			}	
			var language = this.session.properties[gConstants.kPropInstallLanguage];
			if (!language)
				language = this.session.properties["defaultLanguage"];
			if (!language)
				language = "en_US";	

			// Language support
			if (this.GetConstraintClass() != kPolicyClass.Intrinsic)
			{
			    if(!this.payload.SupportsLanguage(language))
			    {
				    // No need for a note, these go stealth in UI
		            this.constraintClass = kPolicyClass.Intrinsic;
				    this.constraintCode = kIntrinsicPolicyCode.Language;
				    this.action.Set(kPolicyActionNo);
			    }
    		    else
			    {
			        if(this.payload.isExtensionPayload())
			        {
		                this.constraintClass = kPolicyClass.Free;
				        this.constraintCode = kFreePolicyCode.Free;
				        this.SetAction(kPolicyActionYes);
				    }
			    }
			 }

			// Conflicts
			if (this.GetConstraintClass() != kPolicyClass.Intrinsic 
				&& ContainerNotEmpty(this.payload.GetConflictingPayloads()))
			{
				// Conflicts with payloads outside are session that are not upgrades
				// to payloads in our session are handled by a static constraint.
				// If we get here, we are dealing only with internal or upgraded internal
				// conflicts.
				var conflictPayloads = this.payload.GetConflictingPayloads();
				var conflictNames = new Array();
				for (var cp in conflictPayloads)
				{
					if (conflictPayloads[cp].GetInstallationRefCount() > 0)
					{
						conflictNames.push(conflictPayloads[cp].GetUIProductName(this.session));
					}
				}

				// If we conflict with something installed, the show stops here.
				if (conflictNames.length > 0)
				{
		            this.constraintClass = kPolicyClass.Intrinsic;
				    this.constraintCode = kIntrinsicPolicyCode.Conflict;
					this.action.Set(kPolicyActionNo);
					this._message.note = new LocalizedString("locPayloadSelectConflict", "conflict");
					// staticIntrinsic.detail["InstallerPayload.Conflict." + this.payload.GetAdobeCode()] = {
					// 	className: "warning",
					// 	text: [new LocalizedString("locPayloadSelectConflictDetail", "[payloadName] conflicts with: [conflictList]", { payloadName: this.payload.GetUIProductName(this.session), conflictList: "" }), conflictNames]};
				}
			}
			

		}
		
		if (this.mode == kWorkflowModeMaintenance)
	    {
		    if( this.IsProtected(this.payload._adobeCode))
		    {
				if(((this.session.properties["pers_EPIC_SERIAL"]) || (this.isProtectedPayloadInstallable)))
					this.action.Set(kPolicyActionYes);
				else
					this.action.Set(kPolicyActionNo);
			}	
				
	        var language = this.session.properties[gConstants.kPropInstallLanguage];
		    if (!language)
			    language = this.session.properties["defaultLanguage"];
		    if (!language)
			    language = "en_US";	

		    // Language support
		    if (this.GetConstraintClass() != kPolicyClass.Intrinsic)
			{
			    if(!this.payload.SupportsLanguage(language))
			    {
				    // No need for a note, these go stealth in UI
		            this.constraintClass = kPolicyClass.Intrinsic;
				    this.constraintCode = kIntrinsicPolicyCode.Language;
				    this.action.Set(kPolicyActionNo);
			    }
    		    else
			    {
			        if(this.payload.isExtensionPayload())
			        {
		                this.constraintClass = kPolicyClass.Free;
				        this.constraintCode = kFreePolicyCode.Free;
				        this.SetAction(kPolicyActionYes);
				    }
			    }
			 }
	    }
	}

	// Do we inherit a blocking intrinsic from below?  If we are IN, don't bother with this.
	if (!(this.GetConstraintClass() == kPolicyClass.Intrinsic && kPolicyActionNo == this.GetAction()))
	{
		var downTree = this._DownTree();
		for (var ppni in downTree)
		{
			var ppn = downTree[ppni].policynode;
			// Disregard ppn if it hasn't been initialized.
			if (ppn.valid
				&& ppn.GetConstraintClass() == kPolicyClass.Intrinsic
				&& ppn.GetAction() != kPolicyActionYes
				&& !ppn._HaveFreeDoppelganger())
			{
				// We are blocked, propogate the note.
				this.action.Set(kPolicyActionNo);
				this._message.note = ppn._message.note;
				this._message.dependentConstraintClass = ppn.GetConstraintClass();
				this._message.dependentConstraintCode = ppn.GetConstraintCode();
				for (var di in ppn._message.detail)
				{
					this._message.detail[di] = ppn._message.detail[di];
					this._message.detail[di].dependentConstraintClass = ppn.GetConstraintClass();
					this._message.detail[di].dependentConstraintCode = ppn.GetConstraintCode();
				}
				this.constraintClass = kPolicyClass.Dependent;
				this.constraintCode = kDependentPolicyCode.Unknown;
				break;
			}
		}
	}
	
	// Logic inversion for remove
	var goalAction = (this.mode == kWorkflowModeUninstall) ? kPolicyActionNo : kPolicyActionYes;

	// Are we constrained from above?
	if (this.GetConstraintClass() == kPolicyClass.Free)
	{
	    var oldConstraintCode = this.GetConstraintCode();
        this.constraintCode = kDependentPolicyCode.Unknown;
		var upTree = this._UpTree()
		for (var ppni in upTree)
		{
			ppn = upTree[ppni].policynode;
			// Disregard ppn if it hasn't been initialized.
			if (ppn.valid
				&& ppn.GetAction() == goalAction
				&& !(this.mode == kWorkflowModeUninstall && (ppn.GetConstraintClass() == kPolicyClass.Intrinsic && ppn.GetConstraintCode() == kIntrinsicPolicyCode.NotInstalled)))
			{
				switch (upTree[ppni].dependencyType)
				{
				    case kDependencyTypeCritical:
				        this.constraintCode = kDependentPolicyCode.Critical;
				        break;
				    case kDependencyTypeRequired:
				        if (this.constraintCode != kDependentPolicyCode.Critical)
				            this.constraintCode = kDependentPolicyCode.Required;
				        break;
				    case kDependencyTypeRecommended:
				        if (this.constraintCode != kDependentPolicyCode.Critical && this.constraintCode != kDependentPolicyCode.Required)
				            this.constraintCode = kDependentPolicyCode.Recommended;
				        break;
				    default:  
				        this.constraintCode = kDependentPolicyCode.Unknown;
				        break;
				}

				this.constraintClass = kPolicyClass.Dependent;
				// this.constraintCode = kDependentPolicyCode.Unknown;
				this.SetAction(goalAction);
				this._message.note = new LocalizedString("locPayloadSelectRequired", "required");
			}
		}
	    if( this.constraintCode == kDependentPolicyCode.Unknown)
	            this.constraintCode = oldConstraintCode;
	}
	
	// If we made a constrained-to-free transition, set our action.
	if (this.GetConstraintClass() == kPolicyClass.Free && oldConstraintClass != kPolicyClass.Free)
	{
		this.SetAction(this.affinity);
	}
}


/**
Get a list of payloads just like me, meaning they satisfy the same family/productName.
This returns a list of InstallerPayload objects, not PayloadPolicyNodes!  We don't know
if the PayloadPolicyNodes will all exist when this is called.  Look for the policyNode
attribute when you get the list back.
*/
PayloadPolicyNode.prototype._Doppelgangers = function()
{
	var doppelgangers = this._cache["_Doppelgangers"];
	if (null == doppelgangers)
	{
		// Locate and cache our shadow-selves.
		doppelgangers = new Array();
		this._cache["_Doppelgangers"] = doppelgangers;
		var satisfies = this.payload.GetSessionData().Satisfies;
		for (var pi in this.session.sessionPayloads)
		{
			var p = this.session.sessionPayloads[pi];
			if (this.payload.GetAdobeCode() != p.GetAdobeCode())
			{
				var testSatisfies = p.GetSessionData().Satisfies;
				if (satisfies.family == testSatisfies.family && satisfies.productName == testSatisfies.productName)
				{
					doppelgangers.push(p);
					this.session.LogDebug("PayloadPolicyNode._Doppelgangers: adding doppelganger " + p.LogID() + " for " + this.payload.LogID());
				}
			}
		}
	}
	return doppelgangers;
}


/**
Do I have an unconstrained doppelganger that satisfies the same requirement?
*/
PayloadPolicyNode.prototype._HaveFreeDoppelganger = function()
{
	var doppelgangers = this._Doppelgangers();

	if (null != doppelgangers)
	{
		for (var ti in doppelgangers)
		{
			var ppn = doppelgangers[ti].policyNode;
			if (ppn && (ppn.GetConstraintClass() == kPolicyClass.Free || ppn.GetAction() == kPolicyActionYes))
			{
				return true;
			}
		}
	}

	return false;
}


/**
Return an array of PayloadPolicyNode objects we depend on.
*/
PayloadPolicyNode.prototype._DownTree = function()
{
	if (null == this._cache["_DownTree"])
	{
		var pnList = new Array();
		var acTree = PayloadDependencySort(this.payload.GetDependentsArray());
		for (var ac in acTree)
		{
		    var satisfiesDep = acTree[ac].satisfiesDependencies;
		    var sd = null;
		    for( sd in satisfiesDep)
		    {
		        if (satisfiesDep[sd].owningPayload._adobeCode == this.payload._adobeCode)
		        {
		            break;
		        }
		    }
		    
			pnList.push( {policynode: acTree[ac].policyNode, dependencyType : satisfiesDep[sd].type});
		}
		this._cache["_DownTree"] = pnList;
	}
	return this._cache["_DownTree"];
}


/**
Return an array of PayloadPolicyNode objects that depend on this.
*/
PayloadPolicyNode.prototype._UpTree = function()
{
	if (null == this._cache["_UpTree"])
	{
		var pnList = new Array();
		var acTree = PayloadDependencySort(this.payload.GetSatisfiedArray());
		for (var ac in acTree)
		{
		    var unresolvedDep = acTree[ac]._unresolvedDependencies;
		    var foundDep = false;
		    var ud = null;
		    for( ud in unresolvedDep)
		    {
		        // Dummy for loop to get the keyname of the first item
		        var udAC; 
		        for (udAC in unresolvedDep[ud].satisfyingPayloads)
		        {
					if (udAC == this.payload._adobeCode)
					{
						foundDep = true;
					    break;
					}
		        }
		        if(foundDep)
					break;
		    }
		    
			pnList.push( {policynode: acTree[ac].policyNode, dependencyType : unresolvedDep[ud].type});
		}
		this._cache["_UpTree"] = pnList;
	}
	return this._cache["_UpTree"];
}

/**
Are all this payloads parent critically dependent on this?
This determines visibility of the payload. If even one non-critical dependency exists, the payload ust be shown in the UI
*/
PayloadPolicyNode.prototype.IsCompletelyCritical = function()
{
    if(this.isCompletelyCritical == null)
    {
        this.isCompletelyCritical = true;
        var upTree = this._UpTree();
        
        // Top level payload
        if(upTree.length == null || upTree.length == 0)
        {
	            this.isCompletelyCritical = false;
        }
        
        for (var dependentParenti in upTree)
        {
	        var dependencyType = upTree[dependentParenti].dependencyType;
	        if(dependencyType != kDependencyTypeCritical)
	        {
	            this.isCompletelyCritical = false;
	            break;
	        }
        }
    }
    return this.isCompletelyCritical;
}


/**
Set the payload operation appropriately for the current action.
*/
PayloadPolicyNode.prototype._SetPayloadAction = function(inAction)
{
	// Decide what to do
	var payloadOperation = kInstallerActionNone;
	if (inAction == kPolicyActionYes)
	{
		payloadOperation = kInstallerActionInstall;

		if (this.mode == kWorkflowModeUninstall)
		{
			payloadOperation = kInstallerActionRemove;
		}
		else if (this.mode == kWorkflowModeMaintenance)
		{
			// If we are not installed, we are in install mode.
			if (this.payload.GetInstallStateForCollection(this.session.sessionCollection) == kCapsInstallStateInstalled)
			{
				payloadOperation = kInstallerActionRepair;
			}
		}
	}
	
	// Do it
	this.session.LogDebug("PayloadPolicyNode._SetPayloadAction: " + payloadOperation + " for " + this.payload.LogID());
	this.payload.SetInstallerAction(payloadOperation);
}

PayloadPolicyNode.prototype.CheckLangPackVisibility = function()
{
	return (!((this.payload.isExtensionPayload()) && (this.payload.GetSessionData().Extends.type=="langPack")));
}

/**
Map the policy to legacy GetUISelectionAction structure.
*/
PayloadPolicyNode.prototype.GetUIPolicy = function(inCheckState)
{
	if (true === inCheckState || false === inCheckState)
	{
		this.SetAction(inCheckState ? kPolicyActionYes : kPolicyActionNo);
	}
	var result = {
	    // Visible: Visible AND not completely critical AND not language AND not installed
		visible: (this.visible && !(this.IsProtected(this.payload._adobeCode)) && this.CheckLangPackVisibility() && (this.IsCompletelyCritical() == false) && (!(this.GetConstraintClass() == kPolicyClass.Intrinsic && this.GetConstraintCode() == kIntrinsicPolicyCode.Language)) && (!(this.GetConstraintClass() == kPolicyClass.Intrinsic && this.GetConstraintCode() == kIntrinsicPolicyCode.NotInstalled)) && !(this.mode == kWorkflowModeUninstall && kPolicyClass.Intrinsic && this.GetConstraintCode() == kIntrinsicPolicyCode.NotOwner)),
		// Selectable: Visible AND (Free top level payload OR Recomended dependency)
		selectable: (this.visible && !(this.IsProtected(this.payload._adobeCode)) && ((this.GetConstraintClass() == kPolicyClass.Free && (this.payload.GetSatisfiedArray().length == null || this.payload.GetSatisfiedArray().length <=0)) || (this.GetConstraintClass() == kPolicyClass.Dependent && this.GetConstraintCode() == kDependentPolicyCode.Recommended))),
		blocked: (this.GetConstraintClass() == kPolicyClass.Free && this.GetAction() != kPolicyActionYes),
		checkedState:  (this.GetAction() == kPolicyActionYes),
		payloadNote: this.payloadNote,
		payloadError: null
	};
	
	if(this.payload.isExtensionPayload())
	{
	    result.visible = false;
	    result.selectable = false;
	    result.checkedState = false;
	}
	
	//In PP mode, non-driver non-selectable payloads must be hidden
	if(!(this.session.IsSuiteMode()) && !(this.isSessionDriver) && !(result.selectable) && (this.payload.isRecommended == false))
		result.visible = false;

	//In PP mode, driver must be visible
	if(!(this.session.IsSuiteMode()) && (this.isSessionDriver))
		result.visible = true;

	if (this.mode == kWorkflowModeUninstall) {
		var payloadSessionData = this.payload.GetSessionData();
		if (payloadSessionData.ThirdPartyCapabilities
				&& payloadSessionData.ThirdPartyCapabilities.isUninstallable == "0") {
			result.visible = false;
			result.selectable = false;
		}
	}

	//Child payload selectability depends on its UIParent
	if(this.payload.hasUIParent() && this.payload.uiParentPayload)
	{
		if(this.mode != kWorkflowModeUninstall)
		{
			if(this.payload.uiParentPayload.policyNode.GetAction() == false)
				result.selectable = false;
		}
		else
		{
		    // #1753766 - Payload having UIParent should not be selectable at uninstall time
			// if(this.payload.uiParentPayload.policyNode.GetAction() == true)
				result.selectable = false;
		}
	}

	// result.payloadNote = new LocalizedString("locDummyPayloadNote", "Payload Note: " + this.payload.LogID() + "");

	// Business rules dictate a pseudo-constraint on already installed payloads, including upgrading payloads.
	// We block any payload that has been upgraded or already installed top level payloads
	if (this.mode != kWorkflowModeUninstall)
	{
		if((this.payload._payloadType == kTopLevelPayload && this.installedOrUpdated && !(this.installedForThisSession)) || this.updated || this.installedAsDriverElseWhere || this.payload.ThirdPartyHasRawConflicts())
		{
		    if ( !(this.alreadyInstalledPayloadNote) )
			{
				if(this.updated)
				{
					this.alreadyInstalledPayloadNote = (new LocalizedString("locPayloadSelectNewerInstalled", "Newer version installed")).Translate(gSession.localization);
					this.payloadNote = this.payloadNote + ("(" + this.alreadyInstalledPayloadNote + ")" + "<br>");
					result.payloadNote = this.payloadNote;
				}
				else
				{
					this.alreadyInstalledPayloadNote = (new LocalizedString("locPayloadSelectAlreadyInstalled", "Already installed")).Translate(gSession.localization);
					this.payloadNote = this.payloadNote + ("(" + this.alreadyInstalledPayloadNote + ")" + "<br>");
					result.payloadNote = this.payloadNote;
				}
			}
			
			if ((this.payload._payloadType == kTopLevelPayload || this.installedAsDriverElseWhere) && (result.selectable || result.checkedState))
			{
			    this.session.LogDebug("PayloadPolicyNode.GetUIPolicy: licensing constraint forbids ref-counting " + this.payload.LogID());
			    result.selectable = false;
			    this.SetAction(kPolicyActionNo);
			}
			
			
			result.checkedState = (this.GetAction() == kPolicyActionYes);
			/* this will just confuse the user
			result.payloadError = {
			   id: "InstallerPayload.AlreadyInstalled",
			   className: "warning",
			   text: new LocalizedString("locPayloadSelectAlreadyInstalledDetail", "Components that are already installed cannot be selected for installation.") };
			*/
	    }
	    else if(this.constraintClass == kPolicyClass.Intrinsic && this.constraintCode == kIntrinsicPolicyCode.Conflict)
	    {
			if ( !(this.alreadyInstalledPayloadNote) )
			{
				this.alreadyInstalledPayloadNote = (new LocalizedString("locPayloadSelectConflictingInstalled", "Beta or pre-release version of this product is installed")).Translate(gSession.localization);
				this.payloadNote = this.payloadNote + ("(" + this.alreadyInstalledPayloadNote + ")" + "<br>");
				result.payloadNote = this.payloadNote;
			}
	    }
	}

	// Assemble messages for the UI
	if (this._message) 
	{
		if (this._message.note != null)
		{
			// result.payloadNote = this._message.note;
		}
		result.payloadError = this._message.detail;
	}

	return result;
}

PayloadPolicyNode.prototype.IsProtected = function(inAdobeCode)
{
   
    if ( this.payload._sessionData["ProtectedContent"]== "1")
    {
        return true;
    }
    else
        return false;
}


InstallerPayload.prototype.HasValidSerialization = function (inSession)
{
	var bRet = false;

	if (kCapsInstallStateInstalled == this.GetInstallStateForCollection(inSession.sessionCollection))
	{
		bRet = true;
	}
	else
	{
		if (inSession && inSession.properties["pers_EPIC_SERIAL"] && inSession.properties["pers_EPIC_SERIAL"].length > 23)
			bRet = true;
		else
			bRet = false;
	}

	return bRet;
}

