/**
 * jquery.vt.doubleBookshelf.js - (c) Pearson Education 2011
 */
(function($) {

	/**
	 * Helper method to make a jQuery object unselectable/undraggable
	 */
	$.fn.unselecterize = function() {
		this.attr('unselectable', 'on')
			.css('-moz-user-select', 'none')
			.each(function() {
				this.onselectstart	= function() {return false;};
				this.ondragstart	= function() {return false;};
			});
	}

	var DoubleBookshelf = function() {

		// in case we are called in the global scope
		// (this probably doesn't make a difference as I am
		// not using the 'this' keyword??)
		if ( !(this instanceof arguments.callee) )
			throw new Error("Constructor called as a function (did you forget to use 'new'?)");

		////////////////////////////////////////////
		// PRIVATE VARIABLES AND FUNCTIONS

		// two constants
		var BACKWARD = 1;
		var FORWARD = -1;

		// global reference to viewport(s)
		var $viewport;
		
		// global references to viewport innards
		var $view_inner_top;
		var $view_inner_bottom;

		// global reference to frame widths
		var $frame_top;

		// control how far the viewport can move
		var minX;     // (negative of) total content width
		var maxX = 0; // always 0

		// how far the viewport moves when we click the
		// arrows (pixels per second)
		var arrowMultiplier = 200;


		var _cropToBounds = function(vLeft) {
			_resetMinX();
			
			if (vLeft > maxX)
				vLeft = maxX;
			else if (vLeft < minX)
				vLeft = minX;

			return vLeft;
		}

		var _getViewportLeft = function() {
			return parseFloat(($viewport.data('left') || $viewport.css('left')));
		}

		var _setViewportLeft = function(newLeft) {
			$viewport.data('left', newLeft);
			$viewport.css('left', newLeft);
		}

		/**
		 * Handler for arrows
		 */
		var _handleArrowMousedown = function(direction) {
			var delta = direction * (arrowMultiplier / 50);
			
			_scrollBy( delta );

			var scrollInterval = setInterval(function() {
				_scrollBy( delta );
			}, 10);

			$('html').bind('mouseup.doubleBookshelf', function() {
				$('html').unbind('.doubleBookshelf');
				clearInterval(scrollInterval);
			});
		}

		/**
		 * Handler for drag functionality
		 */
		var _handleDrag = function(event) {
			event.preventDefault();

			$viewport.removeClass('openhand');
			$viewport.addClass('closedhand');

			// record viewpoint positions (use first match; should all be identical)
			var vLeft = _getViewportLeft();

			// calculate mouse offset
			var clickPageX = event.pageX;

			$('html').bind('mousemove.shelf_drag', function(event) {
				var newLeft = vLeft + (event.pageX - clickPageX);

				newLeft = _cropToBounds(newLeft);
				_setViewportLeft(newLeft);

				// sad, but true - use a global flag to stop a drag from firing
				// the lightbox (if we move it more than 10 pixels)
				// flag cleared in jquery.vt.lightbox.js or
				//   home_logged_in_view.php
				if ( Math.abs(vLeft-newLeft) > 10 ) {
					var ns = window.VT || (window.VT = {});
					ns.preventCoverClick = true;
				}
			});

			return false;
		}

		/**
		 * Recalculate minX
		 */
		var _resetMinX = function() {
			var minV = -( Math.max($view_inner_top.outerWidth(), $view_inner_bottom.outerWidth()) );
			var frameWidth = $frame_top.outerWidth();

			minX = minV + frameWidth;

			if (minX > 0) // happens when the content is shorter than the shelf.
				minX = 0;
		}

		/**
		 * Scroll the viewport by (delta) pixels
		 */
		var _scrollBy = function(delta) {
			var destX = delta + _getViewportLeft();

			destX = _cropToBounds(destX);

			_setViewportLeft(destX);
		}


		////////////////////////////////////////////
		// PUBLIC API BELOW

		var init = function(options, $context) {

			// resolve references supplied in the options
			var $shelf_top = $(options.shelf_top);
			var $shelf_bottom = $(options.shelf_bottom);
			var $arrow_backward = $(options.arrow_backward);
			var $arrow_forward = $(options.arrow_forward);
			// elements that form the shelf contents
			var $elem_top = $shelf_top.children(options.elem_top);
			var $elem_bottom = $shelf_bottom.children(options.elem_bottom);

			// dynamically create the rest of the shelf (frames, viewports)
				$frame_top = $('<div/>').addClass('frame'); // global
			var $frame_bottom = $('<div/>').addClass('frame');
			var $viewport_top = $('<div/>').addClass('viewport');
			var $viewport_bottom = $('<div/>').addClass('viewport');
			$view_inner_top = $('<div/>').addClass('viewport_inner');
			$view_inner_bottom = $('<div/>').addClass('viewport_inner');

			// make everything unselectable and undraggable (prevents
			//   unsightly highlighting when double clicking or dragging
			//   in some browsers)
			$('*', $context).unselecterize();


			////////////////////////////////////////
			// REBUILD THE DOM ELEMENTS

			// insert the elements into the viewports
			var pages_top = 0;
			var pages_bottom = 0;
			$elem_top.each(function() {
				pages_top++;
				$view_inner_top.append($(this).detach());
			});
			$elem_bottom.each(function() {
				pages_bottom++;
				$view_inner_bottom.append($(this).detach());
			});
			
			// put it all together
			$viewport_top.append($view_inner_top).addClass('openhand');
			$viewport_bottom.append($view_inner_bottom).addClass('openhand');
			$frame_top.append($viewport_top);
			$frame_bottom.append($viewport_bottom);
			$shelf_top.append($frame_top);
			$shelf_bottom.append($frame_bottom);

			// fulfill the global reference to the viewports
			$viewport = $('.viewport', $context);


			////////////////////////////////////////////
			// ADD HANDLERS

			// arrows
			$arrow_backward.bind('mouseover.doubleBookshelf', function() {
				if ( _getViewportLeft() < maxX )
					$arrow_backward.addClass('larr_active');
			}).bind('mouseout.doubleBookshelf', function() {
				$arrow_backward.removeClass('larr_active');
			}).bind('mousedown.doubleBookshelf', function() {
				_handleArrowMousedown(1);
			});
			$arrow_forward.bind('mouseover.doubleBookshelf', function() {
				if ( _getViewportLeft() > minX )
					$arrow_forward.addClass('rarr_active');
			}).bind('mouseout.doubleBookshelf', function() {
				$arrow_forward.removeClass('rarr_active');
			}).bind('mousedown.doubleBookshelf', function() {
				_handleArrowMousedown(-1);
			});

			// viewports
			$viewport.bind('mousedown.shelf_drag', function(event) {
				_handleDrag(event);

				$('html').bind('mouseup.shelf_drag', function() {
					$viewport.removeClass('closedhand');
					$viewport.addClass('openhand');

					$('html').unbind('.shelf_drag');
				});
			});
			// window resize handler
			// prevent the viewports from being out of bounds if
			// we stretch the window out wider
			$(window).resize(function() {
				_resetMinX();

				var vLeft = _getViewportLeft();
				var cLeft = _cropToBounds(vLeft);

				if (cLeft != vLeft)
					_setViewportLeft(cLeft);
			});

			// calculate the initial minX after all assets have loaded
			$(window).bind('load', function() {
				_resetMinX();
			});
		}

		// construct and return the public API
		return {
			init	: init
		}
	}

	$.fn.doubleBookshelf = function(options) {
		
		var db = new DoubleBookshelf();
		db.init(options, this);
		
	}

})(jQuery);
