/**
 * All intellectual property rights in this Software throughout the world belong to UK Radioplayer, 
 * rights in the Software are licensed (not sold) to subscriber stations, and subscriber stations 
 * have no rights in, or to, the Software other than the right to use it in accordance with the 
 * Terms and Conditions at www.radioplayer.co.uk/terms. You shall not produce any derivate works 
 * based on whole or part of the Software, including the source code, for any other purpose other 
 * than for usage associated with connecting to the UK Radioplayer Service in accordance with these 
 * Terms and Conditions, and you shall not convey nor sublicense the Software, including the source 
 * code, for any other purpose or to any third party, without the prior written consent of UK Radioplayer.
 *
 * object that contains utility functions for the radioplayer website
 */
glow.lang.apply(radioplayer.utils, {

	/**
	 * checks whether the passed element is a child of the selector element
	 * @param {element} childElement the dom element you want to check
	 * @param {string} cssSelector selector that specs the parent
	 * @param {string | element | glow.dom.NodeList} [context] context within which to check.
	 *			if not specced, it will be checked for the whole document
	 * @returns {boolean}
	 */
	isChild: function (childElement, cssSelector, context) {
	  return !!radioplayer.utils.rebase(childElement, cssSelector);	  
	},
	
	/**
	 * regex that matches a simple class selector (.cta) or an element selector (li)
	 */
	isSimpleSelector: /^[\.]?[^\s:#\[\.,]*$/,

	/**
	 * takes and array and a function that will check whether the items in the array fullfill
	 * a certain condition. Returns an array of items that fullfill the function's test
	 * @param {array | glow.dom.NodeList} array array or glow.dom.NodeList of items that should be checked
	 * @param {function} fn function that has to return a boolean to assert whether the passed item
	 * 	fullfills the condition or not
	 * @returns {array} array of the found items
	 */
	grep: function (array, fn) {
		var len = array.length, 
			i = 0, 
			results = [];

		for (; i < len; i++) {

			if (fn.call(array[i], array[i], i, array) === true) {
				results.push(array[i]);
			}
		}

		return results;
	},

	/**
	 * returns the element or an ancestor that fits the selector
	 * @params {Element | glow.dom.NodeList} element the element to be checked
	 * @params {String} selector the nodespec to be checked against
	 * @returns {Element | Boolean} the element that fits the selector or false if not found
	 */
	rebase: function (element, selector) {
		var el = glow.dom.get(element),
		    match = null;


		// this code may look more complicated than a "simple" .is() test, but the .is test performs
		// really badly in IE, so the following code is designed to shortcircuit the .is test, so
		// if a selector is a simple classname or a simple element, this code will take 250ms down 
		// to <20ms execution time on the triggered event.
		if (radioplayer.utils.isSimpleSelector.test(selector)) {

			// get the raw DOM element from the NodeList
			el = el[0];

			/* 
			 fast loop up the tree
			 dynamically create a match function that either checks for hasClass
			 nodename depending on the selector passed
			*/

			if (selector.substr(0, 1) === '.') {
				// convert selector to simple class name
				selector = selector.substr(1);

				// get nodeList object to grab the hasClass method
				match = (new glow.dom.NodeList()).hasClass;
			}
			else {
				selector = selector.toUpperCase();

				// match custom function allows us to use the same mehtod
				match = function (nodeName) {
					return this[0].nodeName === nodeName;
				};
			}

	    
			// while this element doesn't have the class, and the node isn't the top level doc, the set el to it's parent, and loop

			while (
				!match.call([el], selector) && // we need to pass el as an array as match function _can_ be the hasClass method of Nodelist, which expects an array
				el !== document && // make sure the element is not the document
				el.parentNode ) { // make sure the current element has a parentNode
					el = el.parentNode; //assign the parentNode to the current element & continue looping
			}

			// if we got the document node, then we didn't find what we were looking for
			el = (el !== document) ? [el] : [];
		}
		else {
			var $selector = glow.dom.get(selector);

			// Note: old version of code would call ancestors and loop with .is() - if there the
			// element was 10 nodes deep, .is() is called 10 time == 10ms x 10 == 200ms exec time

			// the .is() selector is *very* bad in IE when nested in a loop so this is an optimised
			// version of the same functionality specific to radioplayer, we're still using the initial
			// .is and using ancestors and filter, but using a bespoke grep to assert if it's in
			// the collection.
			if (!el.is(selector)) { // 10ms
				el = el.ancestors().filter(function () { // ancestors == 10ms
					var el = this;

					return !!radioplayer.utils.grep($selector, function (selEl) {
						return el === selEl;
					}).length;
				});
			}
		}

		return el.length ? el[0] : false;
	},


	/**
	 * get the ancestor element as specified by name & class
	 *
	 * we are keeping this function as it is by an order of magnitude faster than using
	 * something like glow.dom.get(el).ancestors().filter(ancestorname.class);
	 *
	 * @param {Element} el DOM element - child of the ancestor we are looking for
	 * @param {String} ancestorName name of ancestor element tag
	 * @param {String} className class of the ancestor element
	 * @returns {Element}
	 */
	getAncestor: function(el, ancestorName, className) {
		ancestorName = ancestorName.toLowerCase();

		var ancestor = el.parentNode;

		while (ancestor &&
			(ancestor.nodeName.toLowerCase() !==  ancestorName ||
			 ancestor.className.indexOf(className) === -1))
		{
			ancestor = ancestor.parentNode;
		}

		return ancestor ? ancestor : false;
	},


	/**
	 * get the absolute position of element in the page
	 *
	 * returns an object with x & y coordinates, i.e. {x: 123, y:456}
	 *
	 * @param {Element | glow.dom.NodeList | NodeSpec} nodeSpec element you want to get the position of.
	 * 	if you pass a NodeList, only the 1st element is used
	 *			If a NodeList or NodeSpec is passed, we only use the 1st element
	 * @returns {Object}
	 */
	getPosition: function(nodeSpec) {
		var x, y;

		// normalise the element passed
		nodeSpec = glow.dom.get(nodeSpec)[0];

		x = y = 0;

		if (nodeSpec.offsetParent) {

			do {

				x += nodeSpec.offsetLeft;
				y += nodeSpec.offsetTop;

			} while ((nodeSpec = nodeSpec.offsetParent));

			return {'x': x, 'y': y};
		}

		return false; // return false if offsetParent is not supported
	},

	object: {

		/**
		 * checks whether the passed object is empty
		 * @param {Object} obj object to be checked
		 * @returns {boolean}
		 */
		isEmpty: function(obj) {
			for (var i in obj) {
				return false;
			}

			return true;
		},

		/**
		 * checks the length of the passed object
		 * @param {Object} obj object to be checked
		 * @returns {Number}
		 */
		length: function(obj) {
			var prop, size = 0;
			for (prop in obj) {
				if (obj.hasOwnProperty(prop)) {
					size++;
				}
			}
			return size;
		}

	},

	date: {
		/**
		 * converts DATE_ISO8601 (2010-05-05T22:06:00+0000) to UTC
		 * ignores Timezone!!!
		 *
		 * @param {string} dateString ISO8601 representation of a date
		 * @returns {string} utc representation of the date
		 */
		iso8601ToUtc: function(dateString) {
			var date, dateYMD, dateHMS;

			date = dateString.split('T');

			dateYMD = date[0].split('-');
			dateHMS = date[1].slice(0,8).split(':');
			date = dateYMD.concat(dateHMS);

			date = new Date(
				date[0], 
				date[1], 
				date[2],
				date[3],
				date[4],
				date[5] ).getTime();

			return date;
		}
	},
	/**
	 * Adds new query string like variable to the hash string
	 * @param (object) Format like {variable_name: value}
	 */
	addUrlHash : function(newHash){
		if (typeof(newHash) != 'object') {
			throw new Error("radioplayer.utils.addUrlHash: parameter passed is not an object");
		} else {
			//Check the hash is formatted similar to a query string
			if (window.location.hash.indexOf('#') === 0 && window.location.hash.indexOf('=') !== -1) {
				try {
					//Add new variable to existing hash
					var hash = glow.data.decodeUrl(window.location.hash);
					window.location.hash = glow.data.encodeUrl(glow.lang.apply(hash, newHash));
				} catch(e) {
					log('Failed to decode / encode hash');
					//Replace hash with new
					window.location.hash = glow.data.encodeUrl(newHash);
				}
			} else {
				//Replace hash with new
				window.location.hash = glow.data.encodeUrl(newHash);
			}
		}
	},

	removeUrlHash : function(removeHash){

		//Check the hash is formatted similar to a query string
		if (window.location.hash.indexOf('#') === 0 && window.location.hash.indexOf('=') !== -1) {
	
			try {
				//Remove hash
				var hash = glow.data.decodeUrl(window.location.hash.slice(1));
				delete hash[removeHash];
				window.location.hash = glow.data.encodeUrl(hash);
				
			} catch(e) {
				log('Failed to decode / encode hash');

			}
		}


	},

	getUrlHashValue : function(key){

		//Check the hash is formatted similar to a query string
		if (window.location.hash.indexOf('#') === 0 && window.location.hash.indexOf('=') !== -1) {

			try {
				//Remove hash
				var hash = glow.data.decodeUrl(window.location.hash.slice(1));

				if (typeof(hash[key]) !== 'undefined') return hash[key];
				else return false;

			} catch(e) {
				log('Failed to decode / encode hash');

			}
		}
	},
	
	/**
	 * parses data from the radioplayer cookie with the data in the datastore
	 * @param 
	 */
	parseData: function(cookiedata) {
		//transpose data with datastore, ie stationID => playerUrl etc
		var parsedObj = {};
		var item, index, stationID , tempStationObject; 
		var position = 0;
		
		for (var i in cookiedata) {
			item = cookiedata[i].split(':');
			index = item[0];
			stationID = item[1];
			
			//check for empty strings
			if (index !== '') {
			// fixed bug: if station was added to presets then it was deleted from station list manager -> console doesn't work 
				tempStationObject = radioplayer.services.store.get(stationID);
				
				if(tempStationObject){
					parsedObj[position] = tempStationObject;
					parsedObj[position].stationID = item[1]; 
					position = position + 1;
				}
			}
		}		
		return parsedObj;		
	}

});

