// JS toolbox v0.1
// Author: Thomas Hopkins [hopkinsth@gmail.com]
var jsToolbox = (function () {
		"use strict";
		var base,
			url;
		base = (function () {
			var that = {};
			that.match_obj = function (apple, orange) {
				var key,
					loop_test = {},
					i;
				if (apple.hasOwnProperty !== Object.prototype.hasOwnProperty) {
					apple.hasOwnProperty = Object.prototype.hasOwnProperty;
				}
				if (orange.hasOwnProperty !== Object.prototype.hasOwnProperty) {
					orange.hasOwnProperty = Object.prototype.hasOwnProperty;
				}
				for (key in apple) {
					if (apple.hasOwnProperty(key) && 
						orange.hasOwnProperty(key)) {
						if (typeof apple[key] !== 'object') {
							if (apple[key] === orange[key]) {
								loop_test[key] = 'y';
							} else {
								loop_test[key] = 'n';
							}
						} else if (typeof apple[key] === 'object') {
							if (that.isObject(apple[key])) {
								if (typeof orange[key] !== 'object' || 
									orange[key].constructor !== Object) {
									loop_test[key] = 'n';
								} else {
									loop_test[key] = that.match_obj(apple[key], orange[key]);
								}
							} else if (that.isArray(apple[key])) {
								loop_test[key] = that.match_array(apple[key], orange[key]);
							} 
						}
					}
				}
				return loop_test;
			};
			
			//match_obj: loop through apple to compare to orange
			//will call match_array if a key contains an array

			//match_array: loop through apple to compare to orange
			//will call match_obj if index contains an object literal
			that.match_array = function (apple, orange) {
				var match = {},
					i;
				if (typeof apple !== typeof orange ||
					typeof apple !== 'object' ||
					!that.isArray(apple)) {
					return 'n';
				}
				for (i = 0; i < apple.length; i += 1) {
					if (typeof apple[i] === 'object' &&
						typeof orange[i] === 'object') {
						if (that.isArray(apple[i]) &&
							that.isArray(orange[i])) {
							match[i] = that.match_array(apple[i], orange[i]);
						} else if (that.isObject(apple[i]) &&
							that.isObject(orange[i])) {
							match[i] = that.match_obj(apple[i], orange[i]);
						}
					} else {
						if (apple[i] === orange[i]) {
							match[i] = 'y';
						} else {
							match[i] = 'n';
						}
					} 
				}
				return match;
			};
			that.get_keys = function (obj) {
				var key,
					array_of_keys = [];
				obj.hasOwnProperty = Object.prototype.hasOwnProperty;
				for (key in obj) {
					if (obj.hasOwnProperty(key) &&
						key !== 'hasOwnProperty') {
						if (typeof obj[key] === 'object' &&
							that.isObject(obj[key])) {
							array_of_keys.push(get_keys(obj[key]));
						} else {
							array_of_keys.push(key);
						}
					}
				}
				return array_of_keys;
			};
			that.isArray = function (candidate) {
				return Object.prototype.toString.call(candidate) === '[object Array]';
			};
			that.isObject = function (candidate) {
				return Object.prototype.toString.call(candidate) === '[object Object]';
			}
			return that;
		}());
		url = function (first_url, call, requirements) {
			var that = {},
				urls = [],
				url_regex = /^(?:([A-Za-z]+):)?(?:\/{0,3})((?:[0-9.\-A-Za-z]+\.(?:com|net|org|gov|edu|int|mil|biz|info|mobi|name|tel|tw|co\.uk|be)))?(?::(\d+))?(?:\/{0,2}([!\$'\(\)\*\-\/\[\]_\.A-Za-z0-9]*))?(?:\?([^#]*))?(?:#(.*))?$/,
				main_parts = ['protocol',
					'host',
					'port',
					'path',
					'query',
					'hash'],
				add,
				object_push,
				match_obj = base.match_obj,
				match_array = base.match_array,
				check_matches_with_required,
				required_match,
				get_keys = base.get_keys,
				extract_parameters, 
				parse_url,
				input_parse,
				check_url;
					
				object_push = function (url_object, callback, require) {
					var url_id = (urls.push({}) -1),
						parts,
						require_split,
						param_split,
						required_obj = {},
						i;
					if (url_object.hasOwnProperty !== 
						Object.prototype.hasOwnProperty) {
						url_object.hasOwnProperty = Object.prototype.hasOwnProperty;
					}
					urls[url_id] = url_object;

					if (callback !== undefined) {
						urls[url_id].callback = callback;
					}
					if (require !== undefined) {
						parts = require.split(',');
						for (i = 0; i < parts.length; i += 1) {
							require_split = parts[i].split(':');
							if (require_split.length > 1) {
								param_split = require_split[1].split('&');
								required_obj[require_split[0]] = param_split;
							} else {
								required_obj[require_split[0]] = '';
							}
							urls[url_id].must_match = required_obj;
						}
					} else {
						required_obj.all = '';
						urls[url_id].must_match = required_obj;
					}
					return url_id;
				};
				
				add = function (the_url, callback, require) {
					var new_url,
						u;
					if (the_url !== undefined) {
						if (typeof the_url === 'string') {
							new_url = object_push(parse_url(the_url), callback, require);
							//console.log(callback);
						} else if (typeof the_url === 'object' &&
							the_url.length !== undefined &&
							the_url.constructor === Array) {
							//console.log(the_url);
							for (u = 0; u < the_url.length; u += 1) {
								if (typeof the_url[u] === 'string') {
									new_url = object_push(parse_url(the_url[u]), callback, require);
								} else if (typeof the_url[u] === 'object' &&
									the_url[u].url !== undefined &&
									the_url[u].action !== undefined) {
									if (the_url[u].url === document.location) {
										new_url = add(the_url[u].url.toString(), the_url[u].action, the_url[u].must_match);
									} else {
										new_url = object_push(parse_url(the_url[u].url), the_url[u].action, the_url[u].must_match);
									}
								} 
							}
						} else if (the_url === document.location) {
							new_url = add(the_url.toString(), callback, require);
						}
						return that.urls[new_url];
					}
				};
				
				check_matches_with_required = function (given_matches, required_matches) {
					var required_part,
						builder,
						build_all_required = function (givens) {
							var needed = {};
							if (givens.hasOwnProperty !== 
								Object.prototype.hasOwnProperty) {
								givens.hasOwnProperty = Object.prototype.hasOwnProperty;	
							}
							for (builder in givens) {
								if (givens.hasOwnProperty(builder) &&
									builder !== 'hasOwnProperty') {
									if (typeof givens[builder] === 'object' &&
										givens[builder].constructor === Object) {
										build_all_required(givens[builder]);
									} else {
										needed[builder] = '';
									}
								}
							}
							return needed;
						},
						will_match,
						i;
						
						if (typeof required_matches.all !== 'undefined') {
							required_matches = build_all_required(given_matches);
						}

						for (required_part in required_matches) {
							if (required_matches.hasOwnProperty(required_part)) {
								if (required_matches[required_part] === '' &&
									given_matches[required_part] === 'y') {
									will_match = 'y';
								} else if (typeof required_matches[required_part] ===
									'object' &&
									required_matches[required_part].constructor ===
									Object) {
									will_match = 
										check_matches_with_required(given_matches[required_part],
										required_matches[required_part]);
									
								} else if (typeof required_matches[required_part] ===
									'object' &&
									required_matches[required_part].constructor ===
									Array) {
									for(i = 0; i < required_matches[required_part].length; i += 1) {
										if(typeof given_matches[required_part] === 'object' &&
											given_matches[required_part].constructor === Object) {
											if (given_matches[required_part][required_matches[required_part][i]] ===
												'y') {
												will_match = 'y';	
											} else {
												return 'n';
											}
										} 
									}	
								}
							}
						}
						return will_match || 'n';
				}
				check_url = function (current_url) {
					var current_url_obj = parse_url(current_url),
						matches,
						required_matches,
						url_will_match = '',
						key,
						u;
					for (u = 0; u < urls.length; u += 1) {
						required_matches = urls[u].must_match;
						//matches = match_obj(current_url_obj, urls[u]);

						url_will_match = check_matches_with_required(match_obj(current_url_obj, urls[u]), required_matches);
						//console.log(url_will_match);
						if (url_will_match === 'y' &&
							urls[u].callback !== undefined &&
							urls[u].callback !== null) {
								//console.log(url_will_match);
							urls[u].callback.apply(urls[u]);
						}
					}
				};
				extract_parameters = function (url, no_parse) {
					var query_string,
						query_split,
						params = {},
						param_split,
						i;
					//This will need a more elegant solution here later;
					//currently all this does is check if the url array has a zero
					//but not a one indexed value. There are better ways to know
					//the length of the array.
					if (urls[0] !== undefined &&
						urls[1] === undefined &&
						this !== extract_parameters) {
						query_string = urls[0].query;
					} else {
						if (typeof url === 'string') {
							query_string = url;
						} else {
							query_string = (no_parse === undefined ? parse_url(url).query : url.query) ||
							undefined;
						}
								
					}
					query_split = (typeof query_string === 'undefined' ? undefined : query_string.split('&'));
					if (query_split === undefined) {
						return undefined;
					}
					for (i = 0; i < query_split.length; i += 1) {
						param_split = query_split[i].split('=');
						params[param_split[0]] = param_split[1];
					}
					
					return params;
				};
				parse_url = function (url) {
					if (typeof url === 'object' && 
						url.constructor === Object) {
						return url;
					}
					var parsed_url = url_regex.exec(url) || 'none';
					return {
						protocol: parsed_url[1],
						host: parsed_url[2],
						port: parsed_url[3],
						path: parsed_url[4],
						query: parsed_url[5] === undefined ? undefined :
							extract_parameters.call(extract_parameters, parsed_url[5], 'no_parse'),
						hash: parsed_url[6]
					};
				};
				

				that.check_url = check_url;
				that.add = add;
				that.urls = urls;
				that.url_parts = parse_url;
				that.match_obj = match_obj;
				that.match_array = match_array;
				that.extract_parameters = extract_parameters;
				add(first_url, call, requirements);
				return that;

		};
		return {  
			base: base,
			url: url
		};
}());

