/*!
**********************************************************************
@file WizardControl.js

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

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

/**
WizardControl constructor.  This constructs a universe of WizardPages
based on the names passed in.  A WizardPage is constructed by loading
and eval()ing a javascript file of the name pages/{pagename}/{pagename}.js
which must return a WizardPage object.  It is up to the page js file
to call an appropriate WizardPage constructor to load the page resources.

@param inPageList an array of strings specifying page names.
*/
function WizardControl(inInstallerSession, inPageList, inTextColor /* = null */)
{
	/** The session object. */
	this.session = inInstallerSession;

	/** Workflow state */
	this.loadList = new Array();
	this.pageUniverse = new Array();
	this.pageHistory = new Array();
	this.currentPageIndex = 0;
	this.currentPage = null;
	this.focusItem = null;
	this.tabset = null;
	
	this.tabset = null;
	this.tabWidth = 0;
	this.activeTab = null;
	
	/** Optional text color */
	this.textColor = inTextColor;

	if (null == inPageList || inPageList.length <= 0)
	{
		throw "Invalid page set. At least 1 page must be provided";	
	}

	var workflowMode=inInstallerSession.getWorkflowMode();

	// Attempt to load each wizard page, just the javascript class definition
	for (var pageIndex = 0; pageIndex < inPageList.length; pageIndex++)
	{
		var pageName = inPageList[pageIndex];
		var pagePath = this.session.GetResourcesPath() + "/pages/" + workflowMode + "/" + pageName + "/" + pageName + ".js";
 		
 		var defaultProperties = inInstallerSession.GetDefaultProperties();
 		var platformName = defaultProperties["platform"];
 		if ("Win32" == platformName)
 		{
 			inInstallerSession.LogDebug("updating path to Win32");
			pagePath = pagePath.replace(/\/+/g, "\\");
			pagePath = pagePath.replace(/\\{2,}/g, "\\");
 		}
		this.session.LogDebug("WizardControl: loading page \"" + pageName + "\" from " + pagePath);

		try
		{
			var pageDataObj = this.session.LoadFile(pagePath);
			var pageData = pageDataObj.data;
			
			if (pageData && pageData != '')
			{
				// Eval the file's code.  This should return a string
				// identifying the class defined in the file.
				var pageClass = eval(pageData);
				if ("string" != typeof pageClass)
					throw pageName + " did not return a class name string";

				// Construct a new object
				var newPage = eval("new " + pageClass + "(this)");
				if (!newPage)
					throw pageName + " constructor failed";

				// Hook it into the workflow...if it wants to be hooked in.
				newPage.SetController(this);
				if (newPage.isActive())
				{
					this.pageUniverse.push(newPage);
				}
				else
				{
					newPage = null;
					this.session.LogDebug("WizardControl: page " + pageName + " declined to participate in the workflow");
				}
			}
			else
			{
				this.session.LogDebug("WizardControl: no content in " + pagePath);		
			}
		}
		catch (e)
		{
			this.session.LogError("Unable to load file" + pageName + " from " + pagePath);
			this.session.LogError(e);
		}
	}

	// Ask each participating page to load its resources
	for (var pageIndex = 0; pageIndex < this.pageUniverse.length; pageIndex++)
	{
		this.pageUniverse[pageIndex].LoadResources();
	}
	
	this.CreateNavUI();

	// Reveal the nav buttons
	var navDiv = document.getElementById("navButtons");
	if (navDiv && navDiv.style)
	{
		navDiv.style.visibility = "visible";
	}
	// Accessibility is for Windows only - however it can be supported on mac also.
	if (this.session.systemInfo.Windows) {
		this.WAM = new WizardAccessibilityManager1(this.session.IsJawsRunning());
		this.WAM.RunPageControl();
	}

	/* NEW PLACER FOR RUNNING SYSTEM CHECK WITHOUT UI*/
	
	var systemCheck_pageName = "systemCheck_wp";
	var systemCheck_newPage = new systemCheck_wp(this);
	if (!systemCheck_newPage)
		throw systemCheck_pageName + " constructor failed";
	
	systemCheck_newPage.SetController(this);
	
	//Performance fix: Do not run system check on uninstall startup
	if(workflowMode != kWorkflowModeUninstall)
    {
	    systemCheck_newPage.OnFirstShow();
	}
	
	if(systemCheck_newPage.alreadyDidExit == false)
	{
	    systemCheck_newPage.onNext();
	    this.systemCheck_newPage=systemCheck_newPage;

	    // Show the first one
	    this.ShowPage(this.CheckSwitchTo(this.pageUniverse[0]));
    }
}


/**
Display the specified WizardPage.
*/
WizardControl.prototype.ShowPage = function(inWizardPage)
{
	if (inWizardPage)
	{
		var displayElement = document.getElementById("contentwrap");
		if (displayElement)
		{
			// Clean out the old content, inject the new.
			if (inWizardPage.GetUIDOM())
			{
		        this.session.LogDebug("Showing page " + inWizardPage.pageName);

				while (displayElement.hasChildNodes())
					displayElement.removeChild(displayElement.lastChild);
				displayElement.appendChild(inWizardPage.GetUIDOM());

				// Record where we are in the sequence
				this.currentPageIndex = this.GetIndexByPage(inWizardPage);

				// Update the nav
				this.SetNavUI(this.currentPageIndex);

				// Remove all tab elements of the current page from WAM
				if(this.WAM)
					this.WAM.RemoveAllElements();
					
				// Invoke the wizard
				inWizardPage.onShow();
				
				window.setTimeout(
									function()
									{
										document.getElementById("container").style.display = 'none';
										document.getElementById("container").style.display = 'block';
									}, 0 
								);
				if(this.WAM)
					this.WAM.AnnouncePageMeesage();

				return true;
			}
		}
	}
	return false;
}


/**
Get the next default-order page.  With no argument, the page
following the currently displayed page.  With a page WizardPage
argument, the page following the specified page.
*/
WizardControl.prototype.GetNextPage = function()
{
	var nextPageIndex = this.currentPageIndex + 1;
	this.session.LogDebug("currentPageIndex is " + this.currentPageIndex);
	if (arguments.length > 0)
	{
		currentPage = arguments[0];
		this.session.LogDebug("GetNextPage was passed a starter page of " + currentPage.pageName);
		nextPageIndex = this.GetIndexByPage(currentPage) + 1;
	}
	this.session.LogDebug("nextPageIndex is " + nextPageIndex);
	var nextPage = this.GetPageByIndex(nextPageIndex);
	return nextPage;
}


WizardControl.prototype.GetCurrentPage = function()
{
	return this.GetPageByIndex(this.currentPageIndex);
}


/**
Determine if the specified WizardPage wants to participate in the
workflow at this point in time.  This routine recursively walks
the workflow returning the next page that wants to participate.
*/
WizardControl.prototype.CheckSwitchTo = function(inWizardPage)
{
	this.session.LogDebug("CheckSwitchTo for " + inWizardPage.pageName);
	if (inWizardPage)
	{
		if (inWizardPage.onSwitchTo())
		{
			this.session.LogDebug(inWizardPage.pageName + " will participate in the workflow");
			return inWizardPage;
		}
		this.session.LogDebug(inWizardPage.pageName + " has declined the invitation to the workflow");
		
		var nextPage = this.GetNextPage(inWizardPage);
		this.session.LogDebug("The next page is " + nextPage.pageName);
		return this.CheckSwitchTo(nextPage);
	}
	this.session.LogDebug("CheckSwitchTo at the end of the road " + inWizardPage);
	return null;
}


/**
Return the WizardPage subclass name at the specified index.

@param inPageIndex index of the page to retrieve
@return a WizardPage object or null if the index is out of range.
*/
WizardControl.prototype.GetPageByIndex = function(inPageIndex)
{
	if (inPageIndex >= 0 && inPageIndex < this.pageUniverse.length)
	{
		return this.pageUniverse[inPageIndex];
	}
	return null;
}


/**
Return the index of the specified WizardPage object.

@param inWizardPage a WizardPage object
@return integer sequence of the page or -1 if the specified WizardPage cannot be found.
*/
WizardControl.prototype.GetIndexByPage = function(inWizardPage)
{
	if (inWizardPage)
	{
		for (var i = 0; i < this.pageUniverse.length; i++)
		{
			if (this.pageUniverse[i] && inWizardPage.pageName == this.pageUniverse[i].pageName)
			{
				return i;
			}
		}
	}
	return -1;
}


/**
Navigate to next page the user should interact with.  Forward
movement will be blocked by the onNext() handler of the current
page returns false.
*/
WizardControl.prototype.NavNext = function()
{
	var retVal = false;

	// Initialize the mutex
	if (null == this._NextMutex)
		this._NextMutex = { set: false };
	var mutex = this._NextMutex;

	// Test it
	if (true == mutex.set)
		return retVal;

	// Set it
	mutex.set = true;

	// function to clear it
	var clearMutex = function()
	{
		// theoretically the timeout should be 0, but that doesn't appear reliable
		setTimeout(function() { mutex.set = false }, 110);
	};

	// Unless the current page wants to block...
	var currentPage = this.GetCurrentPage();
	if (!(currentPage && !currentPage.onNext()))
	{
		// Get the next page
		var nextPageIndex = this.currentPageIndex + 1;
		var nextPage = this.CheckSwitchTo(this.GetPageByIndex(nextPageIndex));

		if (nextPage)
		{
			// Set up the history so ShowPage can manage the back button
			this.pageHistory.push(currentPage);
			if (!this.ShowPage(nextPage))
			{
				this.pageHistory.pop();
			}
			else
			{
				retVal = true;
			}
		}
	}

	clearMutex();
	return retVal;
}


/**
Navigate to page the user most recently interacted with.
*/
WizardControl.prototype.NavBack = function()
{
	// See if the current page wants to block
	var currentPage = this.GetCurrentPage();
	if (currentPage && !currentPage.onBack())
	{
		return false;
	}

	if (this.pageHistory.length > 0)
	{
		var backPage = this.pageHistory.pop();
		if (backPage && this.ShowPage(backPage))
		{
			this.currentPageIndex = this.GetIndexByPage(backPage);
			return true;
		}
	}
	return false;
}


/**
Cancel/Quit the installer.  Individual pages can hook into
this by overriding the default an onCancel() method.
*/
WizardControl.prototype.NavCancel = function()
{
	var currentPage = this.GetCurrentPage();
	if (currentPage && currentPage.onCancel())
	{
		this.session.UIExitDialog();
	}
}

WizardControl.prototype.NavQuit = function()
{
	this.session.UIExitDialog();
}

/**
Traverses the page history and default properties to find the "current"
value of the requested property.  If more than one page in the history
publishes the property, the most recent one in the historys value will
be used. If the property is unset and not in the default properties,
null will be returned.
*/
WizardControl.prototype.GetPropertyValue = function(propertyName)
{
	var didSetResult = false;
	var result = null;
	
	// only walk history of page properties if the page
	// history has been setup, otherwise fall through
	// to the default property set
	if (this.pageHistory)
	{
		// look for the property in the history pages from
		// most recent to oldest
		for (var pageIndex = this.pageHistory.length - 1;
			 pageIndex >= 0;
			 --pageIndex)
		{
			var wp = this.pageHistory[pageIndex];
			if (wp)
			{
				var pageProperties = wp.getProperties();
				for (var p in pageProperties)
				{
					if (p == propertyName)
					{
						result = pageProperties[propertyName];
						didSetResult = true;
						break;
					}
				}
			}
		}
	}
	
	// if the property wasn't in the page history, look in the
	// default properties
	if (!didSetResult)
	{
		result = this.session.defaultProperties[propertyName];
	}
	
	return result;
}

/*
Traverse the navigation history stack and publish the properties for pickup
by the payloads.
*/
WizardControl.prototype.CommitProperties = function()
{
	for (var pageIndex = 0; pageIndex < this.pageHistory.length; ++pageIndex)
	{
		var wp = this.pageHistory[pageIndex];
		if (wp)
		{
			var pageProperties = wp.getProperties();
			for (var p in pageProperties)
			{
				if (pageProperties[p])
				{
					//var foo = window.external.AMT_SetSessionProperty(p, pageProperties[p]);
					if (p != "pers_EPIC_SERIAL") {
						this.session.LogDebug("Committing " + p + "=" + pageProperties[p]);
					}
					else {
						this.session.LogDebug("Committing " + p);
					}
					this.session.properties[p] = pageProperties[p];
				}
			}
		}
	}
	this.session.LogDebug("New session properties");
	for (var key in this.session.properties)
	{
		if (key != "pers_EPIC_SERIAL") {
			this.session.LogDebug(key + "=" + this.session.properties[key]);
		}
	}
}


/**
Handle keyup for pages by calling the page's keyup handler, if any
*/
WizardControl.prototype.HandleKeyUp = function(ev)
{
	if (ev.keyCode == 27)
	{
		this.NavCancel();
	}
	else
	{
		var currentPage = this.GetCurrentPage();
	
		if (currentPage && currentPage.HandleKeyUp)
			currentPage.HandleKeyUp(ev);
	}

	return false;
}


/**
Handle keyup for main page
*/
WizardControl.prototype.HandleKeyDown = function(ev)
{
	if (ev.altKey && ev.keyCode == 115)
	{
		this.NavCancel();
	}

	return true;
}


/**
Handle blur behavior; currently just reset the focus item
*/
WizardControl.prototype.HandleBlur = function(ev)
{
	this.focusItem = null;
}


/**
Handle submit behavior: user enter to click the button with focus
*/
WizardControl.prototype.HandleSubmit = function()
{
	if (this.focusItem && this.focusItem.type == "button")
		this.focusItem.onclick();
}


/**
Set the focus object for the WizardControl; see HandleSubmit above 
*/
WizardControl.prototype.SetFocus = function(inInput)
{
	this.focusItem = inInput;
	
	return true;
}


/**
Make the navigation.
*/
WizardControl.prototype.CreateNavUI = function()
{
	this.tabset = document.getElementById("tabsetContainer");
	if (this.tabset)
	{
		while (this.tabset.hasChildNodes())
			this.tabset.removeChild(this.tabset.lastChild);
			
        var tabsetImage = new WizardImage1(kImgTabset, "tabset", "tabset", this.tabset);
        
		var tabsetWidth = tabsetImage.GetRawImage().offsetWidth;
		this.tabWidth = (tabsetWidth/(this.pageUniverse.length));
	}
	
	var leftPos = 0;
	
	for (var p = 0;  p < this.pageUniverse.length; p++)
	{
		// If the WizardPage doesn't have a localization, the GetNavDisplayName() will likely blow up.
		// FIXME: this knowledge of WizardPage is a bit too intimate
		if (!this.pageUniverse[p].localization) 
			continue; 

		// Postioning and sizing of the tabs
		
		if (null != this.pageUniverse[p].GetNavDisplayName())
		{
		    var label = document.createElement("span");
		    label.style.width = this.tabWidth + "px";
		    label.style["max-width"] = this.tabWidth + "px";
		    label.style.left = leftPos + "px";
		    label.innerHTML = this.pageUniverse[p].GetNavDisplayName();
		    label.className = "tabText";
			this.tabset.appendChild(label);
			
		    leftPos = leftPos + this.tabWidth;
		}
	}
    this.activeTab = new WizardImage1(kImgActiveTab, "activeTabImg", "activeTabImg", this.tabset);
}

/**
Upadte the navigation bar to reflect the current page.
*/
WizardControl.prototype.SetNavUI = function(inCurrentPageIndex)
{
	var leftPos = 0;
	var tabNum = 0;

    for(var cn = 0; cn < this.tabset.childNodes.length; cn++)
    {
        var currentTab = this.tabset.childNodes[cn]
        var tabClass = currentTab.className;
        // Only look at tabs and active tabs to change their properties
        if(tabClass == "tabText")
        {
            if(inCurrentPageIndex == tabNum)
            {
                this.tabset.childNodes[cn].className = "activetabText";
                this.activeTab.SetPos(currentTab.style.left, null);
                this.activeTab.SetWidth(currentTab.style.width);
           }
            tabNum++;
        }
        else if(tabClass == "activetabText")
        {
            if(inCurrentPageIndex != tabNum)
                currentTab.className = "tabText";
        
            tabNum++;
        }
    }
}

