"use strict";

module.exports = {

	/**
  * Allows us to log information into the body of a response
  * Should be used only for internal, pre-release testing
  * @param  {string} any number of strings or objects
  */
	log() {
		var args = Array.prototype.slice.call(arguments);
		let item1, item2;
		let item1Set = false;
		for (let item of args) {
			if (!item1Set) {
				item1 = this.logVal(item);
				item1Set = true;
			} else {
				item2 = this.logVal(item);
				this.response.log(item1, item2);
				item1Set = false;
			}
		}
		if (item1Set) {
			this.response.log(this.randtoken.generate(5), item1);
		}
	},

	/**
  * Sub helper for log
  */
	logVal(val) {
		if (val === null) {
			return val;
		} else if (typeof val === "object") {
			return val;
		} else {
			return val;
		}
	},

	/**
  * Tests a string to see if it's json or not
  * @param  {string}  str The potentially JSON string
  */
	isJson(str) {

		try {
			JSON.parse(str);
		} catch (e) {
			return false;
		}
		return true;
	},

	/**
  * Tests if things are empty or not
  * @param  {any}  item Item to check if empty
  * @return {Boolean}
  */
	isEmpty(item) {

		// Null and Undefined...
		if (item == null) {
			return true;
		} else if ('boolean' === typeof item) {
			return !item;
		} else if ('number' === typeof item) {
			return item === 0;
		} else if ('string' === typeof item) {
			return item.length === 0;
		} else if ('function' === typeof item) {
			return item.length === 0;
		} else if (this.isArray(item)) {
			return item.length === 0;
		} else if (item instanceof Error) {
			return item.message === '';
		} else if (typeof item === 'object') {
			switch (item.toString()) {

				// Maps, Sets, Files and Errors...
				case '[object File]':
				case '[object Map]':
				case '[object Set]':
					{
						return item.size === 0;
					}

				// Plain objects...
				case '[object Object]':
					{
						return Object.keys(item).length === 0 && item.constructor === Object;
					}
			}
		}

		// Anything else...
		return false;
	},

	isArray(variable) {
		return variable && variable.constructor === Array;
	},

	isNumeric(n) {
		return !isNaN(parseFloat(n)) && isFinite(n) && (typeof n === 'number' || typeof n === 'string');
	},

	cloneObject(obj, err) {
		var copy;

		if (!err) {
			throw new Error('To use clone object, you must provide an error object so that if it ever breaks, we know where to look');
		}

		// Handle the 3 simple types, and null or undefined
		if (null == obj || "object" !== typeof obj) {
			return obj;
		}

		// Handle Date
		if (obj instanceof Date) {
			copy = new Date();
			copy.setTime(obj.getTime());
			return copy;
		}

		// Handle Array
		if (obj instanceof Array) {
			copy = [];
			for (var i = 0, len = obj.length; i < len; i++) {
				copy[i] = this.cloneObject(obj[i], err);
			}
			return copy;
		}

		// Handle Object
		if (obj instanceof Object) {
			copy = {};
			for (var attr in obj) {
				if (obj.hasOwnProperty(attr)) {
					try {
						copy[attr] = this.cloneObject(obj[attr], err);
					} catch (err2) {
						console.log('Clone', err.stack);
						throw new Error("Could not clone object, likely due to a reference to itself");
					}
				}
			}
			return copy;
		}

		throw new Error("Unable to copy obj! Its type isn't supported.");
	},

	patch(item, patch) {

		// Run actions
		for (let key in patch) {
			let val = patch[key];
			let actions = false;
			if (typeof val === 'object' && val.incr) {
				item[key] += val.incr;
				actions = true;
			}
			if (actions) {
				delete patch[key];
			}
		}

		// Patch the item
		return Object.assign(item, patch);
	},

	unique(arr) {
		return this.isArray(arr) ? arr.filter((v, i, a) => a.indexOf(v) === i) : arr;
	},

	/**
  * Encode an object as compressed base 64
  * Requires npm install snappy
  */
	zip(obj) {
		let compressedBuffer = this.snappy.compressSync(JSON.stringify(obj).base64Encode());
		return compressedBuffer.toString('base64');
	},

	/**
  * Encode an object as compressed base 64
  * Requires npm install snappy
  */
	unzip(str) {
		let compressedBuffer = new Buffer(str, 'base64');
		return JSON.parse(this.snappy.uncompressSync(compressedBuffer, { asBuffer: false }).base64Decode());
	},

	/**
  * Add a listener to be first called in a list where listeners may already exist
  * @param {object}   listenObject The object on which we are listening for events
  * @param {string}   eventName    The event name
  * @param {Function} callback     The new listener to be added
  * @param {bool}     replace      If true, will replace and remove any functions already listening and return them instead
  */
	addListenerFirst(listenObject, eventName, callback, replace) {

		let listeners = listenObject.listeners(eventName);

		listenObject.removeAllListeners(eventName);

		let myHandler = function () {
			// blackout-handler <- Don't change this comment string! (MUST MATCH BELOW)
			callback.apply(this, arguments);
		};

		listenObject.on(eventName, myHandler);

		if (!replace) {
			for (let l of listeners) {
				if (l.toString().indexOf('blackout-handler') < 0) {
					listenObject.on(eventName, l);
				}
			}
		} else {
			return listeners;
		}
	},

	/**
  * Sleep for a specified number of ms
  */
	sleep(ms) {
		return new Promise(resolve => setTimeout(resolve, ms));
	},

	/**
  * Generate a v4 UUID
  * @return {string} v4 UUID
  */
	uuid() {
		return this.uuidV4();
	},

	/**
  * Generate a random number between min and max (inclusive)
  * @param  {int} min The minimum number that can be returned
  * @param  {int} max The maximum number that can be returned
  * @return {int}     The random number
  */
	rand(min, max) {
		return Math.floor(Math.random() * (max - min + 1)) + min;
	},

	/**
  * Gets a random float between 0 and 1
  * @return {float} The random float between 0 and 1
  */
	rand0_1() {
		return this.rand(0, 1000) / 1000;
	},

	/**
  * Gets the keys from an object and sorts them based on the value they had in the original object
  * @param  {object} list The object whose sorted keys will be returned. The values should be sortable.
  * @param  {string} sort The sort direction. Default is ascending.
  * @return {array}       List of sorted keys
  */
	getKeysSortedByValue(list, sort = 'asc') {

		sort = sort.toLowerCase();
		if (sort === 'desc') {
			return Object.keys(list).sort(function (a, b) {
				return list[b] - list[a];
			});
		} else {
			return Object.keys(list).sort(function (a, b) {
				return list[a] - list[b];
			});
		}
	}

};
//# sourceMappingURL=helpers.js.map