/**
 * SeedPaintball web Javascript support functions.
 * 
 * Copyright (c) 2009 SeedPaintball. All Rights Reserved.
 */

/**
 * Class: SeedPaintball.Site
 * Main site class, contains functions to create and manage content
 */
SeedPaintball.Site = new Class( {
	Implements: Events,

	/**
	 * Property: m_config
	 * {Object}
	 *    Configuration for this instance
	 */
	m_config: null,

	/**
	 * Property: m_history
	 * {<HistoryManager>}
	 *    Reference to HistoryManager instace
	 */
	m_history: null,

	/**
	 * Property: m_parent
	 * {Object}
	 *    Parent DOM element for content
	 */
	m_parent: null,

	/**
	 * Property: m_loadingIndicator
	 * {Object}
	 *    Loading indicator DOM element
	 */
	m_loadingIndicator: null,

	/**
	 * Property: m_topSwitcher
	 * {<SeedPaintball.ImageSwitcher>} Top area image switcher instance
	 */
	m_topSwitcher: null,

	/**
	 * Constructor: {<SeedPaintball.Site>}
	 * Create a new instance of SeedPaintball.Site
	 * 
	 * @param {Object} config
	 * @return {<SeedPaintball.Site>} site
	 */
	initialize: function( config ) {
		if( !$defined( config ) || !$defined( config.content ) ) {
			return;
		}
		this.setConfig( config );

		var parent = $( config.content );
		if( !$defined( parent ) ) {
			alert( "Error occured while loading site. "
				+ "Please try refreshing this page." );
			return;
		}
		parent.empty();
		this.setParent( parent );

		// Create HistoryManager instance
		var history = new HistoryManager();
		history.addEvent( "onHistoryChange", function( event ) {
				var data = this.getUrlData();
				var mode = data[ 1 ];
				this._menuSliding = false;
				this._switcherHiding = false;
				if( mode == "content" ) {
					this.slideMenu( "up" );
				} else if( mode == "main" ) {
					this.slideMenu( "down" );
				}
				this.loadPage();
			}.bind( this ) );
		this.setHistory( history );

		// Create dynamic components
		this.createComponents();

		// Listen to loadPage event
		$( window ).addEvent( "loadPage", function( hash ) {
				this.getHistory().addState( hash );
				this.loadPage();
			}.bind( this ) );

		// Move to 'default' if no location, else show current
		if( !$defined( this.getHistory().getCurrentHash() )
			|| !this.getHistory().getCurrentHash() ) {
			this.getHistory().addState( this.getConfig().defaultPage );
			this.loadPage( null, true );
		} else {
			this.loadPage( null, true );
		}
		this.setPageEvents();
	},

	/**
	 * Function: setPageEvents
	 * Set global events for the page
	 */
	setPageEvents: function() {
		this.addEvent( "Seed.PageChanged", function() {
				var data = this.getUrlData();
				var mode = data[ 1 ];
				if( mode == "content" ) {
					this.slideMenu( "up" );
				} else if( mode == "main" ) {
					this.slideMenu( "down" );
				}
			}.bind( this ) );
		window._buttonHidden = false;
		window.addEvent( "resize", function() {
			var config = this.getConfig();
			if( window.getWidth() < config.topImageWidth ) {
				if( !window._buttonHidden ) {
					$( config.topContent.leftButton ).setStyle( "display",
						"none"
					);
					$( config.topContent.rightButton ).setStyle( "display",
						"none"
					);
					$( config.topContent.logo ).setStyle( "display", "none" );
					window._buttonHidden = true;
				}
				return;
			}
			if( window._buttonHidden ) {
				$( config.topContent.leftButton ).setStyle( "display", "block" );
				$( config.topContent.rightButton )
					.setStyle( "display", "block" );
				$( config.topContent.logo ).setStyle( "display", "block" );
				window._buttonHidden = false;
			}
		}.bind( this )
		);
	},

	/**
	 * Function: createComponents
	 * Create the dynamic components
	 */
	createComponents: function() {
		this._menuSliding = false;
		this._switcherHiding = false;

		var components = this.getConfig().topContent;
		// Link for logo
		$( components.logo ).addEvent( "click", function() {
				this.getHistory().addState( this.getConfig().defaultPage );
				this.loadPage();
			}.bind( this ) );
		$( components.logo ).addEvent( "mouseover", function( event ) {
				event.target.setStyle( "cursor", "pointer" );
			}.bind( this ) );

		// Create top area switcher
		this.m_topSwitcher = new SeedPaintball.ImageSwitcher( {
				"container": components.content,
				"images": SeedPaintball.Config.topImages,
				"inactiveImage": SeedPaintball.Config.topInactiveImage,
				"leftButton": SeedPaintball.Config.topContent.leftButton,
				"rightButton": SeedPaintball.Config.topContent.rightButton
			} );

		// Menu, loaded dynamically
		this.loadPage( this.getConfig().menu );
	},

	/**
	 * Function: slideMenu
	 * Slide menubar up or down and resize bottom component to match
	 * 
	 * @param {String} direction
	 */
	slideMenu: function( direction, stop ) {
		if( this._menuSliding ) {
			return;
		}
		var div = $( this.getConfig().topContent.content );
		switch( direction ) {
			case "up":
				if( div.getStyle( "height" ) == "40px" ) {
					return;
				}
				window.scrollTo( 0, 0 );
				div.get( 'tween', {
						property: 'height',
						duration: '1000'
					} ).start( 360, 40 );
				this.m_topSwitcher.toggleImageVisibility( "hide" );
				break;
			default:
				if( div.getStyle( "height" ) == "360px" ) {
					return;
				}
				div.get( 'tween', {
						property: 'height',
						duration: '500'
					} ).start( 40, 360 );
				this.m_topSwitcher.toggleImageVisibility( "show" );
		}
		this._menuSliding = true;
		setTimeout( function() {
				// Disable menu sliding. do not set under value of 'duration'
				// above and in setImage
				this._menuSliding = false;
			}.bind( this ), 500 );
	},

	/**
	 * Function: injectPage
	 * Inject a page dynamically from the backend. Use loadPage() below to
	 * add history events and page handling.
	 * 
	 * @param {String} url
	 * @param {String} targetId ID of target DOM node
	 */
	injectPage: function( url, targetId ) {
		if( !$defined( url ) || !$defined( targetId ) ) {
			return;
		}
		var target = $( targetId );
		if( !$defined( target ) ) {
			return;
		}
		var htmlRequest = new Request.HTML( {
				url: this.getConfig().url + "" + url,
				method: "get",
				onFailure: function( error ) {
					target.set( "html", "An error occured while trying to "
							+ " load the requested page. " + "<pre>"
							+ error.responseText + "</pre>" );
					this.hideLoading();
				}.bind( this ),
				onSuccess: function( html, responseElements, responseHTML,
					responseJavaScript ) {
					target.empty();
					target.adopt( html );
					this.hideLoading();
				}.bind( this )
			} );
		htmlRequest.send();
	},

	/**
	 * Function: loadPage
	 * Load a page dynamically from the backend.
	 * 
	 * @param {String} url
	 * @param {Boolean} first
	 */
	loadPage: function( url, first ) {
		this.showLoading();
		if( !$defined( url ) ) {
			url = this.getHistory().getCurrentHash();
		}
		if( !$defined( url ) ) {
			this.hideLoading();
			return;
		}

		// Add to GA if pageTracker available
		// TODO: we might want to clean up the url for GA "a bit"
		if( $defined( pageTracker ) ) {
			try {
				pageTracker._trackPageview( url );
			} catch( e ) {
				// silent error
			}
		}

		// Logic here is:
		// split the URL with ; to get components
		// - first component is the URL
		// - second component is the mode
		// - third component is the content mode
		// - any additional components are for subpage processing
		var data = this.getUrlData( url );
		var type = data[ 0 ];
		var mode = data[ 1 ];
		url = url.replace( /\;.*/, "" );

		switch( type ) {
			case "html":
				var htmlRequest = new Request.HTML( {
						url: this.getConfig().url + "" + url,
						method: "get",
						onFailure: function( error ) {
							this.setError( "An error occured while trying to "
								+ " load the requested page. " + "<pre>"
								+ error.responseText + "</pre>" );
							this.hideLoading();
						}.bind( this ),
						onSuccess: function( html, responseElements,
							responseHTML, responseJavaScript ) {
							this.getParent().empty();
							// Inject the new DOM elements
							// into the results div.
							this.getParent().adopt( html );
							this.hideLoading();
							this.fireEvent( "Seed.PageChanged", mode );
							window.scrollTo( 0, 0 );
						}.bind( this )
					} );
				htmlRequest.send();
				break;
			case "json":
				var jsonRequest = new Request.JSON( {
					url: this.getConfig().url + "" + url,
					secure: false,
					onFailure: function( error ) {
						this.setError( "An error occured while trying to "
							+ " load the requested page. " + "<pre>"
							+ error.responseText + "</pre>" );
						this.hideLoading();
					}.bind( this ),
					onSuccess: function( result, resultText ) {
						if( !$defined( result ) || !$defined( result.onLoad ) ) {
							this.setError( "An error occured while trying to "
								+ " load the requested page. " );
							this.hideLoading();
							return;
						}
						if( $defined( result.onLoad ) ) {
							try {
								result.onLoad( this );
								window.scrollTo( 0, 0 );
							} catch( e ) {
								this
									.setError( "An error occured while trying to "
										+ " load the requested page. "
										+ "<pre>( "
										+ this.dumpObj( e )
										+ "</pre>)" );
								this.hideLoading();
								return;
							}
						}
						this.fireEvent( "Seed.PageChanged", mode );
					}.bind( this )
				}
				);
				jsonRequest.get();
				break;
			default:
				this.setError( "An error has occured while "
					+ "trying to load the requested page. "
					+ "(Unknown request type '" + type + "')" );
		}
	},

	/**
	 * Function: setError
	 * Set an error text in the page content area
	 * 
	 * @param {String} error
	 */
	setError: function( error ) {
		this.getParent().empty();
		this.getParent().set(
			"html",
			"<div class='titleContent'>" + " Error!</div> " + "<br/><br/>"
				+ error + "<br/><br/>"
		);
	},

	/**
	 * Function: showLoading
	 * Show the loading indicator in the main content area
	 */
	showLoading: function() {
		this.getLoadingIndicator().setStyle( "display", "block" );
	},

	/**
	 * Function: hideLoading
	 * Hide the loading indicator
	 */
	hideLoading: function() {
		this.getLoadingIndicator().destroy();
	},

	/**
	 * Function: getLoadingIndicator
	 * Get the loading indicator div element
	 * 
	 * @return {Object} div
	 */
	getLoadingIndicator: function() {
		if( !$defined( this.m_loadingIndicator )
			|| !$defined( $( this.m_loadingIndicator ) ) ) {
			var div = new Element( "div", {
					"class": "loadingIndicator",
					"html": "Please wait while loading requested page.",
					styles: {
						display: "block",
						"z-index": "10"
					}
				} );
			this.getParent().grab( div );
			this.m_loadingIndicator = div;
		}
		return this.m_loadingIndicator;
	},

	/**
	 * Function: dumpObj
	 * Dump key-value pairs of an object, array or string
	* 
	* @param {Object} obj
	 */
	dumpObj: function( obj ) {
		var string = "";
		try {
			switch( $type( obj ) ) {
				case "array":
					for( var i = 0; i < obj.length; i++ ) {
						var thisString = "";
						try {
							thisString = this.dumpObj( obj[ i ] );
						} catch( e ) {
							// Silent
						}
						string = string + "\n" + thisString;
					}
					break;
				case "object":
					for( var id in obj ) {
						var thisString = "";
						try {
							thisString = this.dumpObj( obj[ id ] );
						} catch( e ) {
							// Silent
						}
						string = string + "\n" + id + ": " + thisString;
					}
					break;
				default:
					string = string + "\n" + obj;
			}
			return string;
		} catch( e ) {
			// Could not be dumpped
			return "";
		}
	},

	/**
	 * Function: setHistory
	 * Set reference to HistoryManager
	 * 
	 * @param {<HistoryManager>} history
	 */
	setHistory: function( history ) {
		this.m_history = history;
	},

	/**
	 * Function: getHistory
	 * Get reference to HistoryManager
	 * 
	 * @return {<HistoryManager>} history
	 */
	getHistory: function() {
		return this.m_history;
	},

	/**
	 * Function: setConfig
	 * Set reference to configuration object
	 * 
	 * @param {Object} config
	 */
	setConfig: function( config ) {
		this.m_config = config;
	},

	/**
	 * Function: getConfig
	 * Get reference to configuration object
	 * 
	 * @return {Object} config
	 */
	getConfig: function() {
		return this.m_config;
	},

	/**
	 * Function: setParent
	 * Set reference to parent element
	 * 
	 * @param {Object} parent
	 */
	setParent: function( parent ) {
		this.m_parent = parent;
	},

	/**
	 * Function: getParent
	 * Get reference to parent object
	 * 
	 * @return {Object} parent
	 */
	getParent: function() {
		return this.m_parent;
	},

	/**
	 * Function: getUrlData
	 * Get data from the current URL
	 * 
	 * @param {String} url
	 * @return {Array} data
	 */
	getUrlData: function( url ) {
		if( !$defined( url ) ) {
			url = this.getHistory().getCurrentHash();
		}
		var data = url.replace( /.*\.(html|json)\;/i, "" ).split( /;/ );
		return data;
	}
	// ------------------------------class_end----------------------------------
}
);

/**
 * Class: ContentFlow
 * Create an image based 'coverflow' like chooser component
 */
SeedPaintball.ContentFlow = new Class( {
	/**
	 * Property: m_config
	 * {Object} Configuration for chooser
	 */
	m_config: null,

	/**
	 * Property: m_components
	 * {Object} Components for chooser
	 */

	/**
	 * Constructor: SeedPaintball.ContentFlow
	 * Create a new ContentFlow instance
	 * 
	 * @param {Object} config
	 * @param {Object} components
	 */
	initialize: function( config, component ) {
		this.m_config = config;
		this.m_components = components;
	}
	// ------------------------------class_end----------------------------------
}
);

/**
 * Class: ImageSwitcher
 * Create an image switcher component
 */
SeedPaintball.ImageSwitcher = new Class( {
	/**
	 * Property: m_config
	 * {Object} configuration for switcher
	 */
	m_config: null,

	/**
	* Property: m_switcherSelected
	* {Integer}
	*    Currently selected image
	*/
	m_switcherSelected: null,

	/**
	 * Property: m_div1
	 * {Element} div1 of the image switcher
	 */
	m_div1: null,

	/**
	 * Property: m_div2
	 * {Element} div2 of the image switcher
	 */
	m_div2: null,

	/**
	 * Constructor: SeedPaintball.ImageSwitcher
	 * Create a new ImageSwitcher instance
	 * 
	 * @param {Object} config
	 */
	initialize: function( config ) {
		this.m_config = config;

		// Create switcher divs
		this.m_div1 = new Element( "DIV", {
				id: config.container + "_img1",
				styles: {
					position: "absolute",
					top: "0px",
					left: "0px",
					bottom: "0px",
					right: "0px",
					"background-repeat": "no-repeat",
					"background-position": "top center",
					"opacity": "0"
				}
			} );

		this.m_div2 = new Element( "DIV", {
				id: config.container + "_img2",
				styles: {
					position: "absolute",
					top: "0px",
					left: "0px",
					bottom: "0px",
					right: "0px",
					"background-repeat": "no-repeat",
					"background-position": "top center",
					"opacity": "0"
				}
			} );
		// Create text div if supplied
		if( $defined( config.text ) ) {
			var textDiv1 = new Element( "DIV", {
					"styles": {
						"width": config.text.width,
						"margin-top": config.text.top,
						"margin-left": config.text.left
					}
				} );
			if( $type( config.text.html ) == "string" ) {
				textDiv1.set( "html", config.text.html );
			} else {
				textDiv1.grab( config.text.html );
			}
			var textDiv2 = textDiv1.clone();
			this.m_div1.grab( textDiv1 );
			this.m_div2.grab( textDiv2 );
		}

		this.m_div1.inject( $( config.container ), "top" );
		this.m_div1._sliding = false;
		this.m_div2.inject( $( config.container ), "top" );
		this.m_div2._sliding = false;

		this.setImage( $pick( this.m_switcherSelected, 0 ) );
		var left = $( config.leftButton );
		left.addEvent( "click", function( event ) {
				if( event.target._sliding ) {
					return;
				}
				event.target._sliding = true;
				setTimeout( function() {
						event.target._sliding = false;
					}.bind( this ), 2500 );
				this.setImage( this.m_switcherSelected - 1 );
			}.bind( this ) );
		var right = $( config.rightButton );

		right.addEvent( "click", function( event ) {
				if( event.target._sliding ) {
					return;
				}
				event.target._sliding = true;
				setTimeout( function() {
						event.target._sliding = false;
					}.bind( this ), 2500 );
				this.setImage( this.m_switcherSelected + 1 );
			}.bind( this ) );
	},

	/**
	* Function: setImage
	* Set current image in the switcher
	* 
	* @param {Integer} index
	* @param {String} image
	* @param {Integer} duration
	*/
	setImage: function( index, image, duration ) {
		var config = this.m_config;
		var url;
		if( $defined( image ) ) {
			url = image;
		} else {
			if( index >= config.images.length ) {
				// wrap
				index = 0;
			}
			if( index < 0 ) {
				// wrap
				index = config.images.length - 1;
			}
			url = config.images[ index ];
		}
		if( !$defined( url ) ) {
			if( index >= config.images.length ) {
				// wrap
				index = 0;
			}
			if( index < 0 ) {
				// wrap
				index = config.images.length - 1;
			}
			config.images[ 0 ];
		}
		if( $defined( this.m_div1._switcherVisible )
			&& this.m_div1._switcherVisible ) {
			this.m_div1._switcherVisible = false;
			this.m_div2.setStyle( "background-image", "url(" + url + ")" );
			this.m_div1.get( 'tween', {
					property: 'opacity',
					duration: $pick( duration, "1500" )
				} ).start( 1, 0 );

			this.m_div2.get( 'tween', {
					property: 'opacity',
					duration: $pick( duration, "1500" )
				} ).start( 0, 1 );
		} else {
			this.m_div1._switcherVisible = true;
			this.m_div1.setStyle( "background-image", "url(" + url + ")" );
			this.m_div1.get( 'tween', {
					property: 'opacity',
					duration: $pick( duration, "1500" )
				} ).start( 0, 1 );

			this.m_div2.get( 'tween', {
					property: 'opacity',
					duration: $pick( duration, "1500" )
				} ).start( 1, 0 );
		}
		if( !$defined( image ) ) {
			this.m_switcherSelected = index;
		}
	},

	/**
	 * Function: toggleImageVisibility
	 * Toggle visibility of switcher component
	 * 
	 * @param {String} visible
	 */
	toggleImageVisibility: function( visible ) {
		if( this._switcherHiding ) {
			return;
		}
		var left = $( this.m_config.leftButton );
		var right = $( this.m_config.rightButton );

		if( $defined( left._hidden ) && left._hidden ) {
			// show if requested
			if( visible == "hide" ) {
				return;
			}
			left._hidden = false;
			left.fade( "in" );
			right.fade( "in" );
			this.setImage( this.m_switcherSelected, null, 500 );
		} else {
			if( visible == "show" ) {
				return;
			}
			left._hidden = true;
			left.fade( "out" );
			right.fade( "out" );
			this.setImage( null, this.m_config.inactiveImage, 500 );
		}
		this._switcherHiding = true;
		setTimeout( function() {
				this._switcherHiding = false;
			}.bind( this ), 750 );
	}
	// ------------------------------class_end----------------------------------
}
);
/**
 * Function: domready Fired when the page has been loaded and the DOM is ready.
 */
window.addEvent( "domready", function() {
		new SeedPaintball.Site( SeedPaintball.Config );
	} );
