(function($, window) {
	var 
	basePath,
	buttonsController,
	currentIndex = 0,
	lightSwitchController,
	settings = {},
	slideshowData,
	textContainer,
	windowTitle = window.document.title,
	sliderStyle,
	ie7Mode = false;

	$.fn.ready(init);
	function init() {
		$.fx.interval = 30;

		var	loader = new XMLLoader('/Style%20Library/xml/homepage_widget.xml');
		$(loader).bind('loadComplete', loadCompleteHandler);
		loader.load();		
	}
	function preloadImages(array) {
		var 
		preloader = $('#preloader'),
		bar = preloader.find('#bar'),
		images = [],
		length = array.length,
		startSlideshow = false;		
		for (var i = 0; i < length; i++) {
			var element = array[i];
			images.push(basePath + $(element).text());
		}

		initHandler();
		$.preload(images, {
			onComplete: loadedItemHandler,
			onFinish: loadedAllHandler
		});

		function initHandler() {
			if (preloader) {
				preloader.fadeIn(500);
				bar.width(0);
			}
		}
		function loadedItemHandler(data) {
			var width = Math.ceil((data.index / (data.total - 1)) * 100);
			bar.animate({'width': width + "%"}, 0);
		}
 		function loadedAllHandler(data) {
			preloader.fadeOut(500, function() {
				preloader = null;
				initSlideshow();
			});
		}		
	}
	function getRandomInt(min, max) {
	  return Math.floor(Math.random() * (max - min + 1)) + min;
	}
	function loadCompleteHandler(event) {
		var browserVersion = parseInt($.browser.version);

		slideshowData = event.currentTarget.data;
		settings = slideshowData.find('settings');
		basePath = settings.find('basePath').text();
		sliderStyle = settings.find('style').text();
		ie7Mode = $.browser.msie && (browserVersion >= 7 && browserVersion < 8);

		var iamges;
		if (!ie7Mode) {
			images = slideshowData.find('image[preload!=true] src');
			preloadImages(images);
		}
		else {
			images = slideshowData.find('slide[staticBackup=true] image[preload!=true] src');
			initSlideshow();
		}
	}
	function initSlideshow() {
		var 
		analyticsActions = slideshowData.find('analyticsAction'),
		normalImages = ie7Mode ? slideshowData.find('slide[staticBackup=true] image[type=normal]') : slideshowData.find('image[type=normal]'),
		contrastImages = ie7Mode ? slideshowData.find('slide[staticBackup=true] image[type=contrast]') : slideshowData.find('image[type=contrast]'),
		thumbImages = ie7Mode ? slideshowData.find('slide[staticBackup=true] image[type=thumb]') : slideshowData.find('image[type=thumb]'),
		currentIndex = getRandomInt(0, normalImages.length - 1),
		currentNormalImage = $('#normal-image'),
		currentNormalSrc = '',
		lightswitch,
		lightSwitchView = $('#light-switch'),
		menu = new ListCreator(), 
		menuElement = $('#menu'),
		slideshow, 
		slideshowController;

		// create the light switch
		lightswitch = new Lightswitch(sliderStyle, lightSwitchView);
		
		lightSwitchController = new LightSwitchController(lightSwitchView, null, sliderStyle);		
		$(lightSwitchController).bind('tweenSwitch', function(event) {
			var duration = lightSwitchController.duration;
			currentNormalImage.fadeTo(duration, this.tweenValue);

			slideshowController.setSelectedIndex(buttonsController.selectedIndex);
		});

			// create the menu html	
		if (!ie7Mode) {		
			menu.dataProvider = createDataProvider(thumbImages, true, true);
			menuElement.append(menu.createHtml());			
		}

		// create the slideshow	html
		slideshowElement = $('#slideshow'),
		slideshow = new ListCreator('ul', true);
		slideshow.dataProvider = createDataProvider(contrastImages);
		slideshowElement.append(slideshow.createHtml());

		// initialize the slideshowController
		slideshowController = new SlideshowController(slideshowElement, slideshow.dataProvider);

		// initialize the TextContainer
		textContainer = new TextContainer($('#text-container'));

		// listen for when the user has selected a button
		// when they do track the event in analytics and update the SlideshowController with the new image
		buttonsController = new ButtonsController(menuElement, menu.dataProvider);
		$(buttonsController).bind('buttonSelect', function(event) {
			var index = buttonsController.selectedIndex;

			// track when a user clicks on the one thumbnails
			AnalyticsTracker.track('Event', 'Homepage_marquee', $(analyticsActions[index]).text(), windowTitle);

			textContainer.hide();
			lightSwitchController.tween(0, 100);

			// why so many setTimeouts? We want to stagger the animation so everything doesn't happen at the sametime.
			setTimeout(function() {
				var noramlImageData = $(normalImages[index]);
				currentNormalSrc = basePath + noramlImageData.find('src').text();
				currentNormalImage.attr('src', currentNormalSrc);

				//
				// intro animation
				//
			
				buttonsController.toggleMouseActivity('unbind');
				lightSwitchController.toggleHandVisibility('show');				
				setTimeout(function() {
					var interval = 3000;

					lightSwitchController.tween(1, 2000);
					setTimeout(function() {
						lightSwitchController.toggleHandVisibility('hide');
						textContainer.show();
						buttonsController.toggleMouseActivity('bind');
					}, interval);
				}, 1000);
			}, 450);
		});

		lightSwitchController.toggleHandVisibility('hideNow');			
		if (!ie7Mode) {
			buttonsController.setSelectedIndex(currentIndex);
			slideshowController.setSelectedIndex(currentIndex);
		}
		else {
			var noramlImageData = $(normalImages[currentIndex]);
			currentNormalSrc = basePath + noramlImageData.find('src').text();
			currentNormalImage.attr('src', currentNormalSrc);
			currentNormalImage.show();
			textContainer.show(0);
		}
	}

	/**
	 * Utility function to create a dataPrivder to use with various Controllers
	 * 
	 * @param  Array 	data        	An list of xml nodes
	 * @param  Boolean 	includeLink 	A flag to determine whether to wrap the content around a link or not
	 */
	function createDataProvider(data, includeLink, makeBackground) {
		var 
		length = data.length,
		dataProvider = [];
		for (var i = 0; i < length; i++) {
			var image = $(data[i]),
			src = image.find('src'),
			path = basePath + image.find('src').text(),
			html = '';

			if (includeLink) {
				html += '<a href="#">';
			}

			if (makeBackground) {
				html += '<span class="thumb" style=\"background-image: url(' + path + ');\"><span class="color-plate" style="display: none; background-image: url(/Style%20Library/Images/misc/color_plate.gif);"></span></span>';
			}
			else {
				html += '<img src="' + path + '" />';
			}

			if (includeLink) {
				html += '</a>';
			}

			dataProvider[i] = {html: html}
		}
		return dataProvider;
	}

	/********************************************************************
	 * Analytics Tracker
	 *******************************************************************/

	var AnalyticsTracker = function() {}
	AnalyticsTracker.track = function(type) {
		var _gaq = _gaq || [];

		arguments[0] = '_track' + arguments[0];
		_gaq.push(arguments);
	}


	/********************************************************************
	 * Controllers
	 ********************************************************************/

	/**
	 * LightSwitchController
	 * 
	 * TODO: Create behavior objects that can get passed into the Controller. 
	 * 		 This would help encapsolate the logic needed to animate the slider in different styles. 
	 * 		 It would also make it easier to add new types of sliders with different behaviours. 
	 */
	var LightSwitchController = function(view, dataProvider, sliderStyle) {
		this.view = view;
		this.sliderStyle = sliderStyle;
		this.dataProvider = dataProvider;
		this.tweenValue = 0;
		this.duration = 0;
		this.fakeValue = 0;

		var 
		scope = this,
		knob = this.view.find('#knob'),
		hand = this.view.find('#hand'),
		oldValue,
		slideBounds = 47,
		initialKnobPos = knob.position(),
		initialHandPos = hand.position().top;

		if (!ie7Mode) {
			if (scope.sliderStyle == 'slider') {
				$(knob).bind('mousedown touchstart', mouseDownHandler);
				$(document).bind('mouseup touchend', mouseUpHandler);
			}
		}

		function mouseDownHandler(event) {
			AnalyticsTracker.track('Event', 'Homepage_marquee', 'click_turn_dial', windowTitle);

			if (scope.sliderStyle == 'slider') {
				$(document).bind('mousemove touchmove', mouseMoveHandler);
			}
			else if (scope.sliderStyle == 'dial') {
				$(knob).bind('mousemove touchmove', mouseMoveHandler);
			}
			return false;
		}
		function mouseUpHandler(event) {
			$(event.currentTarget).unbind('mousemove touchmove');
		}
		function mouseMoveHandler(event) {			
			var
			position,
			value,
			touch;

			if (event.originalEvent.touches || event.originalEvent.changedTouches) {
				touch = event.originalEvent.touches[0] || event.originalEvent.changedTouches[0];
			}

			if (scope.sliderStyle == 'slider') {
				var halfKnobHeight = $(knob).height() * .5;
				if (touch) {
					position = (touch.pageY - halfKnobHeight) - (scope.view.offset().top + initialKnobPos.top);		
				}
				else {
					position = (event.pageY - halfKnobHeight) - (scope.view.offset().top + initialKnobPos.top);
				}

				// slide down/up the knob based on the yPos / height
				value = position / (slideBounds - halfKnobHeight);
			}
			else if (scope.sliderStyle == 'dial') {
				if (touch) {
					position = touch.pageX - (scope.view.offset().left + initialKnobPos.left);		
				}
				else {
					position = event.pageX - (scope.view.offset().left + initialKnobPos.left);				
				}
				
				// rotate knob based on percent of xPos / width
				value = 1 - (position / $(this).width());
			}

			scope.tween(value);
		}
		/**
		 * tween
		 * 
		 * Tweens certain elements of the Lightswitch based on the value
		 * 
		 * @param  float value
		 *         An animation value between 0 and 1
		 * @param float duration
		 *         A value to determine how long the tween should last
		 */
		this.tween = function(value, duration, propogateEvent) {
			if (this.tweenValue == value) {
				return;
			}

			// we want to create a bounds for how high/low the value can be
			value = value < 0 ? 0 : value;
			value = value > 1 ? 1 : value;

			this.duration = duration === undefined ? 0 : duration;
			this.tweenValue = value;

			if (this.sliderStyle == 'slider') {

				var top = value * slideBounds + initialKnobPos.top;
				if (hand.css('display') == 'block') {
					$(hand).animate({
						'top': initialHandPos + (slideBounds * value)
					}, this.duration);
				}

				$(knob).animate({
					'top': top
				}, this.duration);
			}
			else if (this.sliderStyle == 'dial') {
				// only animate the hand if it's actually visible
				var
				totalAngle = 50;
				angle = (value * -totalAngle) + 'deg';

				if (hand.css('display') == 'block') {
					$(hand).animate({rotate: angle}, {duration: this.duration});
				}

				$(knob).animate({rotate: angle}, {duration: this.duration});
			}

			if (propogateEvent === undefined) {
				propogateEvent = true;
			}

			if (propogateEvent) {
				$(this).trigger('tweenSwitch');			
			}
		}		

		this.toggleHandVisibility = function(state) {
			if (state == 'hideNow') {
				$(hand).hide();
			}
			else if (state == 'hide') {
				if (this.sliderStyle == 'dial') {
					$(hand).fadeOut(500, function(){
						$(this).animate({rotate: 0}, {duration: 0});
					});
				}
				else if (this.sliderStyle == 'slider') {
					$(hand).fadeOut(500, function() {
						$(hand).css('top', initialHandPos);
					});
				}
			}
			else if (state = 'show') {
				$(hand).fadeIn(500);
			}
		}		
	}


	/**
	 * SlideshowController
	 * 
	 * Handles updating the Slideshow with a new image based on the index given
	 */
	var SlideshowController = function(view, dataProvider) {
		this.view = view;
		this.dataProvider = dataProvider;
		this.selectSlide;
		this.selectedIndex = -1;

		// need to set display to none in order to fadein/out the images
		var 
		slides = this.view.find('li'),
		length = slides.length;
		for (var i = 0; i < length; i++) {
			var element = slides[i];
			$(element).css('display', 'none');
		}

		this.setSelectedIndex = function(index) {
			if (this.selectedIndex == index) {
				return;
			}
			this.selectedIndex = index;

			if (this.selectSlide) {
				this.selectSlide.fadeOut(500);
			}
			
			this.selectSlide = $(slides[index]);
			this.selectSlide.fadeIn(500);
	 	}
	}



	/**
	 * ButtonsController
	 * 
	 * Handles the user interaction for all of the buttons and notifies any listeners when a button has been selected.
	 */
	var ButtonsController = function(view, dataProvider) {
		this.view = view;
		this.dataProvider = dataProvider;
		this.selectedAnchor;
		this.selectedIndex = -1;

		var 
		anchor,
		anchors = this.view.find('a'), 
		bindMouseEvents = true,
		length = anchors.length, 
		scope = this;
		for (var i = 0; i < length; i++) {
			anchor = $(anchors[i]);				
			anchor.click(anchorClickHandler);
			anchor.hover(anchorHoverHandler);
		}

		function anchorHoverHandler(event) {
			if (bindMouseEvents) {
				var
				anchor = $(this),
				colorPlate = anchor.find('.color-plate'),
				anchorId = anchor.parent().attr('id'),
				selectedId = scope.selectedAnchor.parent().attr('id');

				if (anchorId === selectedId) {
					return;
				}

				if (event.type == 'mouseenter') {
					colorPlate.fadeTo(200, .5);
				}
				else if (event.type == 'mouseleave') {
					colorPlate.fadeTo(200, 0);
				}
			}
		}
		function anchorClickHandler(event) {
			if (bindMouseEvents) {
				var index = $(anchors).index(event.currentTarget);
				scope.setSelectedIndex(index);
			}

			return false;
		}
		this.toggleMouseActivity = function(value) {
			bindMouseEvents = value == 'bind' ? true : false;
		}
		/**
		 * setSelectedIndex
		 * 
		 * Sets the selected button based on the index value
		 * @param integer index
		 */
		this.setSelectedIndex = function(index) {
			if (this.selectedIndex == index) {
				return;
			}
			this.selectedIndex = index;

			var colorPlate;
			if (this.selectedAnchor) {
				colorPlate = this.selectedAnchor.find('.color-plate');
				colorPlate.fadeTo(200, 0);
			}
			
			this.selectedAnchor = $(anchors[index]);

			colorPlate = this.selectedAnchor.find('.color-plate');
			colorPlate.fadeTo(200, 0.5);

			$(this).trigger('buttonSelect');
		}
	}



	/********************************************************************
	 * Loaders
	 ********************************************************************/

	/**
	 * XMLLoader
	 * 
	 * Loads in an external XML file and then notifies to any listeners when it's complete
	 */
	var XMLLoader = function(dataSource) {
		this.dataSource = dataSource;
		this.data;
	}
	XMLLoader.prototype.load = function(dataSource) {		
		if (dataSource !== undefined) {
			this.dataSource = dataSource;		
		}

		if (!this.dataSource) {
			throw new Error("You haven't set a dataSource yet. Might want to do that first.");
		}

		var scope = this;
		$.ajax(this.dataSource, {
			success: successHandler,
			error: errorHandler
		});

		function errorHandler(jqXHR, textStatus, errorThrown) {
			throw new Error("There was an error loading this XML: " + textStatus);
		}

		function successHandler(data, textStatus, jqXHR) {
			scope.data = $(data);
			$(scope).trigger('loadComplete');
		}
	}



	/********************************************************************
	 * Views
	 ********************************************************************/

	/**
	  * TextContainer
	  * 
	  * Handles the user interaction for the Text Container's button
	  */
	var TextContainer = function(view) {
		this.view = view;

		var button = this.view.find('.button');
		button.hover(buttonHoverHandler);
		button.click(buttonClickHandler);

		function buttonClickHandler(event) {
			AnalyticsTracker.track('Event', 'Homepage_marquee', 'click_see_range_lenses', windowTitle);
		}
		function buttonHoverHandler(event) {
			var text = $(this).find('span');			
			if (event.type == 'mouseenter')	{
				text.fadeTo(200, .8);
			}
			else if (event.type == 'mouseleave') {
				text.fadeTo(200, 1);
			}
		}
	}
	TextContainer.prototype.hide = function() {
		this.view.fadeOut(500);	
	}
	TextContainer.prototype.show = function(duration) {
		if (duration == undefined) {
			duration = 500;
		}
		this.view.fadeIn(duration);
	}



	/**
	 * Lightswitch
	 * 
	 * Handles the setup and creation of the view for the Lightswitch
	 */
	var Lightswitch = function(style, view) {
		this.style = style;
		this.view = view;
		this.view.addClass(this.style);
	}


	/**
	 * ListCreator
	 * 
	 * Utility class that creates an html list based on the dataProvider set
	 */
	var ListCreator = function(listType, autoHide) {
		this.listType = listType === undefined ? 'ul' : listType;
		this.autoHide = autoHide == undefined ? false : autoHide;
		this.dataProvider = [];
	}
	/**
	 * dataProvider
	 * 
	 * A dataProvider is a list of objects that follow the below structure:
	 * 
	 * 	{
	 *		id: <html id string>,
	 *	 	html: <inner html string>
	 * 	}
	 * @param An array of data objects
	 * 
	 */
	ListCreator.prototype.dataProvider = function(dataProvider) {
		this.dataProvider = dataProvider;
	}
	/**
	 * createHtml
	 * 
	 * Creates a HTML ul/ol list based on the dataProvider given
	 * 
	 */
	ListCreator.prototype.createHtml = function() {
		// FIXME: I hate building html strings like this. Really need to find a cleaner way to do this....
		var 
		length = this.dataProvider.length,
		list = '<' + this.listType + '>',
		displayMode = this.autoHide ? 'none' : 'block';

		for (var i = 0; i < length; i++) {
			var item = this.dataProvider[i];
			list += '<li id="' + i + '" style="display: ' + displayMode + '">' + item.html + '</li>';
		}
		list += '</' + this.listType + '>';
		return list;
	}
})($, window);
