/* 
parent: 		the object that the pane should hang off of visually

one of these is required in options:
url: 				the url to retrieve content from when opening the pane
content_selector: 	a selector matching an element in the DOM that should be put in the pane

other keys in options, not required:
onOpen:  			callback that fires each time the pane is opened
onClose:  			callback that fires each time the pane is closed
onLoad: 			callback that fires only when the pane loads or reloads remote data 
... a bunch of ones i haven't documented yet, see below...
*/
var FloatingPane = function(parent, options) {
	var SHADOW_WIDTH = 7, CORNER_WIDTH = 11, BOTTOM_SHADOW_HEIGHT = 12;

	var width = options.width ? options.width : 200,
		top = parent.offset().top + parent.outerHeight(),
		left = parent.offset().left - SHADOW_WIDTH,
		container = $('<div/>').addClass('floatingPane').hide().css({position: 'absolute', top: top, left: left}), 
		created = false,
		needsUpdate = true,
		lastUrl,
		content, lShadow, rShadow, blShadow, bShadow, brShadow, loadingIndicator;
	
	// prevent panes from sticking out of the main container
	var mainContainer = $('#main_container');
	var maxleft = mainContainer.offset().left + mainContainer.width() - width - SHADOW_WIDTH;
	if (maxleft < left)
		container.css('left', maxleft);
	
	var create = function() {
		lShadow = $('<div/>').css({background: "url(/static/img/pane/bg_horiz.png) repeat-y", width: SHADOW_WIDTH});
		content = $('<div/>').addClass('floatingPaneContent').width(width);
		if (options.maxHeight)
			content.css({maxHeight: options.maxHeight, overflow: 'auto'});
		if (options.id)	
			content.attr('id', options.id);
		rShadow = $('<div/>').css({background: "url(/static/img/pane/bg_horiz.png) repeat-y -13px", width: SHADOW_WIDTH});
		blShadow = $('<div/>').css({background: "url(/static/img/pane/bg_bottomleft.png) no-repeat", width: CORNER_WIDTH});
		bShadow = $('<div/>').css({background: "url(/static/img/pane/bg_bottom.png) repeat-x"});
		brShadow = $('<div/>').css({background: "url(/static/img/pane/bg_bottomright.png) no-repeat", width: CORNER_WIDTH});
		$.each([blShadow, bShadow, brShadow], function(i, x) {x.css('height', BOTTOM_SHADOW_HEIGHT)});
		$.each([lShadow, content, rShadow, blShadow, bShadow, brShadow], function (i, x) {
			x.css('float', 'left');
			container.append(x);
		});		
		
		$('<a class="closeButton"/>').click(function() {close();}).appendTo(container);

		loadingIndicator = $('<img class="floatingPaneLoadingIndicator"/>').attr('src','/static/img/loading_horiz.gif').css({
			width: 30, left: parent.offset().left + parent.outerWidth()/2 - 16, top: top - 5, position: 'absolute'
		}).appendTo('body').hide();

		container.appendTo('body');
		created = true;
		if (options.oncreate) options.oncreate();
	};
	
	// if you pass in a url here, it will always be reloaded.
	var open = function(url, callback) {
		if (!created) create();
		if (options.content_selector) {
			var child = $(options.content_selector).removeClass('hidden');
			if (child.parent()[0] != content[0])
				content.append(child.show());
			if (options.track && options.trackPath)
				options.track(options.trackPath);
		} else if (needsUpdate || url) {
			loadContent(url? url: options.url, true, callback);
			return;
		}
				
		// have to animate opacity rather than using standard show/hide, 
		// because fitToContent doesn't work if the container is set to display: none.
		// but can't use opacity with IE7 because it displays wrong, so we just disable animation.
		container.css('display', 'block');
		if (!$.browser.msie) container.css('opacity', 0);
		fitToContent();
		if (!$.browser.msie) container.animate({opacity: 1});
		
		if (options.closeOnClickOut) {
			var clickedInside = function(event, $el) {
				var offset = $el.offset();
				var x = event.pageX - offset.left;
				var y = event.pageY - offset.top;
				return x > 0 && x < $el.width() && y > 0 && y < $el.height();
			};
			$('body').click(function(event) {
				if (!container.is(':hidden') && !clickedInside(event, container))
					close();
			});			
		}
		if (options.onOpen)
			options.onOpen();
		if (callback)
			callback();
		return false;
	};

	var close = function() {
		if ($.browser.msie)
			container.hide();
		else
			container.fadeOut();
		
		if (options.closeOnClickOut)
			$('body').unbind('click');
		if (options.onClose)
			options.onClose();
	};
	
	var fitToContent = function() {
		$.each([lShadow, rShadow], function (i, x) {x.height(content.outerHeight());});
		bShadow.width(content.outerWidth() - 8);
		container.width(content.outerWidth() + SHADOW_WIDTH*2);
	};

	var loadContent = function(url, openWhenDone, openCallback) {
		loadingIndicator.show();
		$.get(url, null, function(resp, status) {
			lastUrl = url;
			needsUpdate = false;
			content.html(resp);
			if (!container.is(':hidden'))
				fitToContent();
			else if (openWhenDone)
				open(null, openCallback);
			if (options.onLoad)
				options.onLoad();
			if (options.track)
				options.track(url);
			loadingIndicator.hide();
		});
	};
	
	var loadContentInSubPane = function(url, subPaneSelector) {
		loadingIndicator.show();
		$.get(url, null, function(resp, status) {
			content.find(subPaneSelector).html(resp);
			fitToContent();
			if (options.onLoad)
				options.onLoad();
			if (options.track)
				options.track(url);
			loadingIndicator.hide();
		});
	};
	
	var publicMethods = {
		toggle: function() {
			if (!container || container.is(':hidden'))
				open();
			else
				close();
			return false;
		},
		loadContent: loadContent,
		loadContentInSubPane: loadContentInSubPane,
		setNeedsUpdate: function(value) {needsUpdate = value},
		needsUpdate: function() {
			return needsUpdate;
		},
		fitToContent: fitToContent,
		open: open,
		close: close,
		isOpen: function() {
			return !(!container || container.is(':hidden'));
		},
		reload: function() {
			open(lastUrl);
		}
	};
	
	return $.extend(container, publicMethods);
};

var TabbedFloatingPane = function(parent, options) {
	var pane, modal;

	var setTabPoint = function(animated) {
		var tabPoint = pane.find('.tabPoint');
		var tab = pane.find('.tab.selected');
		if (tab.length == 0) return;
		var newPos = tab.offset().left - tab.parent().offset().left + tab.outerWidth()/2 - 6;
		if (animated) {
			Dependo.require(['ext/jquery.backgroundPosition.js'], function() {
				tabPoint.animate({backgroundPosition: newPos + "px 0px"});
			});
		}
		else
			tabPoint.css('background-position', newPos + "px 0px");
	};	

	if (options.onLoad) {
		var oldOptionsOnload = options.onLoad;
		options.onLoad = function() {
			setTabPoint(false);
			oldOptionsOnload();
		};
	} else {
		options.onLoad = function() {
			setTabPoint(false);
		}
	}

	options.oncreate = function() {
		pane.delegate('click', 'a.turn_page', function() {
			pane.loadContentInSubPane($(this).attr('href'), '.pane_tab_content');
			return false;
		});

		pane.delegate('click', '.tab a', function() {
			var $this = $(this);
			var tab = $this.parent();
			if (tab.hasClass('selected')) return;
			pane.find('.tab.selected').removeClass('selected');
			tab.addClass('selected');
			if (modal)
				closeModal(false);
			setTabPoint(true);
			pane.loadContentInSubPane($this.attr('href'), '.pane_tab_content');
			return false;
		});
	}

	var closeModal = function(animated) {
		var kill = function() {
			modal.remove();
			modal = null;
			pane.find('.tab.not_modal').removeClass('hidden');
			if (pane.needsUpdate()) 
				pane.reload();
		}
		if (animated)
			modal.fadeOut('normal', kill);
		else
			kill();
	};

	var loadContentInModal = function(link, modalId, onLoad, animated) {
		if (!pane.isOpen()) {
			pane.open(null, function() {
				loadContentInModal(link, modalId, onLoad, animated);
			});
			return;
		}
		var $link = $(link), tabContent = pane.find('.pane_tab_content');
		if (modal)
			modal.remove();
		modal = $('<div/>').attr('id', modalId).appendTo(tabContent.parent()).css({
			background: '#fff',
			position: 'absolute',
			zIndex: 92,
			overflow: 'auto'			
		});
		var finalPos = {
			top: tabContent.position().top,
			left: tabContent.position().left,
			width: tabContent.outerWidth(),
			height: tabContent.outerHeight()
		};
		var finish = function() {
			modal.load($link.attr('href'), null, function() {
				if (options.track)
					options.track($link.attr('href'));
				if (onLoad)
					onLoad.apply(modal);
			});
		};
		pane.find('.tab.not_modal').addClass('hidden');
		if (animated) {
			modal.css({
				top: $link.offset().top - tabContent.parent().offset().top + $link.outerHeight()/2,
				left: $link.offset().left - tabContent.parent().offset().left + $link.outerWidth()/2,
				width: 1,
				height: 1
			}).animate(finalPos, 'fast', 'linear', finish);
		} else {
			modal.css(finalPos);
			finish();
		}
	};

	pane = FloatingPane(parent, options);
	
	var publicMethods = {
		selectTab: function(index) {
			pane.find('.tab a:eq('+index+')').click();
		},
		loadContentInModal: loadContentInModal,
		closeModal: closeModal,
		selectTabWithUrl: function(index, url) {
			var tab = pane.find('.tab:eq('+index+')');
			if (!tab.hasClass('selected')) {
				pane.find('.tab.selected').removeClass('selected');
				tab.addClass('selected');
			}
			if (modal)
				closeModal(false);
			setTabPoint(true);
			pane.loadContentInSubPane(url, '.pane_tab_content');
		}
	};
	
	return $.extend(pane, publicMethods);
};
