(function($) {
	$.widget('tadeo.ajaxpager', {
		options: {
			pageParamName: 'page',
			initialPage: 1,
			totalPages: null,
			pageUrl: null,
			pagesInfoFormat: '__currentPage__ / __totalPages__', //TODO: handle format options
			additionalParams: {},
			init: function(){},
			startLoad: function(){},
			stopLoad: function(){},
			pageChange: function(){}
		},

		_init: function(){
			var that = this;

			this.element.bind("ajaxpagerinit", this.options.init);
			this.element.bind("ajaxpagerstartload", this.options.startLoad);
			this.element.bind("ajaxpagerstopload", this.options.stopLoad);
			this.element.bind("ajaxpagerpagechange", this.options.pageChange);

			this.$pager = this.options.$pager || this.element;
			this.$pagesContainer = this.options.$pagesContainer || this.$pager.find('.ap_pages_container');
			this.$pagesMask = this.options.$pagesMask || this.$pager.find('.ap_pages_mask');
			this.$prevButton = this.options.$prevButton || this.$pager.find('.ap_prev_button');
			this.$nextButton = this.options.$nextButton || this.$pager.find('.ap_next_button');
			this.$pagesInfo = this.options.$pagesInfo || this.$pager.find('.ap_pages_info');

			this.loadingPage = null;
			this.pages = [];
			this.currentPage = this.options.initialPage; // 1 based current page number
			this.totalPages = this.options.totalPages; // 1 based max page number
			this.pages[this.currentPage-1] = this.$pagesContainer.children();
			this.$prevButton.bind('click', function($event){
				that.advancePage(-1);
				$event.preventDefault();
			})
			this.$nextButton.bind('click', function($event){
				that.advancePage(1);
				$event.preventDefault();
			})
			
			this.$pagesContainer.css('position','relative');
			this.$pagesMask.css('overflow','hidden');
			this.pages[this.currentPage-1].load(function(){
				that.$pagesMask.height(that._getPageDimensions(that.currentPage).height);
			})
		},

		advancePage: function(delta){
			var constraintedPage = Math.max(1, Math.min(this.totalPages, this.currentPage + delta));
			this.gotoPage(constraintedPage);
		},

		gotoPage: function(pageNumber){
			if (this._isPageLoaded(pageNumber)){
				this.currentPage = pageNumber;
				this._showCurrentPage();
			}else{
				this._loadPage(pageNumber);
			}
		},

		_getPage$Wrapper: function(pageNumber){
			return this.pages[pageNumber-1];
		},

		_isPageLoaded: function(pageNumber){
			return !!this._getPage$Wrapper(pageNumber);
		},

		_loadPage: function(pageNumber) {
			var that = this;
			if (this.loadingPage) return;
			this.loadingPage = pageNumber;
			this._loadingFeedback(true);
			var data = {};
			data[this.options.pageParamName] = this.loadingPage;
			$.extend(data, this.options.additionalParams);
			$.ajax({
				url: this.options.pageUrl,
				data: data,
				success: function(data, textStatus, jqXHR){
					that._onPageLoadSuccess(data);
				},
				error: function(jqXHR, textStatus, errorThrown){
					that._onPageLoadError();
				}
			});
		},

		_onPageLoadSuccess: function(data){
			var $data = $(data);
			this.currentPage = this.loadingPage;
			this.loadingPage = false;
			this._loadingFeedback(false);
			this.pages[this.currentPage-1] = $data;
			var prevElement = this._getPrevElement(this.currentPage);
			if (prevElement){
				prevElement.after($data);
			}else{
				this.$pagesContainer.prepend($data);
				var pageDimensions = this._getPageDimensions(this.currentPage);
				this.$pagesContainer.css('top',-pageDimensions.top-pageDimensions.height+'px');
			}
			this._showCurrentPage();
		},

		_onPageLoadError: function(){
			this.loadingPage = false;
			this._loadingFeedback(false);
			alert('Error when loading a page, connection with server might be lost.');
		},

		_getPrevElement: function(pageNumber){
			for (var i=pageNumber-2; i>=0 ; i--) {
				if (this.pages[i]){
					return this.pages[i].last();
				}
			};
			return false;
		},

		_loadingFeedback: function(direction){
			this.$pager.toggleClass('loading', direction);
		},

		_showCurrentPage: function(){
			this._updatePagesInfo();
			var pageDimensions = this._getPageDimensions(this.currentPage);
			this.$pagesMask.animate({
					height: pageDimensions.height+'px'
				}, 400, function() {
				// Animation complete.
			 });
			this.$pagesContainer.animate({
					top: -pageDimensions.top+'px'
				}, 400, function() {
				// Animation complete.
			 });
		},

		_getPageDimensions: function(pageNumber){
			var $page = this.pages[pageNumber-1]
			var top = $page[0].offsetTop;
			var $last = $page.last();
			var bottom = $last[0].offsetTop + $last[0].clientHeight;
			return {
				top: top,
				bottom: bottom,
				height: bottom-top
			}
		},

		_updatePagesInfo: function(){
			this.$pagesInfo.html(this.currentPage + ' / ' + this.totalPages);
			this.$prevButton.toggleClass('disabled', this.currentPage == 1);
			this.$nextButton.toggleClass('disabled', this.currentPage == this.totalPages);
		},

	});
})(jQuery);
