/*
 * Author: Alex Gibson
 * https://github.com/alexgibson/shake.js
 * License: MIT license
 */

(function (global, factory) {
	if (typeof define === 'function' && define.amd) {
		define(function () {
			return factory(global, global.document);
		});
	} else if (typeof module !== 'undefined' && module.exports) 
		module.exports = factory(global, global.document);
	else 
		global.Shake = factory(global, global.document);
})(typeof window !== 'undefined' ? window : this, function (window, document) {
	function Shake (options) {
		//feature detect
		this.hasDeviceMotion = 'ondevicemotion' in window;

		this.options = {
			threshold: 15, //default velocity threshold for shake to register
			timeout: 1000 //default interval between events
		};

		if (typeof options === 'object') {
			for (let i in options) {
				if (options.hasOwnProperty(i)) 
					this.options[i] = options[i];
			}
		}

		//use date to prevent multiple shakes firing
		this.lastTime = new Date();

		//accelerometer values
		this.lastX = null;
		this.lastY = null;
		this.lastZ = null;

		//create custom event
		if (typeof document.CustomEvent === 'function') {
			this.event = new document.CustomEvent('shake', {
				bubbles: true,
				cancelable: true
			});
		} else if (typeof document.createEvent === 'function') {
			this.event = document.createEvent('Event');
			this.event.initEvent('shake', true, true);
		} else 
			return false;
	}

	//reset timer values
	Shake.prototype.reset = function () {
		this.lastTime = new Date();
		this.lastX = null;
		this.lastY = null;
		this.lastZ = null;
	};

	//start listening for devicemotion
	Shake.prototype.start = function () {
		this.reset();
		if (this.hasDeviceMotion) 
			window.addEventListener('devicemotion', this, false);
	};

	//stop listening for devicemotion
	Shake.prototype.stop = function () {
		if (this.hasDeviceMotion) 
			window.removeEventListener('devicemotion', this, false);
        
		this.reset();
	};

	//calculates if shake did occur
	Shake.prototype.devicemotion = function (e) {
		let current = e.accelerationIncludingGravity;
		let currentTime;
		let timeDifference;
		let deltaX = 0;
		let deltaY = 0;
		let deltaZ = 0;

		if ((this.lastX === null) && (this.lastY === null) && (this.lastZ === null)) {
			this.lastX = current.x;
			this.lastY = current.y;
			this.lastZ = current.z;
			return;
		}

		deltaX = Math.abs(this.lastX - current.x);
		deltaY = Math.abs(this.lastY - current.y);
		deltaZ = Math.abs(this.lastZ - current.z);

		if (((deltaX > this.options.threshold) && (deltaY > this.options.threshold)) || ((deltaX > this.options.threshold) && (deltaZ > this.options.threshold)) || ((deltaY > this.options.threshold) && (deltaZ > this.options.threshold))) {
			//calculate time in milliseconds since last shake registered
			currentTime = new Date();
			timeDifference = currentTime.getTime() - this.lastTime.getTime();

			if (timeDifference > this.options.timeout) {
				window.dispatchEvent(this.event);
				this.lastTime = new Date();
			}
		}

		this.lastX = current.x;
		this.lastY = current.y;
		this.lastZ = current.z;
	};

	//event handler
	Shake.prototype.handleEvent = function (e) {
		if (typeof (this[e.type]) === 'function') 
			return this[e.type](e);
	};

	return Shake;
});
