(function( $ ) {
	'use strict';

	var doc = document;
	var win = window;

	function Perllax(el, config) {
		this.el = el;
		this.config = config;
	};

	Perllax.prototype = {
		defaults: function() {
			return {
				speed: 0.2, // number
				direction: 'vertical' // string: 'vertical' || 'horizontal'
			};
		},

		init: function(config) {
			this.state = [];
			this.config = extend({}, this.defaults(), (config || this.config));
			this.bindEvents();
			this.updateAll();
		},
	  
		reinit: function(config) {
			this.kill();
			this.init(config);
		},

		kill: function() {
			this.resetStyles();
			this.unbindEvents();
		},

		bindEvents: function() {
			listen(win, 'scroll', this.onScroll.bind(this));
			listen(win, 'resize', this.onResize.bind(this));
			listen(win, 'load', this.onLoad.bind(this));
			$(window).on('page-sextion-expanded', this.onResize.bind(this));
		},

		unbindEvents: function() {
			unlisten(win, 'scroll', this.onScroll.bind(this));
			unlisten(win, 'resize', this.onResize.bind(this));
			unlisten(win, 'load', this.onLoad.bind(this));
		},

		updateAll: function() {
			this.winH = win.innerHeight;
			this.winW = win.innerWidth;
			this.resetStyles();
			this.setElState();
			if (this.config.direction === 'vertical') this.setHeight();
			else if (this.config.direction === 'horizontal') {
				this.setWidth();
			}
			this.setPosition();
		},

		resetStyles: function() {
			this.el.style.transform = null;
			this.el.style.height = null;
			this.el.style.width = null;
			this.el.style.top = null;
		},

		setElState: function() {
			var clientRect = this.el.getBoundingClientRect();
			var s = {};
			s.offset = clientRect.top + scrollY();
			s.height = clientRect.height;
			s.width = clientRect.width;
			this.state = s;
		},

		setHeight: function() {
			var adjust = this.getAdjustements();
			this.setStyles({ height: adjust.size + 'px' });
		},

		setWidth: function() {
			var adjust = this.getAdjustements();
			this.setStyles({ width: adjust.size + 'px' });
		},

		setPosition: function() {
			var s = this.state;
			var startPoint = s.offset - this.winH;
			var endPoint = s.offset + s.size + this.winH;

			// if( scrollY() < startPoint || scrollY() > endPoint ) return;

			var adjust = this.getAdjustements();
			var currentPoint = (( -s.offset + scrollY() ) * this.config.speed) + adjust.offset;
	    	if (this.config.direction === 'vertical') this.setStyles({ 'transform': 'translateY(' + currentPoint + 'px) translateZ(0)'});
	    	if (this.config.direction === 'horizontal') this.setStyles({ 'transform': 'translateX(' + -currentPoint + 'px) translateZ(0)'});
		},

		setStyles: function(prop) {
			for(var name in prop) {
				this.el.style[name] = prop[name];
			}
		},

		getAdjustements: function() {
	    	var speed = this.config.speed;
			var s = this.state;
			var winSize = (this.config.direction === 'vertical') ? this.winH : this.winW;
			var elSize = (this.config.direction === 'vertical') ? s.height : s.width;
			var o = s.offset;


			if (this.config.direction === 'horizontal') {
				return { 
					size: elSize + (winSize * speed) * 2,
					offset: winSize * speed
				};
			}

			if (0 < speed <= 1 && o !== 0) {
				return { 
					size: elSize + ((winSize - elSize) * speed),
					offset: 0
				};
			} else if (1 < speed && elSize <= winSize) {
				var size = (winSize * speed) - winSize;
		  		return {
					size: winSize + size * 2,  
					offset: -size
		  		};
		  	} else if (1 < speed && winSize < elSize) {
		  		var size = winSize + ((winSize * speed) - winSize) * (1 + (elSize / winSize));
		  		return {
		  			size: size,
		  			offset: -(size - (winSize * speed))
		  		};
		  	} else if (speed < 0 && winSize <= elSize) {
		  		var size = elSize * (1 - speed);
		  		return {
					size: size + (size - elSize),
		  			offset: elSize - size
		  		};
		  	// Possibly invalid, check it when needed
		  	// Ken Supports fixed widths this are higher than 0 so it's irrelevant now
		  	} else if (speed < 0 && elSize < winSize) {
		  		var display = (winSize + elSize) / winSize;
		  		var size = elSize * -speed * display;
		  		return {
				  	size: elSize + (size * 2),
		  			offset: -size
		  		};         		
		  	} else {
		  		return {
		  			size: null,
		  			offset: null
		  		};
	  		}
		},

		drawAll: function() {
			var that = this;
			win.requestAnimationFrame( function() {
				that.setPosition(that.el);
			});
		},

		onScroll: function() { this.drawAll(); },
		onLoad: function() { this.updateAll(); },
		onResize: function() { this.updateAll(); }
	};

	/**
	 * Helper functions
	 */

	var forEach = function(arr, cb) {
		for(var i = 0, l = arr.length; i < l; i++) {
			cb(arr[i], i);
		}
	};

	var isNode = function(o){
	  return (
	    typeof Node === "object" ? o instanceof Node : 
	    o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName==="string"
	  );
	};

	var extend = function() {
		for(var i=1; i<arguments.length; i++) {
			for(var key in arguments[i]) {
				if(arguments[i].hasOwnProperty(key)) {
					arguments[0][key] = arguments[i][key];
				}
			}
		}
		return arguments[0];
	};

	var listen = function (obj, evt, cb) {
		obj.addEventListener(evt, cb);
	};

	var unlisten = function (obj, evt, cb) {
		obj.removeEventListener(evt, cb);
	};

	var prefix = (function () {
		var styles = window.getComputedStyle(document.documentElement, ''),
			pre = (Array.prototype.slice
				.call(styles)
				.join('') 
				.match(/-(moz|webkit|ms)-/) || (styles.OLink === '' && ['', 'o'])
			)[1],
			dom = ('WebKit|Moz|MS|O').match(new RegExp('(' + pre + ')', 'i'))[1];

		return {
			dom: dom,
			lowercase: pre,
			css: '-' + pre + '-',
			js: pre[0].toUpperCase() + pre.substr(1)
		};
	})();

	var scrollY = (function() {
		var state = 0;
		var hasPageYOffset = ( win.pageYOffset !== undefined );
		var body = ( doc.documentElement || doc.body.parentNode || doc.body );

		var setState = function() {
			state = hasPageYOffset ? win.pageYOffset 
								   : body.scrollTop;
		};

		var rAF = function() {
			win.requestAnimationFrame( setState );
		}; 

		setState();
		listen(win, 'load',   setState);
		listen(win, 'resize', setState);
		listen(win, 'scroll', rAF);

		return function() {
			return state; 
		};
	})();

	$.fn.parallaxScroll = function(config) {
		var pllx = new Perllax($(this)[0], config); 
		pllx.init();
	}

}( jQuery ));