/**********************************************************************
 *   @preserve
 *   Copyright 2010 PredictiveIntent Ltd - All Rights Reserved.
 *
 **********************************************************************/

// -----------------------------------------------------------------------------
// GLOBAL VARS
// -----------------------------------------------------------------------------
var
// URL regular epression, used to determine validity of all URL args
URL_REGEX = /^(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,

// reference the toString function of Object
TOS = Object.prototype.toString,

// default user tracking cookie
TRACK_CNAME = "personalmerchant";

/**
 * Creates a new instance of IPS with options to determine the base
 * communication url. It also allows use of a specific the JQuery object
 *
 * @param jquery
 *            The jQuery Object to use
 * @param options
 *            Config settings which must contain:
 *                url: IPS endpoint URL
 *                id: Account ID
 *                code: Account Code
 *                cred: Account Cred
 *                permid: Cookie Name [OPTIONAL]
 *
 */

function IPS(jquery, options) {

	// validate that we actually got JQuery instance
	if (!(jquery.getJSON && jquery.filter)) {
		throw ("#IPS: invalid JQuery reference passed: " + TOS.call(jquery));
	}

	if (options.url == undefined || !URL_REGEX.exec(options.url)) {
		throw ("#IPS: Invalid URL: " + options.url);
	}

	if (options.id == undefined || !/\d+/.exec(options.id)) {
		throw ("#IPS: Invalid Auth ID: " + options.id);
	}

	if (options.code == undefined || options.code.length == 0
		|| options.cred == undefined || options.cred.length == 0) {
		throw ("#IPS: Invalid/undefined Auth Code/Cred");
	}

	this.jQuery = jquery;

	// Turn off caching (default behaviour of IE)
	this.jQuery.ajaxSetup({cache: false});

	// Strip the trailing slash off the URL if present.
	this.url = (options.url.charAt(options.url.length - 1) === '/') ? options.url.substring(0, options.url.length - 1) : options.url;

	// Convert to https if neccesary
	if (document.location.protocol == 'https:') {
		this.url = this.url.replace(/^http:/, 'https:');
	}

	this.id = options.id;
	this.code = options.code;
	this.cred = options.cred;

	// Base URL for all IPS API calls
	this.baseURL = this.url + "/services/cred/" + this.code + "/" + this.cred + "/2.1.0";

	// Get Cookie ID
	var cookieName = (options.permid && options.permid.length > 0) ? options.permid : TRACK_CNAME;
	this.permid = getCookie(cookieName);
	if (!this.permid || this.permid.length == 0) {
		// Cookie doesn't exist, create it.
		var d = new Date();
		var cookieValue = d.getTime() + "-" + Math.round((Math.random() * 999999999) + 1);
		setCookie(cookieName, cookieValue, 3650, "/", document.domain, "");
		this.permid = cookieValue;
	}

	// Get Email Tracking
	this.emailTrack = getUrlVar('emailTrack');

	// *************************************************************************
	// IPS Functions
	// *************************************************************************
	/**
	 * Sends an event notification to the IPS.
	 *
	 * @param event
	 *            Event type
	 * @param data
	 *            Key => Value map of data to be used to build the
	 *				notification query.
	 *
	 */
	this.handleNotification = function(event, data) {

		if (this.isVisitorBot()) {
			return;
		}

		if (event == undefined || event.length == 0) {
			throw ("#IPS: invalid event");
		}

		// Start building the final URL
		var endUrl = this.baseURL + "/notify/" + this.id + "/" + event + "?";

		// Push user fields into data
		data.permid = this.permid;
		if (this.emailTrack) {
			data.emailTrack = this.emailTrack;
		}

		// Convert data into query arguments
		var args = [];
		for (key in data) {
			args.push(encodeURIComponent(key) + "=" + encodeURIComponent(data[key]));
		}
		endUrl += args.join('&');
		endUrl += "&fmt=jsonp&callback=?";

		this.jQuery.getJSON(endUrl);
	};

	/**
	 * Sends an event notification to the IPS if a known search engine
	 * referred this page load with keywords.
	 *
	 * @param event
	 *            Event type
	 *
	 */
	this.handleReferringSearch = function(event) {
		if (this.isVisitorBot()) {
			return;
		}

		var refArgs;
		if (document.referrer) {
			// Check hostname: google, yahoo or bing appear between the first colon (:)
			// and the first forward-slash (/) aka the hostname part of the URL.
			if (/:\/\/[^\/]+(google|yahoo|bing)[^\/]+\//.exec(document.referrer)) {
				// Grab keywords: a query component appearing after the questionmark (?)
				// with the key of either p or q then everything after the subsequent equals
				// (=) until the next ampersand (&) or hash (#).
				var keywords = /\?.*[pq]=([^\&^#]*)/.exec(document.referrer);
				if (keywords && keywords.length > 0 && keywords[1].length > 0) {
					refArgs = new Object();
					refArgs.data = decodeURIComponent(keywords[1].replace(/\+/g, '%20'));
				}
			}
		}
		if (refArgs) {
			this.handleNotification(event, refArgs);
		}
	}

	/**
	 * Makes a suggestion request to the IPS and passes the returned
	 * data to a callback handler.
	 *
	 * @param reqCode
	 *            Request Code
	 *
	 * @param data
	 *            Key => Value map of data to be used to build the
	 *				suggestion request query
	 *
	 * @param dataHandler
	 *            Callback function that handles the returned data
	 *
	 */
	this.handleSuggestion = function(reqCode, data, dataHandler) {

		if (this.isVisitorBot()) {
			return;
		}

		if (!this.jQuery.isFunction(dataHandler)) {
			throw ("#IPS: callback argument is not a function");
		}

		// check that we have a valid strategy
		if (!reqCode || reqCode <= 0) {
			throw ("#IPS: Request Code not specified");
		}

		var endUrl = this.baseURL + "/suggest/" + this.id + "/" + reqCode + "?";

		// Push user fields into data
		data.permid = this.permid;

		// Convert data into query arguments
		var args = [];
		for (key in data) {
			args.push(encodeURIComponent(key) + "=" + encodeURIComponent(data[key]));
		}
		endUrl += args.join('&');
		endUrl += "&fmt=jsonp&callback=?";

		this.jQuery.getJSON(endUrl, dataHandler);
	}

	this.isVisitorBot = function() {
		if (!this.isBot) {
			// Default to non-bot unless a pattern matches
			this.isBot = false;
			if (navigator.userAgent && navigator.userAgent.length > 0) {
				// Valid UA Patterns for bots
				var botPatterns = [
					/AdsBot-Google/, /agbot\//, /Baiduspider\+/, /BlogPulseLive/, /Googlebot/, /008\//,
					/abby\//, /Ask Jeeves\//, /Butterfly\//, /DotBot\//, /Exabot\//, /mxbot\//, /ScoutJet/,
					/spbot\//, /Yahoo\! Slurp/, /YandexBot\//, /YoudaoBot\//, /Twiceler/, /Speedy Spider/,
					/msnbot/, /R6_/, /SBIder\//, /Sosospider\+/, /Surphace Scout/, /TinEye\//
				];
				var patternCount = botPatterns.length;
				for (var i = 0; i < patternCount; i++) {
					if (botPatterns[i].exec(navigator.userAgent)) {
						this.isBot = true;
						break; // Got a match, stop looping the patterns
					}
				}
			}
		}
		return this.isBot;
	}
}

/**
 * Gets a cookie value by the given name
 */
function getCookie(name) {
	// first we'll split this cookie up into name/value pairs
	// note: document.cookie only returns name=value, not the other components
	var cookies = document.cookie.split(';');
	var next = '';
	var cname = '';
	var cvalue = '';

	for (i = 0; i < cookies.length; i++) {
		// now we'll split apart each name=value pair
		next = cookies[i].split('=');

		// and trim left/right whitespace while we're at it
		cname = next[0].replace(/^\s+|\s+$/g, '');

		// if the extracted name matches passed check_name
		if (cname == name) {
			// we need to handle case where cookie has no value but exists (no =
			// sign, that is):
			if (next.length > 1) {
				cvalue = unescape(next[1].replace(/^\s+|\s+$/g, ''));
			}
			// note that in cases where cookie is initialized but no value, null
			// is returned
			return cvalue;
		}
	}

	return null;

}

/**
 * Creates a new cookie
 *
 * name: cookie name value: the value to set under the given name expires:
 * number of days the cookie is valid for, if not set will be valid fo browser
 * session path: path to set, use '/' for the root and is thus available to the
 * whole domain domain: set if using a subdomain
 *
 */
function setCookie(name, value, expires, path, domain, secure) {
	// set time, it's in milliseconds
	var today = new Date(); // TODO - repace Date fn for IE compatability
	today.setTime(today.getTime());

	if (expires) {
		expires = expires * 1000 * 60 * 60 * 24;
	}
	var exdate = new Date(today.getTime() + (expires)); // TODO - repace Date fn for IE compatability

	document.cookie = name + "=" + escape(value) + ((expires) ? ";expires=" + exdate.toGMTString() : "") + ((path) ? ";path=" + path : "") + ((domain) ? ";domain=" + domain : "")
			+ ((secure) ? ";secure" : "");
}

/**
 * Deletes a cookie
 *
 * name: the cookie name path: the cookie path domain: any associated domain
 *
 * @see #setCookie
 */
function deleteCookie(name, path, domain) {
	if (getCookie(name))
		document.cookie = name + '=' + ((path) ? ';path=' + path : '') + ((domain) ? ';domain=' + domain : '') + ';expires=Thu, 01-Jan-1970 00:00:01 GMT';
}

/**
 * Convert suggestion localisation to an map of key => value
 *
 * localisations: the localisations array
 *
 */
function locToMap(localisations) {
	var locs = [];
	for (var i in localisations) {
		var loc = localisations[i];
		locs[loc.key] = loc.value;
	}
	return locs;
}

/**
 * Extract an HTTP GET URL variable and return its value
 *
 * key: the key to search for
 */
function getUrlVar(key) {
	var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
	for (var i = 0; i < hashes.length; i++) {
		hash = hashes[i].split('=');
		if (hash[0] == key) {
			return hash[1];
		}
	}
	return null;
}

