/// <summary></summary>

/*global document */
FetchFromWithCookies = (function() {

	var store = [],
		keyExists,
		find,
		stingifyValue,
		forSome,
		escapeRegexString,
		setPair;
	
	forSome = function(func, scope) {
		scope = scope || store;
		for(var i = 0, l = store.length; i < l; i++) {
			if(func.call(scope, store[i], i)) {
				return true;
			}
		}
		return false;
	};
	
	keyExists = function(key) {
		return forSome(function(o, i) {
			return o.k == key;
		});
	};
	
	find = function(key) {
		var rec = null;
		forSome(function(o, i) {
			if(o.k == key) {
				rec = o;
				return true;
			}
			return false;
		});
		return rec;
	};
	
	setPair = function(key, value) {
		var rec = find(key);
		if(rec) {
			rec.v = value;
		} else {
			store.push({k:key, v:value});
		}
	};
	
	escapeRegexString = function(text) {
		/// <summary>Escape special regex chars so the string can be used in regular expression</summary>
		/// <param name="text" type="String">String to escape</param>
		// lazyly create the replacement regex
		var callee = arguments.callee;
		if (!callee.sRE) {
			var specials = [
				'/', '.', '*', '+', '?', '|',
				'(', ')', '[', ']', '{', '}', '\\'
			];
			callee.sRE = new RegExp('(\\' + specials.join('|\\') + ')', 'g');
		}
		return text.replace(callee.sRE, '\\$1');
	};
	
	stingifyValue = function(o) {
		if (o instanceof String || o instanceof Boolean || o instanceof Number) {
			o = o.valueOf();
		} else if (o === null) {
			return 'null';
		}
		
		switch (typeof o) {
			case 'number' : 
				o = isFinite(o) ? String(o) : 'null';
				break;
			case 'boolean': 
				o = String(o);
				break;
			case 'object': 
				if(typeof o.toString == "function") {
					o = o.toString();
				} else {
					o = "[Object]";
				}
				break;
		}
		return o;
	};

	var FetchFromWithCookies = {
		_URL_PARAM_NAME: "CTFetchCookies",
		
		cookieNameDecoder: decodeURIComponent,
		cookieValueDecoder: function(x){return x;},
		
		clear: function() {
			/// <summary>Cleares all the cookies stored in the object</summary>
			store = [];
		},
		set: function(cookieName, cookieValue) {
			/// <summary>Adds a cookie to the fetching process</summary>
			/// <param name="cookieName" type="String">The name of the cookie to add</param>
			/// <param name="cookieValue">Value of the cookie. This parameter is optional, if ommited the value will 
			/// be looked for in the cookies collection</param>
		
			// if no value is passed, it is assumed that the value should be fetched from the cookies collection
			if(cookieValue === null || typeof cookieValue == "undefined") {
				FetchFromWithCookies.setFromCookie(cookieName);
				return;
			}
			
			cookieValue = stingifyValue(cookieValue);
			setPair(cookieName, cookieValue);
		},
		setFromCookie: function(cookieNameRegex) {
			/// <summary>Addes a cookie to the fetching process from the document's cookies collection</summary>
			/// <param name="cookieNameRegex" type="String">A Regular Expression pattern to match agains the cookies in the document</param>
			var test,
				cookieName,
				cookieNameValue,
				cookieValue,
				cookieParts = document.cookie.split(/;\s/g),
				i;
			if(typeof cookieNameRegex == "string") {
				test = function(val) {return val == cookieNameRegex;};
			} else if(typeof cookieNameRegex.test == "function") {
				test = function(val) {return cookieNameRegex.test(val);};
			} else {
				test = function() {return false;};
			}
			
			for(i = 0; i < cookieParts.length; i++) {
                //check for normally-formatted cookie (name-value)
                cookieNameValue = cookieParts[i].match(/([^=]+)=/i);
                if (cookieNameValue instanceof Array){
                    try {
                        cookieName = FetchFromWithCookies.cookieNameDecoder(cookieNameValue[1]);
                        cookieValue = FetchFromWithCookies.cookieValueDecoder(cookieParts[i].substring(cookieNameValue[1].length+1));
                    } catch (ex){
                        //ignore the entire cookie - encoding is likely invalid
                    }
                } else {
                    //means the cookie does not have an "=", so treat it as a boolean flag
                    cookieName = FetchFromWithCookies.cookieNameDecoder(cookieParts[i]);
                    cookieValue = cookieName;
                }
			
				if(test(cookieName)) {
					setPair(cookieName, cookieValue);
				}
			}
		},
		constructCookiesParam: function() {
			/// <summary>Builds the string that should be used after the hash (#) in the FetchFromUrl variable</summary>
			/// <returns>A Url encoded string to be used in the hash (#) of the FetchFromUrl variable</summary>
			var ar = [];
			forSome(function(o, i) {
				ar.push(o.k);
				ar.push("=");
				ar.push(o.v);
				ar.push(";");
				return false;
			});
			return FetchFromWithCookies._URL_PARAM_NAME + "=" + encodeURIComponent(ar.join(""));
		},
		constructCookiesHash: function() {
			return "#" + FetchFromWithCookies.constructCookiesParam();
		},
		constructFetchFromUrl: function(url) {
			url = url || window.location.href;
			if(!(url.indexOf("#") >= 0)) {
				url += "#";
			} else {
				url += "&";
			}
			url += FetchFromWithCookies.constructCookiesParam();
			return url;
		}
	};
	
	return FetchFromWithCookies;

})();