if (typeof(NI) == "undefined") { var NI = {}; }

(function () {

	NI.HTTP = {
		RequestFactory : null,		
		Request : null,
		Utils : null
	};
	
	
	(function () {
		var _factories = [
			function () { return new XMLHttpRequest(); },
			function () { return new ActiveXObject("Msxml2.XMLHTTP"); },
			function () { return new ActiveXObject("Microsoft.XMLHTTP"); }	
		];
		
		var _factory = null;

		NI.HTTP.RequestFactory = {
			
			toString : function () {
				return "NI.HTTP.RequestFactory";
			},

			createInstance : function () {
				if (_factory != null) {
					return _factory();
				}
				
				for (var i = 0, length = _factories.length; i < length; i++) {
					try {
						var factory = _factories[i];
						var request = factory();
						if (request != null) {
							_factory = factory;
							return request;
						}
					}
					catch(e) {
						continue;
					}
				}		
				
				_factory = function () {
					throw new Error(this + "::createInstance : XMLHttpRequest not supported");
				}
				
				_factory(); // Throw an error
			}

		};

	})();


	(function () {

		NI.HTTP.Utils = {

			toString : function () {
				return "NI.HTTP.Utils";
			},
			
			getResponse : function (request) {
				try {
					switch (request.getResponseHeader("Content-Type")) {
						case "text/xml":
							return request.responseXML;
						case "text/json":
						case "text/javascript":
						case "application/javascript":
						case "application/x-javascript":
							return eval(request.responseText);
						default:
							return request.responseText;
					}
				}
				catch (e) {
					throw new Error(this + "::getResponse : invalid XMLHttpRequest object passed in");
				}
			},

			encodeFormData : function (data) {
				var pairs = [];
				var re = /%20/g;
				var getEncodedValue = function (value) {
					return window.encodeURIComponent(value).replace(re, "+");
				};
				for (var name in data) {
					var value = data[name].toString();
					var pair = getEncodedValue(name) + "=" + getEncodedValue(value);
					pairs.push(pair);
				}	
				return pairs.join("&");
			}
		
		};

	})();


	(function () {

		NI.HTTP.Request = function () {
			try {
				this.request = NI.HTTP.RequestFactory.createInstance();
			}
			catch (e) {
				throw new Error(this + " : " + (e.message ? e.message : e));
			}
		};

		NI.HTTP.Request.prototype = {
			
			toString : function () {
				return "NI.HTTP.Request";
			},
			
			send : function (settings) {
				settings = settings || {};

				var context = {
					url : settings.url || "",
					method : settings.method || "",
					data : settings.data || {},
					timeout : settings.timeout || 0,
					timer : null,
					handlers : settings.handlers || {}
				};

				try {
					_setTimeoutHandler.call(this, context);
					_setStateChangeHandlers.call(this, context);
					_issueRequest.call(this, context);
				}
				catch (e) {
					throw new Error(this + "::send : " + (e.message ? e.message : e));
				}
			},

			getParsedHeaders : function () {
				var headers = {};
				var headersText = this.request.getAllResponseHeaders();
				var lines = headersText.split("\n");
				var linesNum = lines.length;
				
				if (linesNum > 0) {
					var ls = /^\s*/;
					var ts = /\s*$/;			
					
					var trim = function (value) { 
						return value ? value.replace(ls, "").replace(ts, "") : "";
					};
					
					for (var i = 0; i < linesNum; i++) {
						var line = lines[i];
						if (!line.match(/\S/)) {
							continue;
						}				
						var parts = line.split(":");
						var name = trim(parts[0]);
						var value = trim(parts[1]);
						headers[name] = value;
					}
				}
				
				return headers;
			}

		};
		

		var _setTimeoutHandler = function (context) {
			var request = this.request;
			var timeout = context["timeout"];			
			if (timeout) {
				var handler = context["handlers"]["timeout"] || function () {};
				context["timer"] = window.setTimeout(
					function() {
						request.abort();
						handler(context["url"]);
					},
					timeout
				);
			}
		};

		var _setStateChangeHandlers = function (context) {
			var request = this.request;
			var handlers = context["handlers"];
			var responseHandler = handlers["response"] || function () {};
			var errorHandler = handlers["error"] || function () {};
			request.onreadystatechange = function () {				
				if (request.readyState == 4) {					
					if (context["timer"]) {
						window.clearTimeout(context["timer"]);
					}
					if (request.status == 200) {
						var response = NI.HTTP.Utils.getResponse(request);
						responseHandler(response);
					}
					else {
						errorHandler(request);
					}
				}
			};
		};

		var _issueRequest = function (context) {				
			var request = this.request;
			var method = context["method"].toUpperCase();
			var supportedMethods = ["GET", "POST", "HEAD"];
			for (var i = 0, length = supportedMethods.length; i < length; i++) {
				if (method == supportedMethods[i]) {
					var target = context["url"];
					var data = NI.HTTP.Utils.encodeFormData(context["data"]);
					if (method == "GET" && data.match(/\S/)) {
						target += (target.indexOf("?") < 0 ? "?" : "&") + data;
					}
					try {
						request.open(method, target, true);
					}
					catch (e) {
						throw new Error(this + "::_issueRequest : " + (e.message ? e.message : e));
					}
					if (method == "POST") {
						request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
						request.send(data);
					}
					else {
						request.send(null);
					}
					return;
				}
			}
			throw new Error(this + "::_issueRequest : The '" + context["method"] + "' request method is not supported");
		};

	})();

})();
