/**
 * @class
 * Utils
 **/

BLIP.Utils = {
	/**
	 * Page errors in an array so that there
	 * are no overlaps. Each array item is the
	 * name of a page element being targetted.
	 **/
	errorArray: [],
	myModal:null,

	onPlayerReady : function(playerObject, readyCallback) {
		var limit = 5000,
				i = 0,
				interval;

		interval = setInterval(function() {
			var el = document.getElementById(playerObject.id);

			if(el.addJScallback) {
				clearInterval(interval);
				readyCallback();
			}

			i++;
			if(i > limit) {
				clearInterval(interval);
			}
		}, 33);
	},

	getCookie: function(cookieName) {
		var c_name = cookieName,
				c_start = document.cookie.indexOf(c_name + "=");

		if (c_start !== -1) {
			c_start=c_start + c_name.length+1;
			c_end=document.cookie.indexOf(";",c_start);

			if (c_end === -1) {
				c_end=document.cookie.length;
			}
			return unescape(document.cookie.substring(c_start,c_end));
		}

		return null;
	},

	getCacheKiller: function() {
		var c_value = this.getCookie("cache_killer");

		if (c_value === null) {
			c_value = 0;
		}

		return c_value;
	},

	/**
	 * Removes all errors from the List.
	 **/
	clearAllErrors: function() {
		var item;

		for (item in this.errorArray) {
			if (!this.errorArray.hasOwnProperty(item)) {
				continue;
			}

			this.errorArray[item].destroy();
		}
	},

	/**
	 * Converts days to mysql dateformat.
	 **/
	dateToMySqlDays: function(date) {
		var year,
				month,
				day,
				utcDays;

		if (typeof(date) === "string") {
			date = Date.parse(date);
		}

		//bad date..
		if (!date) {
			return;
		}

		year = date.getFullYear();
		month = date.getMonth();
		day = date.getDate();

		utcDays = Date.UTC(year,month,day) / ( 86400 * 1000);
		//from AD 1 instead of 1970
		utcDays += 719528;

		return utcDays;
	},

	escapeHtml: function (html) {
		return html.replace(/&/gmi, '&amp;')
			.replace(/\"/gmi, '&quot;')
			.replace(/>/gmi, '&gt;')
			.replace(/</gmi, '&lt;');
	},

	escapeDoubleQuotes: function (str) {
		if (str && typeof(str) === "string") {
			return str.replace(/\"/g, "&#34;");
		}
	},

	LocalEvent : function(name, defaultContext) {
		var stopped = false;
		this.listeners = [];

		this.name = name;

		this.addListener = function(fn, scope) {
			this.listeners.push({fn : fn, scope : scope});
		};

		this.fire = function(data) {
			var i,
					stopped;

			for(i = 0, l = this.listeners.length; i < l; i++) {
				if(!stopped) {
					listener = this.listeners[i];   /* reference the listener obj literal to prevent multiple lookups
					* on the (potentially large) listeners array
                                                         */
					listener.fn.call(listener.scope || defaultContext || null, { name : name, event : this, data : data});
				}
			}

			stopped = false;
		};

		this.stop = function() {
			stopped = true;
		};

		return this;
	},

	formatNumber: function(nStr, prefix){
		var x,
				x1,
				x2,
				rgx;

		prefix = prefix || '';

		nStr += '';
		x = nStr.split('.');
		x1 = x[0];
		x2 = x.length > 1 ? '.' + x[1] : '';
		rgx = /(\d+)(\d{3})/;
		while (rgx.test(x1)) {
			x1 = x1.replace(rgx, '$1' + ',' + '$2');
		}

		return prefix + x1 + x2;
	},

	/**
	 *  retrieves error modals
	 * @tparam params
	 **/
	getError: function(div) {
		if (this.errorArray[div]) {
			return this.errorArray[div];
		}
	},

	/**
	 * Returns load failure HTML.
	 * @treturn String Load failure HTML.
	 **/
	getLoadFailureHTML: function() {
		var failControl = $('<div class="FailControl">'),
				failMessage = $('<div class="FailMessage">');

		failMessage
			.append($('<img src="/skin/mercury/dashboard/images/dashboard.error.gif">'))
			.append('There was a problem loading this dashboard component.');

		failControl.append(failMessage);

		return failControl;
	},

	/**
	 * Formats filenames correctly. Especially if they're too long.
	 **/
	getProperFilename: function(name, chars) {
		var size = chars ? chars : 31,
				lastSlash,
				lastDot,
				filename,
				format,
				shortFilename;

		if (!name) {
			return false;
		}

		lastSlash = name.lastIndexOf("/");
		lastDot = name.lastIndexOf(".");
		filename = name.substring(lastSlash+1, lastDot);
		format = name.substring(lastDot, name.length);
		shortFilename = BLIP.Utils.truncate(filename, size) + format.toUpperCase();
		return shortFilename;
	},


	/**
	 *  hides error modals
	 * @tparam params
	 **/
	hideError: function(div) {
		if (this.errorArray[div]) {
			this.errorArray[div].hide();
		}
	},

	/** A non-blocking iteration utility. Usage:

		var total = 0;
        var collection = [1,2,3,4,5];
        BLIP.Utils.iterate(collection,
            function(item, i, collect) {
                total+=item;
            },
            function() {
            }
        );

        TAKE CARE with this, as while it does not cause the browser to block,
        it adds an immense amount of overhead. Only use it for, say, an outer loop
        which has an expensive inner-loop.

    **/

	iterate: function(collection, func, callback, context) {
		var len,
				current,
				iter;

		if(!collection.length) {
			throw new TypeError();
		}

		len = collection.length;
		current = 0;

		iter = function() {
			if(current < len) {
				func.call(context, collection[current], current, collection);

				current++;

				setTimeout(iter, 0);
			}
			else {
				callback.call(context);
			}
		};

		setTimeout(iter,0); // start the iteratin'
	},

	/**
	 * Converts days to mysql dateformat.
	 **/
	mySqlDaysToDate: function(days) {
		var date,
				unixTime,
				year,
				month,
				day;

		if (days === "" || parseInt(days,10) < 0) {
			return;
		}

		//must be milliseconds -- mysql epoch is also from year 1, not 1970
		date = new Date();
		unixTime = (days - 719528) * 86400 * 1000;
		date.setTime(unixTime);

		//now we need to grab this in UTC time and reset it
		//this is to properly deal with DST without doing a lot
		//of crazy calcs

		year = date.getUTCFullYear();
		month = date.getUTCMonth();
		day = date.getUTCDate();

		return new Date(year,month,day);
	},

	/**
	 * Scrolls to error
	 **/
	scrollToError: function(params) {
		var div = params.div;
		this.errorArray[div].scrollToError();
	},

	/**
	 * Displays error modals
	 * @tparam params
	 **/
	showError: function(params) {
		var div = params.div,
				msg = params.msg,
				small = params.small || false;

		if (this.errorArray[div] === undefined) {
			this.errorArray[div] = new BLIP.Controls.Error({
				target: div,
				message: msg,
				small: small
			});
		}

		this.errorArray[div].show();
	},

	showHelpfulError: function(params) {
		var div = params.div,
				msg = params.msg,
				small = params.small || false;

		if (!this.errorArray[div]) {
			this.errorArray[div] = new BLIP.Controls.Help({
				target: div,
				message: msg,
				small: small
			});
		}

		$('.HelpfulErrorBox').hide();
		this.errorArray[div].show();
	},

	showModalHelpText: function(params) {
		var name = params.name,
				src = params.src,
				modal = new BLIP.Controls.ModalWindow({
			target: "",
			contentsUrl: src,
			purpose: "Help",
			name: name
		});

		modal.show();
	},

	standardDateFormat : function(d) {
		var months;

		if(d.constructor !== Date) {
			throw new TypeError("Date expected, " + typeof d + " encountered with constructor " + d.constructor);
		}
		months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
		return [
			months[d.getMonth()],
			" ",
			d.getDate(),
			", ",
			d.getFullYear()
		].join("");
	},

	graphDateFormat : function(d) {
		var months;

		if(d.constructor !== Date) {
			throw new TypeError("Date expected, " + typeof d + " encountered with constructor " + d.constructor);
		}
		months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
		return [
			d.getDate(),
			months[d.getMonth()],
			d.getFullYear()
		].join("-");
	},

	monthDateFormat : function(d) {
		var months;

		if(d.constructor !== Date) {
			throw new TypeError("Date expected, " + typeof d + " encountered with constructor " + d.constructor);
		}

		months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
		return [
			months[d.getMonth()],
			d.getFullYear()
		].join(" ");
	},

	trimDelegate: function(str) {
		return str.substring(0, str.length - 3);
	},

	/**
	 * Although trim() is supported in ECMAscript 5, there are some browsers that
	 * don't support it.
	 */
	trim: function (str) {
		if (str === null || str === undefined) {
			return "";
		}

		return str.replace(/^\s+|\s+$/g, '');
	},

	/**
	 * truncate() cuts a string down to a limited set of charaters.
	 *
	 * BEFORE IT DOES THAT, it throws away all leading and trailing whitespace.
	 */
	truncate: function(str, max) {
		str = this.trim(str);

		if (str && str.length > max) {
			return str.substring(0,(max-2)) + "&hellip;";
		}

		return str;
	},

	updateCacheKiller: function(cookie_domain) {
		//debugger;
		var domain_str = '',
				exdate,
				expiredays,
				cache_killer;

		if (cookie_domain && cookie_domain !== '') {
			domain_str = ';domain=' + cookie_domain;
		}

		exdate = new Date();
		expiredays = 30;
		exdate.setDate(exdate.getDate()+expiredays);
		cache_killer ="cache_killer=" + escape(Math.floor(Math.random() * 1000000000))+ ";expires="+exdate.toGMTString() + ";path=/" + domain_str;
		document.cookie = cache_killer;
	},

	/**
	 * Unchecks all inputs in a DIV.
	 * @tparam STRING target DIV target with inputs ( like checkboxes or radio buttons) inside of it.
	**/
	uncheckAll: function(target) {
		$("#" + target + " input:checked").attr("checked", false);
		$("#" + target + " input:selected").attr("selected", false);
	},

	/**
	 * Wraps some contents in HTML code.
	 * @tparams Object params an object: {tag, contents, elementId, elementClass, elementName, elementValue}.
	 * @treturn String HTML.
	 **/
	wrapHtmlTag: function(params) {
		var idHtml = params.elementId ? " id='"+ params.elementId +"'" : "",
				classHtml = params.elementClass ? " class='"+ params.elementClass +"'" : "",
				inputNameHtml = params.elementName ? " name='"+ params.elementName +"'" : "",
				inputValueHtml = params.elementValue ? " value='"+ params.elementValue +"'" : "",
				onClickHtml = params.onClick ? " onClick='" + params.onClick + "'" : "",
				onChangeHtml = params.onChange ? ' onChange="' + params.onChange + '"' : '',
				selectedHtml = params.elementSelected ? " selected" : "",
				forHtml = params.forHtml ? " for='" + params.forHtml + "'" : "",
				maxlengthHtml = params.maxlength ? " maxlength='" + params.maxlength + "'" : "",
				disabled = params.disabled ? " disabled='" + params.disabled + "'" : "",
				attributesHtml = idHtml + classHtml + inputNameHtml + inputValueHtml + onClickHtml + onChangeHtml + disabled + maxlengthHtml + selectedHtml + forHtml,
				html =      "<" + params.tag + attributesHtml + ">";

		html +=                 params.contents;
		html +=         "</" + params.tag + ">";
		return html;
	},

	/**
	 * Wraps target in a form tag.
	 * @tparam String divTarget The id of the page element to be wrapped in a form tag.
	 * @tparam String htmlClass The css class to assign to the form.
	 * @tparam Integer i The index of the List item.
	 **/
	wrapInFormTag : function(divTarget, htmlClass, i) {
		var formElement = document.createElement("form");

		formElement.setAttribute("enctype", "multipart/form-data");
		formElement.setAttribute("encoding", "multipart/form-data");
		formElement.setAttribute("id", "form" + i);
		if (htmlClass !== undefined) {
			formElement.setAttribute("class", htmlClass);
		}

		$(divTarget).wrap(formElement);
	},

	/**
	 * createUrlParams() expects an array of arrays to make into
	 * properly formed url params.
	 *
	 * For example:
	 *
	 *  [["no-cache", 1], ["user", 123]]
	 *
	 * Returns the string:
	 *
	 *  "no-cache=1&user=123"
	 **/
	createUrlParams: function(params) {
		var paramPairs = [],
				returnString = "",
				i;

		for (i in params) {
			if (!params.hasOwnProperty(i)) {
				continue;
			}

			paramPairs.push(params[i].join("="));
		}

		returnString += paramPairs.join("&");

		return returnString;
	},

	UrlBuilder : function(url) {
		var paramObjs = {},
				base = "",
				queryString = "",
				splitUrl,
				strParams,
				i,
				paramSplit;

		this.addParam = function(param, value) {
			paramObjs[param] = value;
			return this;
		};

		this.getValue = function(param) {
			return paramObjs[param];
		};

		this.getFullUrl = function() {
			return [base,buildParamString()].join('?');
		};

		function buildParamString () {
			var paramStrings = [],
					param;

			for(param in paramObjs) {
				paramStrings.push([param, paramObjs[param]].join('='));
			}

			return paramStrings.join('&');
		}

		// prepare the url
		splitUrl = url.split("?");
		base = splitUrl[0];
		queryString = splitUrl[1] ? splitUrl[1] : "";
		if(queryString !== "") {
			strParams = queryString.split("&");

			for(i = 0; i < strParams.length; i++) {
				paramSplit = strParams[i].split("=");
				this.addParam(paramSplit[0], paramSplit[1]);
			}
		}

		return this;
	},

	secondsToTimeCode : function(seconds) {
		var hours = Math.floor(seconds / (60*60)),
			minutes,
			format = function(num) {
				return num < 10 ? '0' + num.toString(10) : num.toString(10);
			};

		seconds = seconds - (hours * 60 * 60);
		minutes = Math.floor(seconds / 60);
		seconds = seconds - (minutes * 60);

		return [format(hours), format(minutes), format(seconds)].join(':');
	},

	timeCodeToSeconds : function(timecode) { // expected a string '00:00:00', '00:00' or ':00'
		var split = timecode.split(':'),
			massaged = "", i = 0, l = split.length,
			date;
		for(; i < l; i++) {
			split[i] = split[i].length < 2 ? '0'+split[i] : split[i];
		}
		while(split.length < 3) {
			split.unshift('00');
		}

		massaged = split.join(':');

		date = Date.parse(massaged);
		return date.getHours() * 60 * 60 + date.getMinutes() * 60 + date.getSeconds();
	},

	buildTrackedUrl : function(anchor, source, medium, content, campaign) {
		var url = new BLIP.Utils.UrlBuilder(anchor.attr('href'));

		url.addParam('utm_source', source);
		url.addParam('utm_medium', medium);
		url.addParam('utm_content', content);
		url.addParam('utm_campaign', campaign);

		anchor.attr(url.getFullUrl());
	},

	areCoordsWithinBox : function (coords, boxCoords, dimensions) {
		return (
				coords[0] >= boxCoords[0] &&
				coords[1] >= boxCoords[1] &&
				coords[0] <= dimensions[0]+boxCoords[0] &&
				coords[1] <= dimensions[1]+boxCoords[1]
		);
	},

	animateElementTo : function(elements, target, options) {
		// Currently works best with images, I plan on expanding this in the future

		var easeInOutQuad = function (x, t, b, c, d) {
			if ((t/=d/2) < 1) { return c/2*t*t + b; }
			return -c/2 * ((--t)*(t-2) - 1) + b;
		},
		targetOffset = {
			left: target.offset().left + target.width() / 2,
			top: target.offset().top + target.height() / 2
		},
		config = {
			duration: 600,
			durationJitter: 0,
			scale: 1,
			ghost: false
		};

		$.extend(config, options);

		elements.each(function(i,elm) {
			var duration;
			elm = $(elm);

			if (config.durationJitter > 0) {
				duration = Math.random() * config.durationJitter + config.duration;
			}

			elm.clone()
				.css({
					"position": "absolute",
					"left": elm.offset().left,
					"top": elm.offset().top, "z-index":999
				})
				.appendTo("body")
				.animate({
					left : targetOffset.left - ( elm.outerWidth() * config.scale ) / 2,
					top : targetOffset.top - ( elm.outerHeight() * config.scale ) / 2,
					width: elm.outerWidth() * config.scale,
					height: elm.outerHeight() * config.scale
				}, duration, easeInOutQuad(), function() {
					$(this).fadeOut(function() {
						$(this).remove();
					});
				});
		});
	}
};

