/**
 * Carbon Slider
 *
 * @version 1.0
 * @author Juri Saltbacka <juri@carbon.fi>
 */
window.Slider = function(element, options) {

	// If element doesn't exists, return
	if(!element) {
		throw "Slider: No element defined!";
	}

	if(typeof jQuery === 'undefined') {
		throw "Slider: No jQuery Loaded!";
	}

	var _this = this;

	// Default options
	this.options  = options || {};
	this.index    = this.options.startAt || 0;
	this.speed    = this.options.speed || 300;
	this.callback = this.options.callback || function() {};
	this.delay    = this.options.auto || 0;

	this.container = element;
	this.element   = this.container.children[0];

	this.container.style.overflow = 'hidden';
	this.element.style.listStyle  = 'none';

	this.init();

	// add event listeners
	if (this.element.addEventListener) {
		this.element.addEventListener('touchstart',          this, false);
		this.element.addEventListener('touchmove',           this, false);
		this.element.addEventListener('touchend',            this, false);
		this.element.addEventListener('webkitTransitionEnd', this, false);
		this.element.addEventListener('msTransitionEnd',     this, false);
		this.element.addEventListener('oTransitionEnd',      this, false);
		this.element.addEventListener('transitionend',       this, false);

		if(this.options.resize) window.addEventListener('resize', this, false);
	}
};

Slider.prototype = {

	cssSupport: function(property)
	{
		var prefixes = 'Webkit Moz O ms Khtml'.split(' '),
			div      = document.createElement('div'),
			support  = div.style[property] != undefined,
			i        = 0,
			prefix;

		property = property.charAt(0).toUpperCase() + property.slice(1);

		while(!support && ( prefix = prefixes[i++] )) {
			support = div.style[prefix + property] != undefined;
		}

		return support;
	},

	init: function()
	{
		this.slides = this.element.children;
		this.length = this.slides.length;

		this.supportTransform = this.cssSupport('transform');

		this.setup();
		this.begin();
	},

	setup: function()
	{
		if(this.length < 2) {
			return;
		}
		
		if(this.container.getBoundingClientRect().width)
			this.width = this.container.getBoundingClientRect().width || this.options.width;
		else if (this.container.getBoundingClientRect().left && this.container.getBoundingClientRect().right)
			this.width = this.container.getBoundingClientRect().right - this.container.getBoundingClientRect().left;

		if(!this.width) {
			return;
		}

		this.container.style.visibility = 'hidden';

		$(this.element).css('width', (this.slides.length * this.width) + 'px')

		var index = this.slides.length;
		while (index--) {
			var el = this.slides[index];
			$(el).css({
				'display': 'block',
				'width': this.width + 'px',
				'float': 'left'
			});
		}

		this.slide(this.index, 0);

		this.container.style.visibility = 'visible';
	},

	slide: function(index, duration)
	{
		duration = typeof duration !== 'undefined' ? duration : this.speed;
		var to = -(index * this.width) + 'px', _this = this;

		if(this.supportTransform) {
			var style = this.element.style;
			style.webkitTransitionDuration = style.MozTransitionDuration = style.msTransitionDuration = style.OTransitionDuration = style.transitionDuration = duration + 'ms';
			style.webkitTransform = 'translate3d(' + to + ', 0, 0)';
			style.msTransform = style.MozTransform = style.OTransform = 'translateX(' + to + ')';
			if(!duration) this.transitionEnd(null);
		}
		else {
			$(this.element).animate({
				'margin-left': to
			}, duration, function(){
				_this.transitionEnd(null);
			});
		}

		this.index = index;
	},

	begin: function()
	{
		var _this = this;
		if(this.delay) {
			clearTimeout(this.interval)
			this.interval = setTimeout(function() {
				_this.next(_this.delay);
			}, this.delay);
		}
	},

	next: function(delay)
	{
		this.delay = delay || this.delay || 0;
		clearTimeout(this.interval);

		if (this.index < this.length - 1) {
			this.slide(this.index + 1);
		}
		else {
			this.slide(0, this.speed);
		}
	},

	prev: function(delay)
	{
		this.delay = delay || 0;
		clearTimeout(this.interval);

		if(this.index) {
			this.slide(this.index - 1);
		}
		else {
			this.slide(this.length - 1);
		}
	},

	handleEvent: function(e)
	{
		switch (e.type) {
			case 'touchstart': this.onTouchStart(e); break;
			case 'touchmove': this.onTouchMove(e); break;
			case 'touchend': this.onTouchEnd(e); break;
			case 'webkitTransitionEnd':
			case 'msTransitionEnd':
			case 'oTransitionEnd':
			case 'transitionend': this.transitionEnd(e); break;
			case 'resize': this.setup(); break;
		}
	},

	// Events
	// ======

	onTouchStart: function(e)
	{
		this.start = {
			pageX: e.touches[0].pageX,
			pageY: e.touches[0].pageY,
			time:  Number( new Date() )
		};

		this.isScrolling = undefined;
		this.deltaX = 0;

		var style = this.element.style;
		style.webkitTransitionDuration = style.MozTransitionDuration = style.msTransitionDuration = style.OTransitionDuration = style.transitionDuration = 0;
	},

	onTouchMove: function(e)
	{

		// ensure swiping with one touch and not pinching
		if(e.touches.length > 1 || e.scale && e.scale !== 1) return;

		this.deltaX = e.touches[0].pageX - this.start.pageX;

		// determine if scrolling test has run - one time test
		if ( typeof this.isScrolling == 'undefined') {
			this.isScrolling = !!( this.isScrolling || Math.abs(this.deltaX) < Math.abs(e.touches[0].pageY - this.start.pageY) );
		}

		// if user is not trying to scroll vertically
		if (!this.isScrolling) {

			// prevent native scrolling
			e.preventDefault();

			// cancel slideshow
			clearTimeout(this.interval);

			// increase resistance if first or last slide
			if((!this.index && this.deltaX > 0) || (this.index == this.length - 1 && this.deltaX < 0)) {
				this.deltaX = this.deltaX / (Math.abs(this.deltaX) / this.width + 1);
			}

			// translate immediately 1-to-1
			this.element.style.webkitTransform = 'translate3d(' + (this.deltaX - this.index * this.width) + 'px,0,0)';
			this.element.style.msTransform = style.MozTransform = style.OTransform = 'translateX(' + (this.deltaX - this.index * this.width) + 'px)';

		}

	},

	onTouchEnd: function(e)
	{
		// determine if slide attempt triggers next/prev slide
		var isValidSlide =
		(Number(new Date()) - this.start.time < 400      // if slide duration is less than 250ms
		&& Math.abs(this.deltaX) > 20)                   // and if slide amt is greater than 20px
		|| Math.abs(this.deltaX) > this.width/2,        // or if slide amt is greater than half the width

		// determine if slide attempt is past start and end
		isPastBounds =
		(!this.index && this.deltaX > 0)                          // if first slide and slide amt is greater than 0
		|| (this.index === this.length - 1 && this.deltaX < 0);    // or if last slide and slide amt is less than 0

		// if not scrolling vertically
		if (!this.isScrolling) {
			// call slide function with slide end value based on isValidSlide and isPastBounds tests
			this.slide( this.index + ( isValidSlide && !isPastBounds ? (this.deltaX < 0 ? 1 : -1) : 0 ), this.speed );
		}

	},

	transitionEnd: function(e)
	{
		if(this.delay) {
			this.begin();
		}

		this.callback(e, this.index, this.slides[this.index]);
	}

};

