/**
 * Brochure.js
 * This is the root of all the objects in the brochure.
 * It interacts with the page by 3 major methods:
 * The constructor.
 * The writeInitialContent() method - called as the page is being written
 * The unLoad() method - called as part of onUnLoad.
 * All user interaction gets pumped through this object to be distributed after.
 *
 * The brochure has:
 * viewer - object that controls the main content and its interaction with the rest of the brochure.
 * template - responds to requests to generate HTML.
 * portal - The object representing the portal.
 * client - The object representing the client.
 * customizations - Loaded from brochureData to customize the template etc.
 * path - The path of the document base.
 * jsPath - The path to the directory holding JS file we're in.
 * browser - The type of browser we're being run in.
 * pages - List of page data objects looked up by name.
 * currentPageName - The name of the current page.
 * currentFloorplanName - The name of the current floorplan within the current page.
 *
 * The way it acquires the sub objects is via finding the JS file url in the brochureData structure.
 * This allows the HTML page to override which one is used before the Brochure object is created.
 * In the case of the Template, a constructor is needed.  So the name of the constructor is the same as
 * the filename.
 * So the HTML page follows the following rules:
 * 1) Include the JS files for the product (including this one)
 * 2) Include the brochureData js definition.
 * 3) Do whatever overrides are needed.
 * 4) Call the constructor of the Brochure passing in the data object and the path to the JS files.
 * 5) onLoad and onUnLoad are to wired up to the brochure object's load and unLoad functions.
 */

/*Static properties
 */
var clientJS = null;
var portalJS = null;
var templateJS = null;

/*******************************************************************************
 * Public methods.
 *******************************************************************************/

/**
 * Constructor.
 * @param jsPath - The relative or absolute URL from the document to the directory holding the javascript file we're in.
 * @param brochureDataPath - The path from this page to the brochureData
 * @param brochureData - The data structure to run our brochure.
 */
function Brochure( jsPath, brochureDataPath, brochureData )
{
	this.id = (new Date()).getTime();
		//Setup the cheap product defaults:
	this.useTemplateLayoutURL = true;
	this.layoutUrl = null;

		//Work out the path of the document we're in
	var path = document.location.href;
	var pos = path.lastIndexOf( '/' );
	if( pos > 0 )
	{
		path = path.substring( 0, pos );
	}
		//Remember the path we're in and the origin of the product JS files
	this.path = path;
	this.jsPath = jsPath;
		//Determine browser type so everything can look up here
	if( navigator.userAgent.indexOf("Opera") != -1 )
	{
		this.browser="Opera";
	}
	else if( document.all )
	{		//TODO better way to do this
		this.browser="IE";
	}
	else if( typeof( navigator.appName ) != "undefined" && navigator.appName.indexOf("Netscape") >= 0 )
	{
		this.browser="NN";
	}
	else
	{
		this.browser=null;
	}
		//Make sure all resources in brochure data are relative to the document path, not the brochureData path
	Utility.locateResources( brochureData, brochureDataPath, path );
		// Create the viewer.
	if (brochureData.viewer == 'Java')
	{
		//Perform cursory java check. More in depth testing occurs when the applet is being written to the page in JavaWrapper.js
		//This just checks that there is at least some form of java available.
		if(navigator.javaEnabled()) {
			this.viewer = new AppletWrapper(this);
		} else {
			//Check if a valid flash player is available
			var hasReqestedVersion = DetectFlashVer(7, 0, 0);
			if (hasReqestedVersion) {
				this.viewer = new FlashWrapper(this);
			} else {
				this.viewer = new NormalWrapper(this);
			}
		}
	}
	else if (brochureData.viewer == 'Flash')
	{
		//Check if a valid flash player is available
		var hasReqestedVersion = DetectFlashVer(7, 0, 0);
		if (hasReqestedVersion) {
			this.viewer = new FlashWrapper(this);
		} else {
			//Perform cursory java check. More in depth testing occurs when the applet is being written to the page in JavaWrapper.js
			//This just checks that there is at least some form of java available.
			if(navigator.javaEnabled()) {
				this.viewer = new AppletWrapper(this);
			} else {
				this.viewer = new NormalWrapper(this);
			}
		}
	}
	else
	{
			// If a viewer technology is not specified, use the Java viewer.
		this.viewer = new NormalWrapper(this);
	}

		// Load the pages.
	this.pages = new Array();
	for (var id in brochureData.pages)
	{
		var page = brochureData.pages[id];
		this.pages[id] = page;
	}

		// Load the customizations
	this.customizations = {};
	if( brochureData.customizations )
	{
		if( brochureData.customizations.viewerObject )
		{
			this.customizations.viewerObject = brochureData.customizations.viewerObject;
		}
		else
		{
			this.customizations.viewerObject = null;
		}
		if( brochureData.customizations.viewableTypes )
		{
			this.customizations.viewableTypes = brochureData.customizations.viewableTypes;
		}
		else
		{
			this.customizations.viewableTypes = null;
		}
	}
	else
	{
		this.customizations.viewerObject = [];
		this.customizations.viewableTypes = [];
	}
		//Now lets load in the client, portal and template
	if (!clientJS) {
		clientJS = brochureData.clientJS;
	}
	if (!portalJS) {
		portalJS = brochureData.portalJS;
	}
	if (!templateJS) {
		templateJS = brochureData.templateJS;
	}
	Utility.loadScript( clientJS );
	Utility.loadScript( portalJS );
	Utility.loadScript( templateJS );
		//TODO consider using Jon's generic system so we can extend this to load other object types
		// The brochure must always have a current page.
		//Set the initial page name
	this.currentPageName = brochureData.currentPageName;
		//Setup initial values
	this.client = null;
	this.portal = null;
	this.template = null;
}

/**
 * Sets the portal JS in the data structure found in the document
 */
Brochure.setPortal = function( portalJSV )
{
	portalJS = portalJSV;
}

/**
 * Sets the client JS in the data structure found in the document
 */
Brochure.setClient = function( clientJSV )
{
	clientJS = clientJSV;
}

/**
 * Sets the template JS in the data structure found in the document
 */
Brochure.setTemplate = function( templateJSV )
{
	templateJS = templateJSV;
}

/**
 * This is called in a separate tag on the web page so that the previous tags have run.
 * This primarily writes out the stylesheet tags
 */
Brochure.prototype.prepareHeadContent = function()
{
	this.templateClassName = Utility.getClassName( templateJS );
		//Since we have the clientData and portalData objects in the document.
		//We should now use them by first making the resources all link up and then using it
	Utility.locateResources( clientData, Utility.getDirectory( clientJS ), this.path );
	this.client = clientData;
	delete clientData;
	Utility.locateResources( portalData, Utility.getDirectory( portalJS ), this.path );
	this.portal = portalData;
	delete portalData;
	this.template = eval("new "+this.templateClassName+"()");
		//Tell the template to register its data
	this.template.init( Utility.getDirectory( templateJS ), this );
		// The brochureData object is only required during brochure
		// construction. After construction, the brochureData object
		// may be garbage-collected.
	delete brochureData;
}

/**
 * Called in a separate tag so that the prepareHeadContent can stream in other script tags.
 * This method writes out the style tags needed.  It also finishes the template initialisation.
 */
Brochure.prototype.finishHeadContent = function()
{
	this.template.finishInit();
		//Write out the stylesheets and remember for listening until they download
	var styleUrl = this.template.data.style;
	this.loadingStyleCSS = false;
	if( styleUrl != null )
	{
		this.loadingStyleCSS = true;
		Utility.loadStylesheet( styleUrl );
	}
	var layoutUrl;
	if( this.useTemplateLayoutURL )
	{
		layoutUrl = this.template.data.layout;
	}
	else
	{
		layoutUrl = this.layoutUrl;
	}
	this.loadingLayoutCSS = false;
	if( layoutUrl != null )
	{
			//Now start the CSS downloading
		this.loadingLayoutCSS = true;
		Utility.loadStylesheet( layoutUrl );
	}
}

/**
 * This should be called by as part of the initial page download.  NOT in onLoad.
 * At this point we should have window.clientData and window.portalData defined.
 * This method should write out the brochure DIVs.
 */
Brochure.prototype.writeInitialContent = function()
{
	var str = "";
	if (this.pages[this.currentPageName])
	{
		var pageName = this.currentPageName;
		var page = this.pages[pageName];
		this.currentFloorplanName = page.currentFloorplanName;
		this.viewer.loadPage(pageName,page);
		str += this.viewer.getInitialExternalContent();
		str += this.template.getInitialContent(pageName,page);
	}
	else
	{
		this.currentPageName = null;
		this.debug("Initial page doesn't exist!");
		str+="Error in brochureData - initial page doesn't exist!";
	}
	document.writeln(str);
}

/**
 * Called in response to the onLoad event.
 * This function sets up a callback loop that checks if the CSS files have downloaded using the <a  * href="#checkCSS">checkCSS()</a> function, and if so forwards execution to <a href="#onReady">onReady()</a>.
 */
Brochure.prototype.load = function()
{
		//Wait for stylesheets to download in a separate thread callback
	Utility.waitForCondition( this, "checkCSS", "onReady" );
}

/**
 * Callback for when the CSS downloads.  This method returns true if not waiting for the CSS to download or if certain style  * criteria are met.  Currently this is listening for #waitLayer {width:200px;height:100px;}.
 * Each of the two dimensions is defined in a separate CSS file.
 * If you replace or modify these files ensure that these styles are provided.  If not the brochure system will not continue.
 */
Brochure.prototype.checkCSS = function(count)
{
	if( this.loadingLayoutCSS )
	{
		var w = Utility.getStyle( "waitLayer", "width", "width" );
		var h = Utility.getStyle( "waitLayer", "height", "height" )
		var ans = (w == "200px");
		ans &= (h == "100px");
		return ans;
	}
	return true;
}

/**
 * Called when all external files have downloaded successfully, onLoad ran etc.
 * This launches the first rendering.
 * This is called by the Product extension to this Brochure.
 */
Brochure.prototype.onReady = function()
{
	var waitLayer = Brochure.getLayer("waitLayer");
	if( waitLayer != null )
	{
		waitLayer.style.display="none";
	}
	this.template.onReady();

	this.singleViewable();
}

/**
 * Called in response to the window's onUnload event.
 */
Brochure.prototype.unLoad = function()
{
		//Unload page
	this.unloadPage();
		//Tell the viewer to clean its part up
	this.viewer.unLoad();
		//Clear other objects
	this.viewer = null;
	this.customizations = null;
	this.pages = null;
	this.client = null;
	this.portal = null;
}

Brochure.prototype.getViewer = function()
{
	return this.viewer;
}

Brochure.prototype.getCustomizations = function()
{
	return this.customizations;
}

Brochure.prototype.getPortal = function()
{
	return this.portal;
}

Brochure.prototype.getClient = function()
{
	return this.client;
}

Brochure.prototype.getTemplate = function()
{
	return this.template;
}

/**
 * Returns an array containing all of the pages in this brochure.
 */
Brochure.prototype.getPageNames = function()
{
	var pageNames = new Array();

	for (var id in this.pages)
		pageNames.push(id);

	return pageNames;
}

/**
 * Gets the current page name.
 */
Brochure.prototype.getCurrentPageName = function()
{
	return this.currentPageName;
}

/**
 * Gets the current page.
 */
Brochure.prototype.getCurrentPage = function()
{
	return this.pages[this.currentPageName];
}

/**
 * Gets the page with the specified name.
 */
Brochure.prototype.getPage = function(pageName)
{
	return this.pages[pageName];
}

/**
 * Sets the current page to the specified value
 * unless the specified page does not exist.
 */
Brochure.prototype.changePage = function(pageName)
{
	this.unloadPage();
	if (this.pages[pageName])
	{
		this.currentPageName = pageName;
		var page = this.pages[pageName];
		this.currentFloorplanName = page.currentFloorplanName;
		this.viewer.loadPage(pageName,page);
		this.template.loadPage(pageName,page);
	}
}

/**
 * Called internally from two places
 */
Brochure.prototype.unloadPage = function()
{
	this.viewer.unloadPage();
	this.template.unloadPage();
}

/**
 * id is the index of the hotspot in the floorplan
 */
Brochure.prototype.floorPlanActionCallback = function( id )
{
	var fp = this.getCurrentFloorplan();
	if( fp == null )
	{
		return;
	}
	if( fp.HotspotsArray[id] )
	{
		var targetName = fp.HotspotsArray[id].targetName;
		if( typeof( this.getCurrentPage().floorplans[targetName] ) != "undefined" )
		{
			this.template.showFloorplan( targetName );
		}
		else if( typeof( this.getCurrentPage().viewables[targetName] ) != "undefined" )
		{
			this.viewer.showViewable( targetName );
		}
	}
}

Brochure.prototype.singleViewable = function ()
{
	this.viewableCount = 0;
	var page = this.getCurrentPage();
	var isText = false;
	var isDropdown = true;

	for (var id in page.viewables)
	{
		var viewable = page.viewables[id];
		this.viewableCount ++;
	}


	 		if(this.viewableCount == 1)
	 		{
	 			thisDropDown = document.getElementById('Drop_Down_Menu');
	 			thisDropDown.style.visibility = 'hidden';
	 			thisScrollingText = document.getElementById('Detailed_Image_Text');
	 			thisScrollingText.style.width = '552';
	 			thisScrollingText.style.left = '0';
	 			isDropdown = false;
			}

	for (var id in page.viewables)
	{
		if(page.viewables[id].description)
			isText = true
	}
	if(isText == false && isDropdown == false)
	{
		thisText = document.getElementById('Detailed_Image_Text');
		thisText.style.visibility = 'hidden';
		thisBackground = document.getElementById('User_Defined_Frame_1');
		thisBackground.style.visibility = 'hidden';
	}

}

/*
Brochure.prototype.noText = function ()
{
	var currentViewableParams = this.brochure.getCurrentPage().viewables[params];
	var tempStr = currentViewableParams.description;

	var id in brochureData.pages
}
*/
/**
 * Called by the applet or flash to tell us that the viewable with the given id is now shown
 */
Brochure.prototype.notifyViewableChanged = function( id )
{
	this.viewer.notifyViewableChanged( id );
	this.template.notifyViewableChanged( id );
}

/**
 * Callback for a button press of some sort.
 * Buttons are defined with both a <em>type</em> and an <em>id</em>.
 * Types include &quot;thumbnail&quot;, &quot;floorplan&quot; and &quot;button&quot;.
 * Based on the type, one of three actions is undertaken:
 * <UL>
 * <LI>thumbnail - the <a href="Viewer.html#showViewable">Viewer.showViewable( id )</a> method is called.
 * <LI>floorplan - the <a href="Template.html#showFloorplan">Template.showFloorplan( id )</a> method is called.
 * <LI>button - the <a href="Template.html#buttonClicked">Template.buttonClicked( id )</a> method is called.
 * </UL>
 */
Brochure.prototype.buttonClicked = function( type, id )
{
	this.debug( "Brochure.buttonClicked( "+type+", "+id+" ) enter" );
	switch( type )
	{
	case 'thumbnail':
		this.viewer.showViewable( id );
		break;
	case 'floorplan':
		this.template.showFloorplan( id );
		break;
	case 'button':
		this.template.buttonClicked( id );
		break;
	default:
		this.debug("unknown button type clicked '"+type+"' id='"+id+"'");
	}
	this.debug("Brochure.buttonClicked( "+type+", "+id+" ) exit");
}

Brochure.prototype.mouseOverHandler = function( type, id )
{
	this.template.getButton( type, id ).mouseOverHandler();
}

Brochure.prototype.mouseOutHandler = function( type, id )
{
	this.template.getButton( type, id ).mouseOutHandler();
}

Brochure.prototype.getCurrentFloorplan = function()
{
	var page = this.getCurrentPage();
	if( page.floorplans )
	{
		if( typeof( page.floorplans[this.currentFloorplanName] ) != "undefined" )
		{
			return page.floorplans[this.currentFloorplanName];
		}
	}
	return null;
}

Brochure.prototype.setFloorplan = function(floorplanName)
{
	var page = this.getCurrentPage();
	if( page.floorplans && typeof( page.floorplans[floorplanName] ) != "undefined" )
	{
		this.currentFloorplanName = floorplanName;
	}
}

/**
 * Multi-browser way to get a DIV tag object TODO ensure it works with NN etc
 */
Brochure.getLayer = function( name )
{
	return document.getElementById( name );
}

/**
 * Due to some bugs in some browsers when we set innerHTML=<something new> and it doesn't really unload the
 * old content.  We have a way to force it to clear before setting the new value.
 */
Brochure.unloadDiv = function( divTag )
{
	if( divTag != null )
	{
		divTag.innerHTML = "";
	}
}

/**
 * Render one layer only.  This is called to render it again because it is already rendered in the overall rendering.
 * It can be passed parameters which are passed to the format method.
 */
Brochure.prototype.reRenderLayer = function( name, params )
{
	var layer = Brochure.getLayer( name );
	if( layer != null )
	{
		this.template.reRenderLayer( name, layer, params );
	}
}

/**
 * Shows the specified layer. If the layer overlaps with other layers,
 * the visibility of the other layers is set to hidden.
 */



 Brochure.prototype.showLayer = function(layerName)
 {

	// cross browser getStyle
	// http://www.robertnyman.com/2006/04/24/get-the-rendered-style-of-an-element/
	// supports
	// - IE 5.5+
	// - Firefox
	// - Safari
	// - Opera 8.5
	// does not support IE5.0

	//TODO use/update Utility.getStyleFromTag()
	function getStyle(oElm, strCssRule){
	    var strValue = "";
	    if(document.defaultView && document.defaultView.getComputedStyle){
		strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
	    }
	    else if(oElm.currentStyle){
		strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){
		    return p1.toUpperCase();
		});
		strValue = oElm.currentStyle[strCssRule];
	    }
	    return strValue;
	}

 	var layer = Brochure.getLayer(layerName);
 	if( layer == null )
 	{		//Wasn't there
 		return;
 	}
 		// Hide any layers that overlap with the layer.
 	var overlappingLayerNames = this.getOverlappingLayerNames(layerName);

 	var highestZ = 0;
 	var tempZ = 0;
 	var tStr = "";

 	for (var i = 0; i < overlappingLayerNames.length; i++)
 	{
 			//instead of hiding the other layer, we want to get the zIndex value and add one to it
 		//Brochure.getLayer(overlappingLayerNames[i]).style.visibility = 'hidden';
 		//tempZ = Brochure.getLayer(overlappingLayerNames[i]).style.zIndex;
 		var ber = document.getElementById(overlappingLayerNames[i]);
 		tempZ = getStyle(ber, "z-index");
 		tStr += overlappingLayerNames[i] + ".style.zindex:" + tempZ + "\n";
 		if (tempZ > highestZ) {
 			highestZ = tempZ;
 		}
 	}
 	highestZ ++;
 		// Show the layer.
 	layer.style.visibility = 'visible';
 	layer.style.zIndex = highestZ;
}

/* old version
Brochure.prototype.showLayer = function(layerName)
{
	var layer = Brochure.getLayer(layerName);
	if( layer == null )
	{		//Wasn't there
		return;
	}
		// Hide any layers that overlap with the layer.
	var overlappingLayerNames = this.getOverlappingLayerNames(layerName);

	for (var i = 0; i < overlappingLayerNames.length; i++)
	{
		Brochure.getLayer(overlappingLayerNames[i]).style.visibility = 'hidden';
	}

		// Show the layer.
	layer.style.visibility = 'visible';
}
*/

/**
 * Returns an array containing the names of all of the layers
 * that overlap with the layer whose name is provided as a parameter.
 */
Brochure.prototype.getOverlappingLayerNames = function(layerName)
{
	var overlappingLayerNames = new Array();
	var layers = this.template.getLayers();
	var layer = layers[layerName];

	for (var currentLayerName in layers)
	{
		var currentLayer = layers[currentLayerName];

		if (currentLayerName != layerName &&
			currentLayer.top < layer.bottom && currentLayer.bottom > layer.top &&
			currentLayer.left < layer.right && currentLayer.right > layer.left)
		{
			overlappingLayerNames.push(currentLayerName);
		}
	}

	return overlappingLayerNames;
}


/**
 * Returns a boolean informing if the given layer is on top of all other overlapping layers
 * true, its on top, false it is not on top, null if no such layer
 */

 Brochure.prototype.isLayerOnTop = function(layerName)
 {

	// cross browser getStyle
	// http://www.robertnyman.com/2006/04/24/get-the-rendered-style-of-an-element/
	// supports
	// - IE 5.5+
	// - Firefox
	// - Safari
	// - Opera 8.5
	// does not support IE5.0

	//TODO use/update Utility.getStyleFromTag()
	function getStyle(oElm, strCssRule){
	    var strValue = "";
	    if(document.defaultView && document.defaultView.getComputedStyle){
		strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
	    }
	    else if(oElm.currentStyle){
		strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){
		    return p1.toUpperCase();
		});
		strValue = oElm.currentStyle[strCssRule];
	    }
	    return strValue;
	}

 	var layer = Brochure.getLayer(layerName);
 	if( layer == null )
 	{		//Wasn't there
 		return null;
 	}
 		// Get overlapping layers.
 	var overlappingLayerNames = this.getOverlappingLayerNames(layerName);

 	var highestZ = 0;
 	var tempZ = 0;
 	var tStr = "";

 	for (var i = 0; i < overlappingLayerNames.length; i++)
 	{
 			//instead of hiding the other layer, we want to get the zIndex value and add one to it
 		//Brochure.getLayer(overlappingLayerNames[i]).style.visibility = 'hidden';
 		//tempZ = Brochure.getLayer(overlappingLayerNames[i]).style.zIndex;
 		var ber = document.getElementById(overlappingLayerNames[i]);
 		tempZ = getStyle(ber, "z-index");
 		tStr += overlappingLayerNames[i] + ".style.zindex:" + tempZ + "\n";
 		if (tempZ > highestZ) {
 			highestZ = tempZ;
 		}
 	}
	var concernedLayer = document.getElementById(layerName);
	tempZ = getStyle(concernedLayer, "z-index");
 	if (tempZ > highestZ ) return true;
 	else return false;
}

Brochure.prototype.debug = function(msg)
{
	var tag = document.getElementById('debugTA');
	if( tag )
	{
		tag.value+=msg+'\n';
		if( !this.debugText || this.debugText != "" )
		{
			this.debugText = "";
		}
	}
	else
	{
		if( !this.debugText )
		{
			this.debugText = "";
		}
		this.debugText+=msg+'\n';
	}
}

Brochure.prototype.isIE = function()
{
	return this.browser == "IE";
}

Brochure.prototype.isNN = function()
{
	return this.browser == "NN";
}

Brochure.prototype.isOpera = function()
{
	return this.browser == "Opera";
}

