/**
 * PandoraPagerManager implements the table paging functionality.  This class is married to jquery.pandoraPager.js
 *
 */
(function() {
PandoraPagerManager = {
/**
 * NOTE:  very important.  PandoraPagerManager should never carry state.  A page can have multiple tables
 * and there is only the one instance of PandoraPagerManager
 */
	init: function(domProxy, settings, optionalSettings, function_onStateChange) {
		//normalize the settings
		if (!optionalSettings) {
			optionalSettings = {};
		}

		if (!isNaN(parseInt(optionalSettings.totalItemCount))) {
			domProxy.setTotalItemCountAttr(optionalSettings.totalItemCount);
		}

		if (!isNaN(parseInt(optionalSettings.currentPage))) {
			domProxy.setCurrentPageNumberAttr(optionalSettings.currentPage);
		}

		if (optionalSettings.showall) {
			domProxy.setShowAllAttr(true);
		}

		if (settings.navContainers == null || settings.navContainers.length == 0) {
			settings.navContainers = domProxy.createNavContainers(settings.table_id + 'nav_container');
		}

		if (settings.url_rowprovider != null
				&& settings.url_rowprovider.length > 0
				&& settings.url_rowprovider.indexOf("?") < 0) {
			settings.url_rowprovider += "?";
		}

		if (isNaN(settings.batchSize)) {
			settings.batchSize = 5;
		}

		if (isNaN(settings.pageSize)) {
			settings.pageSize = 10;
		}

		// if show_index is supplied, set currentPage so that show_index will be displayed
		if (!isNaN(settings.show_index)) {
			var p = Math.floor(settings.show_index / settings.pageSize) + 1;
			domProxy.setCurrentPageNumberAttr(p);
		}

		PPM.executeFunction(settings.onDataDownload);

		if (domProxy.getPageCount() > 1) {
			if (!domProxy.isPagingControlBuilt()) {
				var blnAllowSkipToEnd = settings.allowSkipToEnd
						&& (settings.maxItemsToAllowSkipToEnd == null || isNaN(settings.maxItemsToAllowSkipToEnd) || domProxy.getTotalItemCount() < settings.maxItemsToAllowSkipToEnd);
				
				domProxy.makeNav(blnAllowSkipToEnd );
			}

			if (!domProxy.isBoundToStateChangedEvent()) {
				//if we haven't already bound to the STATE_CHANGED_EVENT, bind to it
				STATE_CHANGED_EVENT.subscribe(function_onStateChange);
				domProxy.setIsBoundToStateChangedEvent();
			}

			domProxy.setPage(domProxy.getShowAll() ? PandoraPagerManager.PAGE_NUMBER_SHOW_ALL : domProxy.getCurrentPageNumber());
		} else {
			if (domProxy.hasPagingControl()) {
				//if we have a paging control, but now we don't have any pages, that means that this table used to page
				//but now it doesn't, so we have to hide the paging control
				domProxy.setNavItemVisibility([]);
			}
		}

		domProxy.applyToggleAllVisiblity();

		// Show the table body, in case it was hidden
		domProxy.showTableBody();
	},

/**
 * show and hide the table rows according to our current page.  If the table doesn't contain the requested page
 * this will attempt to download the required table rows
 */
	applyTableRowVisibility: function (domProxy, settings, blnReturnFromFetch) {
		var countRowsOnBrowser = domProxy.getRowCount();
		var totalItemCount = domProxy.getTotalItemCount();
		var currentPage = domProxy.getCurrentPageNumber();
		var blnShowAll = domProxy.getShowAll();

		if (countRowsOnBrowser > totalItemCount) {
			//if we have more rows on the browser than the total item count,
			//we must have added some rows.  update the total item count accordingly
			totalItemCount = countRowsOnBrowser;
		}

		var startIndex_currentPage;
		var lastIndex_currentPage;

		if (blnShowAll) {
			startIndex_currentPage = 0;
			lastIndex_currentPage = totalItemCount;
		} else {
			startIndex_currentPage = (currentPage - 1) * settings.pageSize;
			lastIndex_currentPage = startIndex_currentPage + settings.pageSize;
		}

		if (startIndex_currentPage >= totalItemCount) {
			//if we're trying to jump to a page that doesn't exist, go back to page 1
			domProxy.setPage(1);

			return;
		}

		var blnNeedMoreRecords = lastIndex_currentPage > countRowsOnBrowser;
		var blnAllRecordsFetched = totalItemCount == countRowsOnBrowser;
		var blnHasDataProvider = settings.url_rowprovider != null && settings.url_rowprovider.length > 0;


		if (!blnReturnFromFetch && blnHasDataProvider && blnNeedMoreRecords && !blnAllRecordsFetched) {
			//if we don't have the necessary rows, we have to download them
			var countRowsNeeded = Math.min(countRowsOnBrowser + (settings.batchSize * settings.pageSize), totalItemCount);
			countRowsNeeded = Math.max(lastIndex_currentPage, countRowsNeeded);

			domProxy.setNavItemVisibility([PandoraPagerManager.NAV_CONTAINER_FETCHING_DATA]);

			domProxy.fetchRows(settings.url_rowprovider, countRowsOnBrowser, countRowsNeeded, function(html) {
				domProxy.appendTableRowHTML(html);

				PPM.executeFunction(settings.onDataDownload);

				PPM.applyTableRowVisibility(domProxy, settings, true);
			});
		} else {
			var strWindowTitle = PPM.buildWindowTitle(domProxy.getWindowTitle(), settings.friendlyName, blnShowAll ? PandoraPagerManager.PAGE_NUMBER_SHOW_ALL : currentPage);

			domProxy.setWindowTitle(strWindowTitle);

			var pageCount = domProxy.getPageCount();

			domProxy.enableNavPrev(currentPage > 1);
			domProxy.enableNavNext(currentPage < pageCount);
			domProxy.setNavPageDesc(currentPage + " of " + pageCount);

			// Hide all rows
			domProxy.showTableRows(false);

			// Show rows for the current page
			if (blnShowAll) {
				domProxy.setNavItemVisibility([PandoraPagerManager.NAV_CONTAINER_DONOT_SHOWALL]);

				domProxy.showTableRows(true);
			} else {
				domProxy.setNavItemVisibility([PandoraPagerManager.NAV_CONTAINER_PAGING, PandoraPagerManager.NAV_CONTAINER_SHOWALL]);

				domProxy.showTableRowsByIndex(startIndex_currentPage, settings.pageSize);
			}

			domProxy.showTableBody();
		}
	},

	buildWindowTitle: function (currentWindowTitle, tableFriendlyName, page) {
		var pageTitlePrefix = " || ";

		//if we previously added a "<table> <page>" suffix to the title, remove it
		var indexOfPageTitle = currentWindowTitle.indexOf(pageTitlePrefix);
		if (indexOfPageTitle >= 0) {
			currentWindowTitle = currentWindowTitle.substr(0, indexOfPageTitle);
		}

		//if our page number is greater than 1, add a page number suffix
		if (page == PandoraPagerManager.PAGE_NUMBER_SHOW_ALL) {
			currentWindowTitle += pageTitlePrefix + "All " + tableFriendlyName;
		} else if (page > 1) {
			currentWindowTitle += pageTitlePrefix + tableFriendlyName + " page " + page;
		}

		return currentWindowTitle;
	},

	eventHandler_onPage: function(domProxy, navButton) {
		USER_INTERACTION_EVENT.fire();

		var pageCount = domProxy.getPageCount();
		var currentPage = domProxy.getCurrentPageNumber();

		if (navButton == PandoraPagerManager.NAV_BUTTON_NEXT) {
			if (currentPage < pageCount) {
				currentPage++;
			}
		} else if (navButton == PandoraPagerManager.NAV_BUTTON_PREVIOUS) {
			if (currentPage > 1) {
				currentPage--;
			}
		} else if (navButton == PandoraPagerManager.NAV_BUTTON_FIRST) {
			currentPage = 1;
		} else if (navButton == PandoraPagerManager.NAV_BUTTON_LAST) {
			currentPage = pageCount;
		}

		domProxy.setPage(currentPage);

		return false;
	},

	eventHandler_onShowAll: function(domProxy) {
		USER_INTERACTION_EVENT.fire();

		domProxy.setPage(PandoraPagerManager.PAGE_NUMBER_SHOW_ALL);

		return false;
	},

	eventHandler_onDoNotShowAll: function (domProxy) {
		USER_INTERACTION_EVENT.fire();

		domProxy.scrollToWindowTop();

		domProxy.setPage(domProxy.getCurrentPageNumber());

		return false;
	},

/**
 * executed when the user clicks the browser's back or forward button, or when they reload the page
 * The stateEvent parameter describes the desired current state.  This method inspects this value
 * and updates its table appropriately
 */
	eventHandler_onStateChange: function(proxy, settings, stateEvent) {
		var tableID_PageNum = stateEvent.id.split(",");

		if (settings.table_id == tableID_PageNum[0]) {
			var showAll_fromEvent = tableID_PageNum[1] == PandoraPagerManager.PAGE_NUMBER_SHOW_ALL;
			var currentPage_fromEvent = parseInt(tableID_PageNum[1]);


			proxy.setShowAllAttr(showAll_fromEvent);

			if (!isNaN(currentPage_fromEvent)) {
				proxy.setCurrentPageNumberAttr(currentPage_fromEvent);
			}

			PPM.applyTableRowVisibility(proxy, settings);
			proxy.applyToggleAllVisiblity();
		}
	},

/**
 * Excecutes a javascript function.  The param function_unknownType can be a
 * function pointer or a String.  If it is a function point, the function will
 * be called directly.  If it is a string, the string is eval'd
 */
	executeFunction: function (function_unknownType) {
		if (function_unknownType != null) {

			if (typeof function_unknownType == "function") {

				function_unknownType();

			} else if (typeof function_unknownType == "string") {

				if (function_unknownType.length > 0) {
					eval(function_unknownType);
				}

			}
		}
	}
}
var PPM = PandoraPagerManager;

/**
 * PandoraPagerDomProxy is responsible for marshalling calls to the ui compents.
 * The unit tests will supply a fake version of this class to intercept these calls
 * and assert conditions
 *
 * This is the bit of the pager that is not tested
 */
PandoraPagerDomProxy = function(jqTable, settings) {
	this._jqTable = jqTable;
	this._settings = settings;
}

PandoraPagerDomProxy.prototype = {
	private_getTBody: function () {
		return $(this._jqTable).children("tbody");
	},

	private_getTHead: function () {
		return $(this._jqTable).children("thead");
	},

	private_getRows: function () {
		return this.private_getTBody().children("tr");
	},

	private_getPagingControl: function () {
		return $("." + this._settings.table_id + PandoraPagerManager.NAV_CONTAINER_PAGING, this._settings.navContainers);
	},

	hasPagingControl: function () {
		return this.private_getPagingControl().length > 0;
	},

	getRowCount: function() {
		return  this.private_getRows().size();
	},

	isPagingControlBuilt: function() {
		return this.private_getPagingControl().length > 0
	},


	isBoundToStateChangedEvent: function() {
		return $(this._jqTable).attr("boundToEvent") == "true";
	},
	setIsBoundToStateChangedEvent: function() {
		$(this._jqTable).attr("boundToEvent", "true");
	},


	showTableBody: function() {
		this.private_getTBody().show();
	},

	showTableRows: function(blnShow) {
		if (blnShow) {
			this.private_getRows().show();
		} else {
			this.private_getRows().hide();
		}
	},

	showTableRowsByIndex: function(startIndex_currentPage, pageSize) {
		this.private_getRows().gt(startIndex_currentPage - 1).lt(pageSize).show();
	},

	setNavItemVisibility: function(arrVisibleButtons) {
		for (var i = 0; i < PandoraPagerManager.ALL_NAV_CONTAINERS.length; i++) {
			$("." + this._settings.table_id + PandoraPagerManager.ALL_NAV_CONTAINERS[i], this._settings.navContainers).hide();
		}

		for (var i = 0; i < arrVisibleButtons.length; i++) {
			$("." + this._settings.table_id + arrVisibleButtons[i], this._settings.navContainers).show();
		}
	},

	enableNavPrev: function(enabled) {
		var pagingControl = this.private_getPagingControl();

		if (enabled) {
			$(".navPrevDimmed", pagingControl).hide();
			$(".navPrev", pagingControl).css("display", "");
		} else {
			$(".navPrevDimmed", pagingControl).css("display", "");
			$(".navPrev", pagingControl).hide();
		}
	},

	enableNavNext: function(enabled) {
		var pagingControl = this.private_getPagingControl();

		if (enabled) {
			$(".navNextDimmed", pagingControl).hide();
			$(".navNext", pagingControl).css("display", "");
		} else {
			$(".navNextDimmed", pagingControl).css("display", "");
			$(".navNext", pagingControl).hide();
		}
	},

	setNavPageDesc: function(desc) {
		var pagingControl = this.private_getPagingControl();

		$(".navPageNum", pagingControl).html(desc);
	},

/**
 * returns the total number of items on the server
 */
	getTotalItemCount: function () {
		return parseInt($(this._jqTable).attr("totalitemcount"));
	},
	setTotalItemCountAttr: function (itemCount) {
		$(this._jqTable).attr("totalitemcount", itemCount);
	},

/**
 * return the current page index.  1 based
 */
	getCurrentPageNumber: function () {
		var p = parseInt($(this._jqTable).attr("currentpage"));
		return isNaN(p) ? 1 : p;
	},
	setCurrentPageNumberAttr: function (pageNum) {
		$(this._jqTable).attr("currentpage", pageNum);
	},

/**
 * if showAll == true, the paging controls will be hidden and all items will be visible
 */
	getShowAll: function () {
		return $(this._jqTable).attr("showall") == "true";
	},
	setShowAllAttr: function (blnShowAll) {
		$(this._jqTable).attr("showall", blnShowAll ? "true" : false);
	},


	createNavContainers: function(navContainersClassName) {
		var nc = $("." + navContainersClassName, $(this._jqTable).parent());

		if (nc == null || nc.length == 0) {
			//we haven't already created it.  create it now
			$(this._jqTable).parent().append('<span class="left ' + navContainersClassName + '" nowrap="true" />');
			nc = $("." + navContainersClassName, $(this._jqTable).parent());
		}

		return nc;
	},

/**
 * fetch the number of total number pages in the table
 */
	getPageCount: function () {
		return Math.max(Math.ceil(this.getRowCount() / this._settings.pageSize), Math.ceil(this.getTotalItemCount() / this._settings.pageSize));
	},


/**
 * if the current page has less than two visible rows, hide
 * the toggle all checkbox, if there is one.
 *
 * This method assumes that the toggle all checkbox lives
 * in the thead, and it is the only checkbox in the thead
 *
 * todo:  this feels like it breaks encapsulation a little bit.  consider
 *        moving this bit of functionality out of the pager.
 */
	applyToggleAllVisiblity: function () {
		if (this.private_getRows().length > 1) {
			this.private_getTHead().find("input[@type=checkbox]").show();
		} else {
			this.private_getTHead().find("input[@type=checkbox]").hide();
		}
	},

	appendTableRowHTML: function(html) {
		this.private_getTBody().append(html);
	},

	getWindowTitle: function() {
		return window.document.title;
	},

	setWindowTitle: function(strTitle) {
		window.document.title = strTitle;
	},

	scrollToWindowTop: function() {
		if (window.scrollTo) {
			var tableTop = $(this._jqTable).offset().top;

			if (tableTop < document.body.scrollTop) {
				//only scroll if we need to - if we already see the table top, no need to scroll to it
				window.scrollTo(0, tableTop);
			}
		}
	},

	setPage: function(page) {
		var windowTitle = PandoraPagerManager.buildWindowTitle(this.getWindowTitle(), this._settings.friendlyName, page);

		EXANIMO.managers.StateManager.setState(this._settings.table_id + "," + page, windowTitle);
	},

	fetchRows: function(url_rowprovider, countRowsOnBrowser, countRowsNeeded, function_callBack) {
		var url = url_rowprovider + "&countRowsOnBrowser=" + countRowsOnBrowser + "&countRowsNeeded=" + countRowsNeeded;

		$.ajax({
			type: "get",
			url: url,
			dataType: "html",
			processData: false,
			success: function_callBack
		});
	},


/**
 * build the navigation controls
 */
	makeNav: function (blnAllowSkipToEnd ) {
		var me_domProxy = this;
		var str = '';

		str += '<table cellpadding="0" cellspacing="0" border="0" style="width: 168px"><tr>';


		str += '<td class="' + this._settings.table_id + PandoraPagerManager.NAV_CONTAINER_PAGING + '" align="left" nowrap="true" style="border: none;">';
		str += '<span class="navPrevDimmed">';
		str += '<img src="/images/first_dim.gif" height="16" width="16" border="0">';
		str += '<img src="/images/prev_dim.gif" height="16" width="16" border="0">';
		str += '</span>';

		str += '<span class="navPrev" style="display:none">';
		str += '<a href="#"><img src="/images/first.gif" height="16" width="16" border="0"></a>';
		str += '<a href="#"><img src="/images/prev.gif" height="16" width="16" border="0"></a>';
		str += '</span>';

		str += '<span class="navPageNum" style="font-size:10px;">' + this.getCurrentPageNumber() + ' of ' + this.getPageCount() + '</span>';

		str += '<span class="navNextDimmed" style="display:none">';
		str += '<img src="/images/next_dim.gif" height="16" width="16" border="0">';
		if (blnAllowSkipToEnd) {
			str += '<img src="/images/last_dim.gif" height="16" width="16" border="0">';
		}
		str += '</span>';

		str += '<span class="navNext">';
		str += '<a href="#"><img src="/images/next.gif" height="16" width="16" border="0"></a>';
		if (blnAllowSkipToEnd) {
			str += '<a href="#"><img src="/images/last.gif" height="16" width="16" border="0"></a>';
		}
		str += '</span>';

		str += '</td>';

		if (blnAllowSkipToEnd) {
			str += '<td class="' + this._settings.table_id + PandoraPagerManager.NAV_CONTAINER_SHOWALL + '" align="right" style="border: none; padding-top: 4px; font-size:10px; ">';
			str += '<a href="#" class="navShowAll"><img src="/images/show_all_button.gif" width="50" height="12" border="0" alt="show all" onMouseOver="this.src=\'/images/show_all_button_hover.gif\';" onMouseOut="this.src=\'/images/show_all_button.gif\';"></a>';
			str += '</td>';
		}


		str += '<td class="' + this._settings.table_id + PandoraPagerManager.NAV_CONTAINER_DONOT_SHOWALL + '" style="font-size: 10x; padding: 6px; margin-left: 6px; display: none; border: none;">';
		str += '<a href="#" class="navReturnToPagedView"><img src="/images/dont_show_all_button.gif" width="81" height="12" border="0" alt="don\'t show all" onMouseOver="this.src=\'/images/dont_show_all_button_hover.gif\';" onMouseOut="this.src=\'/images/dont_show_all_button.gif\';"></a>';
		str += '</td>';

		str += '<td class="' + this._settings.table_id + PandoraPagerManager.NAV_CONTAINER_FETCHING_DATA + '" style="font-size: 10x; padding: 3px 7px 0px 7px; margin-left: 5px; display: none; border: none;">';
		str += 'fetching data...';
		str += '</td>';

		str += '</tr></table>';

		for (var i = 0; i < this._settings.navContainers.length; i++) {
			var nc = $(this._settings.navContainers[i]);
			nc.children().remove();
		}

		this._settings.navContainers.append(str);
		this._settings.navContainers.after('<div style="clear: both" />');

		$("." + this._settings.table_id + PandoraPagerManager.NAV_CONTAINER_DONOT_SHOWALL, this._settings.navContainers).find("a").click(function() {
			PandoraPagerManager.eventHandler_onDoNotShowAll(me_domProxy);

			return false;
		});

		$("." + this._settings.table_id + PandoraPagerManager.NAV_CONTAINER_SHOWALL, this._settings.navContainers).find("a").click(function() {
			PandoraPagerManager.eventHandler_onShowAll(me_domProxy);

			return false;
		});

		$("." + this._settings.table_id + PandoraPagerManager.NAV_CONTAINER_PAGING, this._settings.navContainers).find("a").click(function() {
			this.blur();

			// get rid of dotted outline around link
			var src = $(this).children().eq(0).attr("src");
			var image = src.substring(src.lastIndexOf("/") + 1);

			var navButton;
			if (image == 'next.gif') {
				navButton = PandoraPagerManager.NAV_BUTTON_NEXT;
			} else if (image == 'prev.gif') {
				navButton = PandoraPagerManager.NAV_BUTTON_PREVIOUS;
			} else if (image == 'first.gif') {
				navButton = PandoraPagerManager.NAV_BUTTON_FIRST;
			} else if (image == 'last.gif') {
				navButton = PandoraPagerManager.NAV_BUTTON_LAST;
			}

			PandoraPagerManager.eventHandler_onPage(me_domProxy, navButton)

			return false;
		});
	}
}

PandoraPagerManager.PAGE_NUMBER_SHOW_ALL = "all";

PandoraPagerManager.NAV_BUTTON_FIRST = "first";
PandoraPagerManager.NAV_BUTTON_PREVIOUS = "previous";
PandoraPagerManager.NAV_BUTTON_NEXT = "next";
PandoraPagerManager.NAV_BUTTON_LAST = "last";

PandoraPagerManager.NAV_CONTAINER_SHOWALL = "nav_container_showall";
PandoraPagerManager.NAV_CONTAINER_DONOT_SHOWALL = "nav_container_donot_showall";
PandoraPagerManager.NAV_CONTAINER_PAGING = "nav_container_paging";
PandoraPagerManager.NAV_CONTAINER_FETCHING_DATA = "nav_container_fetching_data";

PandoraPagerManager.ALL_NAV_CONTAINERS = [
		PandoraPagerManager.NAV_CONTAINER_SHOWALL,
		PandoraPagerManager.NAV_CONTAINER_DONOT_SHOWALL,
		PandoraPagerManager.NAV_CONTAINER_PAGING,
		PandoraPagerManager.NAV_CONTAINER_FETCHING_DATA
		];
})();