Pager = function (paging, pageChangingCallback, outOfBoundsCallback, isLooped) {
	this.paging = paging;
	this.paging.index = 0;
	this.paging.getTotalIndex = function () {
		var idx = this.index + this.itemsOnPage * (this.pageNumber - 1);
		return idx;
	};
	this.paging.getUpperBound = function (pageNumber) {
		pageNumber = pageNumber || this.pageNumber;
		return pageNumber < this.totalPages ? this.itemsOnPage : this.totalItems - (pageNumber - 1) * this.itemsOnPage;
	};
	this.paging.getPagerInfo = function () {
		return (this.getTotalIndex() + 1) + " | " + this.totalItems;
	};
	this.isLooped = isLooped || false;
	
	var _self = this;
	
	this._pageChangedCallback = function (result) {
		if (result && _self._newPageNumber != _self.paging.pageNumber) {
			Logger.write("Page changed successfully", "Pager", true);
			if (_self._newIndex || _self._newIndex === 0) {
				_self.paging.index = _self._newIndex;
				_self._newIndex = null;
			} else {
				_self.paging.index = _self._newPageNumber > _self.paging.pageNumber 
					? 0 
					: _self.paging.getUpperBound(_self.paging.totalPages) - 1;
			}
			_self.paging.pageNumber = _self._newPageNumber;
		}
	};
	
	this._pageChangingCallback = function (onPage, newPageNumber) {
		if (pageChangingCallback) {
			pageChangingCallback(onPage, newPageNumber, _self._pageChangedCallback);
		}
	};
	
	this._outOfBoundsCallback = function (num) {
		if (outOfBoundsCallback) {
			outOfBoundsCallback(num);
		}
	};
	
	this._samePageUpdateNext = function () {
		
	};
	
	this._samePageUpdatePrev = function () {
		
	};
	
	this.setPageNumber = function (newPageNumber, newIndex) {
		this._newPageNumber = newPageNumber;
		this._newIndex = newIndex;
		this._pageChangingCallback(this.paging.itemsOnPage, this._newPageNumber);
	};
	
	this.next = function () {
		Logger.write("Next item", "Pager", false);
		if (this.paging.index < (this.paging.getUpperBound() - 1)) {
			Logger.write("Same page", "Pager", true);
			this.paging.index++;
			this._samePageUpdateNext();
			return true;
		} else if (this.paging.pageNumber < this.paging.totalPages) {
			Logger.write("Next page", "Pager", true);
			this.setPageNumber(this.paging.pageNumber + 1, 0);
		} else if (this.isLooped) {
			this.setPageNumber(1, 0);
		} else {
			this._outOfBoundsCallback(1);
		}
	};
	
	this.prev = function () {
		Logger.write("Prev item", "Pager", true);
		if (this.paging.index > 0) {
			this.paging.index--;
			this._samePageUpdatePrev();
			return true;
		} else if (this.paging.pageNumber > 1) {
			Logger.write("Prev page", "Pager", true);
			this.setPageNumber(this.paging.pageNumber - 1, this.paging.itemsOnPage - 1);
		} else if (this.isLooped) {
			this.setPageNumber(this.paging.totalPages, this.paging.getUpperBound(this.paging.totalPages) - 1);
		} else {
			this._outOfBoundsCallback(-1);
		}
	};
};

ListPager = function (paging, listName, visibleItemsCount, pageChangingCallback, outOfBoundsCallback) {
	this.__proto__ = new Pager(paging, pageChangingCallback, outOfBoundsCallback);
	
	this.listName = listName;
	
	var _self = this;
	
	this.scroll = {
		visibleItemsCount: visibleItemsCount,
		pageNumber: 0,
		getTotalPages: function () {
			Logger.write("Total items: " + _self.paging.totalItems + "; visible items: " + this.visibleItemsCount, "Pager", true);
			return Math.ceil(_self.paging.totalItems / this.visibleItemsCount);
		},
		updatePageNumber: function (num) {
			var idx = _self.paging.getTotalIndex();
			if ((!num) || (num > 0 && (idx + 1) % this.visibleItemsCount == 1) || (num < 0 && (idx + 1) % this.visibleItemsCount == 0)) {
				this.pageNumber = Math.ceil((idx + 1) / this.visibleItemsCount) - 1;
				Logger.write("Updating scroll page number, idx: " + idx + "; page number: " + this.pageNumber, "Pager", true);
			}
		}
	};
	
	this._pageChangedCallback = function (result) {
		if (result && _self._newPageNumber != _self.paging.pageNumber) {
			Logger.write("Page changed successfully", "Pager", true);
			if (_self._newIndex || _self._newIndex === 0) {
				_self.paging.index = _self._newIndex;
			} else {
				_self.paging.index = _self._newPageNumber > _self.paging.pageNumber ? 0 : _self.paging.itemsOnPage - 1;
			}
			_self.paging.pageNumber = _self._newPageNumber;
			_self.scroll.updatePageNumber();
			$(_self.listName).sfList('move', _self.paging.index);
			$(_self.listName).sfList('focus');
		}
	};
	
	this.setPageNumber = function (newPageNumber, newIndex) {
		this._newPageNumber = newPageNumber;
		this._newIndex = newIndex;
		this._pageChangingCallback(this.paging.itemsOnPage, this._newPageNumber);
	};
	
	this._pageChangingCallback = function (onPage, newPageNumber) {
		if (pageChangingCallback) {
			pageChangingCallback(onPage, newPageNumber, _self._pageChangedCallback);
		}
	};
	
	this._samePageUpdateNext = function () {
		this.scroll.updatePageNumber(1);
		$(this.listName).sfList('move', this.paging.index);
	};
	
	this._samePageUpdatePrev = function () {
		this.scroll.updatePageNumber(-1);
		$(this.listName).sfList('move', this.paging.index);
	};
}