var ScrollerV3 = $.Class.create(
{
	//**************************//
	//customisable variables
	//**************************//
	_arrowcontrol: false,
	_hashcontrol: false,
	_browsercontrol: false,
	_keyboardcontrol: false,
	_paginationcontrol: false,
	
	_element: null,
	
	_container:
	{
		width: null,
		height: null
	},
	
	_item:
	{
		width: null,
		height: null
	},
	
	_paginationTopDiv: null,
	_paginationBottomDiv: null,
	_paginationWindow: 5,
	
	_cacheLength: 1,
	_itemsPerPage: 4,
	
	_url: null,
	_id: null,
	_mode: null,
	_hashTag: null,
	
	_animation:
	{
		duration: 600,
		skipDuration: 80,
		type: "swing"
	},
	
	_removeBorderLastItem: false,
	
	//**************************//
	//not customisable variables
	//**************************//
	_parent: null,
	
	_stage: 0,
	_skipStage: null,
	_index: 0,
	_totalItems: 0,
	
	_isScrolling: false,
	_isActive: false,
	_isPaginated: false,
	_cancelFetching: false,
	_isFetching: false,
	
	_fetchingRequest: null,
	
	_leftArrow: null,
	_rightArrow: null,
	
	_cache: [],
	
	_fakeCollectionLength: 10,
	_fakeStage: [{"id":"301611","price":"197.90","title":"Yardmaster 68 ZGEY Metal Shed","product_url":"Garden-Buildings-Direct/Yardmaster-68-ZGEY-Metal-Shed","type":"deal","pool":"23","image":"https://roberto.3tb.co/product-images/543/nologosize/982563_nologosize.jpg"},{"id":"5127071","price":"119.00","title":"Rowlinson Wooden Trellis Arch","product_url":"tesco-direct/Rowlinson-Wooden-Trellis-Arch","type":"deal","pool":"23","image":"https://roberto.3tb.co/product-images/582/nologosize/3209573_nologosize.jpg"},{"id":"1154145","price":"299.99","title":"London Water Feature","product_url":"Homebase/London-Water-Feature","type":"deal","pool":"23","image":"https://roberto.3tb.co/product-images/983/nologosize/862831_nologosize.jpg"},{"id":"526807","price":"19.98","title":"B&Q Rotary Airer 30m","product_url":"B-and-Q/B-and-Q-Rotary-Airer-30m","type":"deal","pool":"23","image":"https://roberto.3tb.co/product-images/265/nologosize/369927_nologosize.jpg"}],
	_itemHTML: "<div class='quad' style='top:0px; float:left;height:{height}px; width:{width}px;'>",
	_fetchedItems: "",
	
	initialize: function(options)
	{
		for(var option in options)
		{
			this["_" + option] = options[option];
		}
	
		this._element = $(this._element);
		this._element.css("position", "relative");

		this._parent = this._element.parents("div.dscroll");
		this._parent.scrollLeft(0);
		
		this._itemHTML = this._itemHTML.replace("{height}", this._element.find("div.quad").css("height").toInt());
		this._itemHTML = this._itemHTML.replace("{width}", this._element.find("div.quad").css("width").toInt());
		
		this._createArrow("left");
		this._createArrow("right");

		this._processUrl();
	},
	
	_addEvents: function()
	{
		if (this._keyboardcontrol)
		{
			this._setKeyboardControl();
		}
		
		if (this._arrowcontrol)
		{
			this._setArrowControl();
		}
		
		if (this._paginationcontrol)
		{
		
			this._setPagination();
			this._updatePagination();
		}
	},
	
	_setKeyboardControl: function()
	{
		//empty for the moment.	
	},
	
	_setArrowControl: function()
	{
		var self = this;
		this._parent.parent().bind("mouseover", function()
		{
			self._isActive = true;
			
			$("input[type=text]").blur();
			
			self._refreshArrowStatus();
		});
		
		this._parent.parent().bind("mouseout", function()
		{
			self._isActive = false;
			
			self._leftArrow.hide();
			self._rightArrow.hide();
		});
	},
	
	_setPagination: function()
	{
		

		if ((this._paginationTopDiv != null) && (this._paginationBottomDiv != null))
		{
			PaginationHeader.initialize(
			{
				containerTop: $(this._paginationTopDiv),
				containerBottom: $(this._paginationBottomDiv),
				window: this._paginationWindow,
				slider: this
			});
			
			this._isPaginated = true;
		}
	},
	
	isBrowsable: function()
	{
		return this._browsercontrol;
	},
	
	_createArrow: function(direction)
	{
		var self = this;
		
		var arrow = $(document.createElement("div")).css(
		{
			'height': '60px',
			'width': '60px',
			'background-image': (direction == "left") ?
								"url('/images/global/arrows_left.png')" :
								"url('/images/global/arrows_right.png')",
			'background-repeat': 'no-repeat',
			'background-position': 'left',
			'cursor': 'pointer',
			'left' : (direction == "left") ? 20 : this._container.width - 80,
			'z-index': 99,
			'top': this._container.height / 2 - 30,
			'position': 'absolute',
			'float': 'none',
			'display': 'none'
        });
		
		if (direction == "left")
		{
			arrow.click(function(){self.scrollLeft();});
			this._leftArrow = arrow;
		}
		else
		{
			arrow.click(function(){self.scrollRight();});
			this._rightArrow = arrow;
		}
		
		this._parent.parent().append(arrow);
	},
	
	_hashExists: function()
	{
		if (location.hash.indexOf(this._hashTag) == -1)
		{
			return false;
		}
		else
		{
			return true;
		}
	},

	getHashIdValue: function()
	{
		if (this._hashExists())
		{
			var hash = location.hash;
			var indexOfHash = hash.indexOf(this._hashTag);
			var hashContent = hash.substring(indexOfHash);
			
			var startPoint = hashContent.indexOf("-") + 1;
			var endPoint = startPoint + hashContent.substring(startPoint).indexOf("-");
			return Number(hashContent.substring(startPoint, endPoint));
		}
		
		return -1;
	},
	
	getHashStageValue: function()
	{
		if (this._hashExists())
		{
			var indexOfHash = location.hash.indexOf(this._hashTag);
			var hashContent = location.hash.substring(indexOfHash);
			return Number(hashContent.substring(hashContent.indexOf("(") + 1, hashContent.indexOf(")")));
		}
		
		return -1;
	},
	
	_removeFromHash: function()
	{
		var currentHash = newHash = location.hash;
		
		if (this._hashExists())
		{
			newHash = currentHash.substring(0, location.hash.indexOf(this._hashTag));
			
			var remainPart = currentHash.substring(location.hash.indexOf(this._hashTag));
			
			if (remainPart.indexOf(")") != (remainPart.length - 1))
			{
				newHash += remainPart.substring(remainPart.indexOf(")") + 1);
			}
		}

		if (newHash == "")
		{
			newHash = ".";
		}
		
		return newHash;
	},

	_updateHash: function(direction, notUpdateAddressBar, isJumping)
	{
		newHash = this._removeFromHash();
		
		var newStage = (isJumping != null) ? this._stage : 0;
		
		if (isJumping == null)
		{
			if (direction == "right")
			{
				newStage = this._stage + 1;
				this._index++;
			}
			else if (direction == "left")
			{
				newStage = this._stage - 1;
				this._index--;
			}
		}
		
		if (this._hashcontrol && notUpdateAddressBar == null)
		{
			var newHash = newHash.replace("#", "") + this._hashTag + "-" + this._id + "-(" + newStage + ")";
			$.history.load(newHash);
		}

		this._stage = newStage;
	},
	
	_refreshArrowStatus: function()
	{
		if (this.isScrollable("left"))
		{
			this._leftArrow.show();
		}
		else
		{
			this._leftArrow.hide();			
		}

		if (this.isScrollable("right"))
		{
			this._rightArrow.show();
		}
		else
		{
			this._rightArrow.hide();			
		}
	},
	
	scrollLeft: function(notUpdateAddressBar)
	{
		var dir = "left";
		if (this.isScrollable(dir))
		{
			this._updateHash(dir, notUpdateAddressBar);		
			
			this._scroll(dir);						
		}
	},
	
	scrollRight: function(notUpdateAddressBar)
	{
		var dir = "right";
		
		if (this.isScrollable(dir))
		{
			this._updateHash(dir, notUpdateAddressBar);
			
			this._scroll(dir);
		}
	},
	
	isScrollable: function(direction)
	{
		if (this._isScrolling)
		{
			return false;
		}

		if (direction == "right")
		{
			return this._index != (this._cache.length - 1);
		}
		else if (direction == "left")
		{
			return (this._stage > 0) && (this._index > 0);
		}
	},
	
	isScrollableFromOutside: function(direction)
	{
		return this.isScrollable(direction);
	},
	
	_scroll: function(direction, isSkip)
	{
		var self = this;
		
		this._cancelFetching = this._isFetching;
		if (this._cancelFetching && (this._fetchingRequest != null))
		{
			this._fetchingRequest.abort();
			this._isFetching = false;
		}
		
		var dir = direction == "right" ? 
					this._parent.scrollLeft() + this._container.width :
					this._parent.scrollLeft() - this._container.width;
		
		if (!isSkip)
		{
			this._updateSlider(direction);
			this._updatePagination();
		}

		this._isScrolling = true;
		this._parent.animate(  { opacity: 0.60 },1);
		this._parent.animate({scrollLeft: dir},
								!isSkip ? this._animation.duration : this._animation.skipDuration,
								this._animation.type,
								function() 
								{
									self._endScrolling(direction);
									
									if (isSkip)
									{									
										if (self._skipStage != self._stage)
										{
											self._updateHash(direction, true);
											self._scroll(direction, true);
										}
										else
										{
											self._updateHash(null, null, true);
											self._updatePagination();
										}
									}
											
								});this._parent.animate(  { opacity: 100 },'fast');

	},
	
	_endScrolling: function(direction)
	{
		this._isScrolling = false;
		
		if (this._arrowcontrol)
		{
			this._refreshArrowStatus();
		}
		
		if (!this._isFetching && !this._cancelFetching)
		{
			this._injectNewStage(direction);
		} 
		else if (this._cancelFetching)
		{
			this._cancelFetching = false;
		}
		
		this._refreshArrowStatus();
	},
	
	_injectNewStage: function(direction)
	{
		if (((this._stage < this._cacheLength) && (direction == "left")) ||
		    ((this._stage > (Number(this._totalItems).normalize(this._itemsPerPage) - this._cacheLength)) && (direction == "right")))
		{
			this._setScrollablePanelWidth(direction);
		}
		else
		{
			this._fetchFromCache(direction);
		}
	},
	
	_updateSlider: function(direction)
	{
		if (((this._index == 0) && (direction == "left") && (this._stage >= this._cacheLength)) || ((this._index == (this._cache.length - 1)) && (direction == "right")))
		{
			this._collectNewStage(direction);
		}
	},
	
	_fetchFromCache: function(direction)
	{
		if (direction == "left")
		{
			var html = this._generateNewStage(this._cache[this._index - 1]);
			this._element.prepend(html);
		}
		else
		{
			var html = this._generateNewStage(this._cache[this._index + 1]);
			this._element.append(html);	

			if (Number(this._totalItems).normalize(this._itemsPerPage) == (Number(this._stage) + 1))
			{
				this._restyleLastItem();
			}
		}
		
		this._setScrollablePanelWidth(direction);
		
		this._refreshArrowStatus();
	},
	
	_fetchItems: function(direction)
	{
		var self = this;
		
		this._isFetching = true;
		
		this._fetchingRequest = $.ajax(
		{
            type: 'get',
            url: this._url + "slide_no=" + this._stage + "&per_page=" + this._itemsPerPage + "&id=" + this._id + "&mode=" + this._mode,
			success: function(jsonData)
			{
				var newData;
				try
				{
					newData = JSON.parse(jsonData);
                } 
				catch(x) 
				{
					try
					{
						newData = $.evalJSON(jsonData)
                    }
					catch(y)
					{
						return;
                    }
				}

				if ((newData != null) && (newData.length > 0))
				{
					self._totalItems = newData[0].pool;
				}

				self._afterFetching(newData);
			}
		});
	},
	
	_setScrollablePanelWidth: function(direction)
	{
		var units = 0;
		
		if ((this._index >= this._cacheLength) && (this._index <= (this._cache.length - 1 - this._cacheLength)))
		{
			units = ((this._cacheLength * 2) + 1);
		}
		else if (this._index < this._cacheLength)
		{
			for(var i = 0; (i < this._cache.length) && (i <= (this._index + this._cacheLength)); i++)
			{
				units++;
			}
		}
		else
		{
			for(var i = (this._cache.length - 1); (i >= 0) && (i >= (this._index - this._cacheLength)); i--)
			{
				units++;
			}		
		}
		
		this._element.css("width", (units * this._container.width));
		
		var currentLeftPosition = this._parent.scrollLeft();
		
		if ((direction != null) && (this._element.find("div.quad").length > units))
		{
			if (direction == "left")
			{
				this._element.find("div.quad").last().removeWithoutLeaking();
				
				if (this._stage >= this._cacheLength)
				{
					this._parent.scrollLeft(currentLeftPosition + this._container.width);
				}
			}
			else
			{
				this._element.find("div.quad").first().removeWithoutLeaking();
				
				if (units == ((2 * this._cacheLength) + 1))
				{
					this._parent.scrollLeft(currentLeftPosition - this._container.width);
				}
			}
		}
		else
		{
			if ((direction != null) && (direction == "left"))
			{
				if (this._stage >= (Number(this._totalItems).normalize(this._itemsPerPage) - this._cacheLength))
				{
					this._parent.scrollLeft(currentLeftPosition + this._container.width);
				}
			}
		}
	},
	
	_getNewData: function(data, direction)
	{
		if (data.length == 0)
		{
			return [];
		}
		else if ((data.length % this._itemsPerPage) != 0)
		{
			var stage = [];
			if (direction == null)
			{
				stage = data.slice(data.length - (data.length % this._itemsPerPage), data.length);
			}
			else
			{
				if (direction == "right")
				{
					stage = data.slice(((this._cacheLength  + 1)* this._itemsPerPage));
				}
				else
				{
					stage = data.slice(0, this._cacheLength * this._itemsPerPage);	
				}
			}
			
			/*var samples = ((this._cache.length * this._itemsPerPage) > 0) ? this._cache.expand() : stage;
			var currentStageLength = stage.length;
			for(var i = 0; i < (this._itemsPerPage - currentStageLength) && (currentStageLength > 0); i++)
			{
				var randomIndex = Math.floor(Math.random() * samples.length);
				stage.push(samples[randomIndex]);
			}*/

			return stage;
		}
		else
		{
			if ((((this._itemsPerPage * this._cacheLength) * 2) + this._itemsPerPage) >= (Math.abs(data.length / this._itemsPerPage)))
			{
				if (direction == "right")
				{
					return data.slice((this._cacheLength  + 1) * this._itemsPerPage);
				}
				else
				{
					return data.slice(0, this._cacheLength * this._itemsPerPage);				
				}
			}
			else if (this._stage >= this._cacheLength)
			{
				if (direction == "right")
				{
					return data.slice((this._cacheLength + 1) * this._itemsPerPage);
				}
				else
				{
					return data.slice(0, this._cacheLength * this._itemsPerPage);
				};
			}
			else
			{
				if (direction == "right")
				{
					return data.slice((this._stage + 1) * this._itemsPerPage);
				}
				else
				{
					return data.slice(0, (this._stage * this._itemsPerPage));
				};
			}
		}
	},
	
	_generateNewStage: function(data)
	{
		var self = this;
		
		var html = this._itemHTML;

		$(data).each(function(index)
		{
			html += HtmlFormatterV3.getHtml(this, self._item);
		});
		html += "</div>";	

		return html;
	},
	
	_restyleLastItem: function()
	{
		if (this._removeBorderLastItem)
		{
			this._element.find("div.quad").last().find("> div").last().css("border-right", "0px");
		}
	},
	
	_afterFetching: function()
	{
		return false;
	},
	
	_processUrl: function()
	{
		if (!this._hashExists())
		{
			this._resetState();
			this._getPreloadedItemsInformation();
		}
		else
		{
			this.goToStage(this.getHashStageValue(), false);
			this._addEvents();
		}
	},
	
	goToStage: function(newStage, scrollingTo)
	{
		var oldId = this._id;
		this._id = Number(this.getHashIdValue()) > 0 ? Number(this.getHashIdValue()) : this._id;
		
		if ((oldId != this._id) && scrollingTo)
		{
			this.goToStage(newStage, false);
			return;
		}
		
		if ((this._stage == newStage) && scrollingTo)
		{
			return;
		}
		
		scrollingTo = scrollingTo ? this._isNewStageInCache(newStage) : false;

		if (scrollingTo)
		{
			this._skipScrollingTo(newStage);
		}
		else
		{
			this._stage = newStage;
		
			this._updateHash(null, null, true);
			
			this._getRequestedStage();
		}		
	},
	
	_skipScrollingTo: function(newStage)
	{
		this._skipStage = newStage;
		
		var offset = newStage - this._stage;
		
		//Uncomment this out if you want the slider to process every action occured on the browser's back/forward buttons
		//(e.g. several rapid clicks on them).
		//That sounds ideal, and the slider's result will be always correct. But I highly don't reccomend it.
		//This action will break the synchronation mechanism introduced to keep two indipendent systems
		//(browser's navigation bar and the jQuery sliding effect) under control as much as possible when an 
		// an action on back/forward button fires an action on the slider.
		//
		//this._isScrolling = false;
		
		if (offset > 0)
		{
			if (Math.abs(offset) == 1)
			{
				this.scrollRight();
			}	
			else
			{
				this._scroll("right", true);
			}
		}
		else
		{
			if (Math.abs(offset) == 1)
			{
				this.scrollLeft();
			}
			else
			{
				this._scroll("left", true);
			}
		}
	},
	
	_isNewStageInCache: function(newStage)
	{	
		if (newStage > this._stage)
		{
			var offset = newStage - this._stage;
			if (offset <= (this._cache.length - this._index - 1))
			{
				 return true;
			}
		}
		else if (newStage < this._stage)
		{
			var offset = this._stage - newStage;
			if (offset <= this._index)
			{
				return true;
			}
		}
		
		return false;
	},
	
	_getPreloadedItemsInformation: function()
	{
		var self = this;
		this._afterFetching = function(data)
		{
			self._cache = data.subset(self._itemsPerPage, []);
			
			self._isFetching = false;
				
			self._updatePagination();
			
			self._addEvents();
			
			self._afterFetching = function(){return false;};
			self._log();
		};
		
		this._fetchItems();
	},
	
	_log: function()
	{
	},
	
	_getRequestedStage: function()
	{
		var self = this;
		
		/*var loadingDiv = this._itemHTML + "</div>";
		this._element.html(loadingDiv);
		this._element.find("div.quad").first().css({
                'background-image':'url("/images/loader.gif")',
                'background-position':'center',
                'background-repeat':'no-repeat'
            });*/
		
		this._afterFetching = function(data)
		{
			self._cache = data.subset(self._itemsPerPage, []);
			self._index = self._getCurrentIndex();
			
			// That basicaly means: cache empty
			if (self._index == -1)
			{
				self._resetState();
				self._getPreloadedItemsInformation();			
			};
			
			var html = "";
			for (var i = 0; i < self._cache.length; i++)
			{
				html += self._generateNewStage(self._cache[i]);
			}
			
			self._setScrollablePanelWidth();
				
			/******* Slide down effect ******/
			var newElement = self._element.clone();
			newElement.html(html);
			newElement.hide();
			self._parent.prepend(newElement);
			self._parent.scrollLeft(self._container.width * self._index);
			newElement.slideDown(1000, function()
			{
				self._element.removeWithoutLeaking();

				self._element = newElement;
				
				if (Number(self._totalItems).normalize(self._itemsPerPage) < (self._stage + self._cacheLength))
				{
					self._restyleLastItem();
				}
			});
			/********************************/
			
			self._isFetching = false;
				
			self._updatePagination();
			
			self._afterFetching = function(){return false;};
			self._log();
		};
		
		this._fetchItems();
	},
	
	_collectNewStage: function(direction)
	{
		var self = this;
		
		this._afterFetching = function(newData)
		{
			if ((newData != null) && (newData.length > 0))
			{
				newData = self._getNewData(newData, direction).subset(self._itemsPerPage, self._cache);

				if (direction == "right")
				{
					for(var i = 0; i < newData.length; i++)
					{
						self._cache.push(newData[i]);
					}
				}
				else
				{
					for(var i = 0; i < newData.length; i++)
					{
						self._cache.splice(i, 0, newData[i]);
						self._index++;
					}
				}
				
				if (!self._isScrolling)
				{
					self._injectNewStage(direction);
				}
			}
			
			self._isFetching = false;
			
			self._afterFetching = function(){return false;};
			self._log();
		}
		
		this._fetchItems(direction);
	},
	
	_updatePagination: function()
	{
		if (this._isPaginated)
		{
			PaginationHeader.setLimit(Number(this._totalItems).normalize(this._itemsPerPage));
			PaginationHeader.updatePages(this._stage);
		}
	},
	
	regenerate: function(newId)
	{
		if ((newId != null) && (!isNaN(newId)))
		{
			this._id = Number(newId);
			
			this._resetState();	
			
			this._updateHash();
			
			this._getRequestedStage();
		}
	},
	
	_resetState: function()
	{
		this._stage = 0;
		this._index = 0;
		
		this._parent.scrollLeft(0);
	},
	
	getCurrentStage: function()
	{
		return this._stage;
	},
	
	getCurrentId: function()
	{
		return this._id;
	},
	
	_getCurrentIndex: function()
	{
		if ((this._cache.length == 0) || (this._stage > Number(this._totalItems).normalize(this._itemsPerPage)))
		{
			return -1;
		}
		else
		{
			if (this._cache.length == ((this._cacheLength * 2) + 1))
			{
				return this._cacheLength;
			}
			else
			{
				if (this._stage < this._cacheLength)
				{
					return this._stage;
				}
				else
				{
					var offset = Number(this._totalItems).normalize(this._itemsPerPage) - this._stage;
					return this._cache.length - 1 - offset;
				}
			}
		}
	},

	processKeyboardEvent: function(event)
	{
		if (!this._isActive)
		{
			return;
		}
		
		if (event.keyCode == 37)
		{
			this.scrollLeft();
		}
		else if (event.keyCode == 39)
		{
			this.scrollRight();
		}
	}
});


String.prototype.toInt = function()
{
	var text = this.toString();
	if ((text.substring(text.length - 2)) == "px")
	{
		text = text.substring(0, (text.length - 2));
	}

	return Number(text);
}

Number.prototype.normalize = function(pattern)
{
	return ((this % pattern) == 0) ? ((this / pattern) - 1) : Math.floor(this / pattern);
}

Array.prototype.subset = function(pattern, collection)
{
	var newData = [];
	var samplesCollection = ((collection != null) && (collection.length > 0)) ? collection.expand() : [];
	
	for(var i = 0; i < (Number(this.length).normalize(pattern) + 1); i++)
	{
		var temporaryData = this.slice((i * pattern), ((i + 1) * pattern));

		samplesCollection = samplesCollection.concat(temporaryData);		
		if ((collection != null) && (temporaryData.length < pattern))
		{
			var currentStageLength = temporaryData.length;
			for(var i = 0; (i < (pattern - currentStageLength)) && (currentStageLength > 0); i++)
			{
				var randomIndex = Math.floor(Math.random() * samplesCollection.length);
				
				temporaryData.push(samplesCollection[randomIndex]);
			}
		}
		
		newData.push(temporaryData);
	}
	
	return newData;
}

Array.prototype.expand = function()
{
	var newData = [];
	
	for(var i = 0; i < this.length; i++)
	{
		for(var j = 0; j < this[i].length; j++)
		{
			newData.push(this[i][j]);
		}
	}
	
	return newData;
}

$.fn.removeWithoutLeaking = function() {
    this.each(function(i,e){
        if( e.parentNode )
            e.parentNode.removeChild(e);
    });
};

