if(VVF === undefined) {
	var VVF = {};
}

(function(namespace) {
	namespace = namespace || window;
	var $ = jQuery;
	
	Function.prototype.bind = function(scope) {
		var fn = this, args = Array.prototype.slice.call(arguments, 1);
		return function() {
			return fn.apply(scope, args);
		}
	}
	
	// SlideCycle Constructor
	var SlideCycle = function(datas) {
		if(!(this instanceof SlideCycle)) {
			return new SlideCycle(datas);
		}
		
		this.initialize(datas);
	};
	namespace.extend(SlideCycle, {
		initialize: function(datas) {
			var that = this;
			
			this.startAt =        datas.start - 1 || 0;
			this.itemsDisplayed = datas.itemsDisplayed;
			this.move =           datas.move;
			this.offScreen =      datas.offScreen;
			this.animDiv =        datas.animDiv || 2;
			this.animDelay =      datas.animDelay || 40;
			this.context =        null;
			this.items =          null;
			this.prev =           null;
			this.next =           null;
			this.css =            datas.direction || 'left';
			this.itemsNb =        0;
			this.timer =          null;
			this.inAnim =         false;
			this.iRef =           false;
			this.where =          null;
			this.lastMove =       null;
			this.tabs =           [];
			this.tabsPos =        [];
			this.tabsPosTo =      [];
			this.tabsPosSteps =   [];
			
			this.context = $(datas.context);
			if(!this.context.length) {throw new Error('SlideCycle: no context found');}
			this.items = $(datas.items, datas.context);
			this.itemsNb = this.items.length;
			this.next = $(datas.next, this.context).click(function(e) {that.slideStart(e, this);});
			this.prev = $(datas.prev, this.context).click(function(e) {that.slideStart(e, this);});
			if(this.itemsNb > this.itemsDisplayed) {
				this.context.addClass('slide');
				this.getElements(this.items);
			} else {
				this.next.addClass('hide');
				this.prev.addClass('hide');
			}
		},
		
		reassignEvents: function() {
			var that = this;
			this.next.click(function(e) {that.slideStart(e, this);});
			this.prev.click(function(e) {that.slideStart(e, this);});
		},
		
		getElements: function() {
			if(this.iRef === false) {
				for(var i = this.startAt; i < (this.itemsDisplayed + this.startAt); i++) {this.tabs.push(this.items[this.verifIndex(i)]);}
			} else if(!this.lastMove || this.where == this.lastMove) {
				if(this.where == 'next') {
					if(this.lastMove) {this.tabs.shift();}
					this.tabs.push(this.items[this.iRef]);
				} else {
					if(this.lastMove) {this.tabs.pop();}
					this.tabs.unshift(this.items[this.iRef]);
				}
			}
			if(this.lastMove) {this.startAt = 0;}
			this.reposition();
		},
		
		computeTabs: function() {
			var i = -1, l = this.tabs.length, move = this.where == 'next' ? - this.move : this.move;
			while(++i < l) {
				this.tabsPos[i] = parseInt(this.tabs[i].style[this.css]);
				this.tabsPosTo[i] = this.tabsPos[i] + move;
			}
		},
		
		reposition: function() {
			if(this.where) {
				if(this.where == 'next') {var i = this.tabs.length - 1, move = this.move * this.itemsDisplayed;}
				else {var i = 0, move = - this.move;}
				this.tabs[i].style[this.css] = move +'px';
			} else {
				for(var i = 0; i < this.itemsDisplayed; i++) {this.tabs[i].style[this.css] = (i * this.move) +'px';}
			}
		},
		
		verifIndex: function(i) {
			if(i >= this.itemsNb) {i = i - this.itemsNb;}
			else if(i < 0) {i = i + this.itemsNb;}
			return i;
		},
		
		slideEnd: function() {
			this.tabs[this.where == 'next' ? 0 : this.itemsDisplayed].style[this.css] = this.offScreen +'px';
			clearInterval(this.timer);
			this.inAnim = false;
		},
		
		slideCheck: function(cur, pos) {
			return this.where == 'next' ? cur <= pos : cur >= pos;
		},
		
		slideComputeStep: function(cur, pos) {
			return cur + Math[this.where == 'next' ? 'floor' : 'ceil']((pos - cur) / this.animDiv);
		},
		
		slideCompute: function() {
			for(var i = 0, tab; tab = this.tabs[i]; i++) {
				var cur = parseInt(tab.style[this.css]),
					to = this.tabsPosTo[i],
					c = this.slideComputeStep(cur, to),
					tps = [c];
				while(!this.slideCheck(c, to)) {
					c = this.slideComputeStep(c, to);
					tps.push(c);
				}
				this.tabsPosSteps[i] = tps;
			}
			this.step = 0;
			this.steps = this.tabsPosSteps[0].length;
		},
		
		slide: function() {
			if(this.step < this.steps) {
				for(var i = 0, tab; tab = this.tabs[i]; i++) {
					tab.style[this.css] = this.tabsPosSteps[i][this.step] +'px';
				}
				this.step++;
			} else {
				this.slideEnd();
			}
		},
		
		slideStart: function(e, obj) {
			e.preventDefault();
			if(!this.inAnim) {
				this.where = obj == this.next.get(0) ? 'next' : 'prev';
				if(this.where == 'next') {
					this.iRef = this.iRef === false ? this.verifIndex(this.itemsDisplayed + this.startAt) : this.verifIndex(this.iRef + (this.lastMove == 'prev' ? this.itemsDisplayed : 1));
				} else {
					this.iRef = this.iRef === false ? this.verifIndex(this.itemsNb - 1 + this.startAt) : this.verifIndex(this.iRef - (this.lastMove == 'next' ? this.itemsDisplayed : 1));
				}
				this.getElements();
				this.computeTabs();
				this.lastMove = this.where;
				this.inAnim = true;
				this.slideCompute();
				this.timer = setInterval(this.slide.bind(this), this.animDelay);
			}
		}
	});
	
	if(!namespace.SlideCycle) {
		namespace.SlideCycle = SlideCycle;
	}
})(VVF);
