(function () {
	NI.createSubmodule("Http");
})();

(function () {

	NI.requireComponent("Http");
	
	var RequestFactory = function () {
	};

	RequestFactory.prototype.toString = function () {
		return "RequestFactory";
	};

	RequestFactory.prototype.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
	};

	var _factories = [
        function () { return new XMLHttpRequest(); },
        function () { return new ActiveXObject("Msxml2.XMLHTTP"); },
        function () { return new ActiveXObject("Microsoft.XMLHTTP"); }
    ];

    var _factory = null;

	NI.Http.importComponent(new RequestFactory());

})();

(function () {

	NI.requireComponent("Http");

	var Utils = function () {
	};

	Utils.prototype.toString = function () {
		return "Utils";
	};

	Utils.prototype.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(request) : invalid XMLHttpRequest object passed in");
		}
	};

	Utils.prototype.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("&");
	};

    NI.Http.importComponent(new Utils());

})();

(function () {

	NI.requireComponent("Http");

    var Request = function Request() {
		NI.Http.requireComponent("RequestFactory");
		NI.Http.requireComponent("Utils");
        try {
            this.request = NI.Http.RequestFactory.createInstance();
        }
        catch (e) {
            throw new Error(this + " : " + (e.message ? e.message : e));
        }
    };

    Request.prototype.toString = function () {
        return "Request";
    };

    Request.prototype.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(settings) : " + (e.message ? e.message : e));
        }
    };

    Request.prototype.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(context) : " + (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(context) : The '" + context["method"] + "' request method is not supported");
    };

	NI.Http.importComponent(Request);

})();