/// <reference path="leaf.ts" />
var Leaf;
(function (Leaf) {
    var APIFactory = (function () {
        function APIFactory(option) {
            this.option = option;
            if (!(typeof this.option.defaultTimeout === "number")) {
                this.option.defaultTimeout = 0;
            }
            if (!this.option.dataField) {
                this.option.dataField = "data";
            }
            if (!this.option.errorField) {
                this.option.errorField = "error";
            }
            if (!(typeof this.option.prefix === "string")) {
                this.option.prefix = "";
            }
            if (!(typeof this.option.suffix === "string")) {
                this.option.suffix = "";
            }
            if (!this.option.root) {
                this.option.root = "";
            }
        }
        APIFactory.prototype.createAPI = function (option) {
            var api = new APIDeclare(this, option);
            return api;
        };
        APIFactory.prototype.createAPIFunction = function (option) {
            var api = this.createAPI(option);
            return api.getFunction();
        };
        APIFactory.prototype.solveResponse = function (err, response, callback) {
            callback(err, response);
        };
        APIFactory.prototype.request = function (option, callback) {
            var _this = this;
            var method = option.method || "GET";
            var queryString = this.buildQueryString(option.query);
            var url = option.url;
            var sendData = null;
            var headers = option.headers || {};
            if (method.toUpperCase() === "GET") {
                url += queryString;
                sendData = null;
            }
            else {
                sendData = queryString;
            }
            var xhr = new XMLHttpRequest();
            xhr.open(method, url, true);
            if (option.withCredentials) {
                xhr.withCredentials = true;
            }
            xhr.setRequestHeader("Accept", "application/json");
            xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4) { }
            };
            for (var name_1 in headers) {
                xhr.setRequestHeader(name_1, headers[name_1]);
            }
            var done = false;
            var _callback = callback;
            var timer = null;
            callback = function (err, response) {
                clearTimeout(timer);
                // disable multiple callback
                callback = function () { };
                _this.solveResponse(err, response, _callback);
                return;
            };
            if (option.timeout) {
                timer = setTimeout(function () {
                    callback = function () { };
                    _callback(new Leaf.Errors.Timeout("Request timeout after " + option.timeout), null);
                    xhr.abort();
                }, option.timeout * 1000);
            }
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 0 && !done) {
                    callback(new Leaf.Errors.NetworkError(), null);
                    return;
                }
                if (xhr.readyState === 4) {
                    done = true;
                    var data = void 0;
                    if (xhr.responseText) {
                        try {
                            data = JSON.parse(xhr.responseText);
                        }
                        catch (e) {
                            callback(new Leaf.Errors.NetworkError("Broken Response"), null);
                            return;
                        }
                        callback(null, data);
                    }
                    else {
                        callback(new Leaf.Errors.NetworkError("Empty response"), null);
                    }
                }
            };
            if (method.toLowerCase() != "get") {
                xhr.send(sendData);
            }
            else {
                xhr.send();
            }
            return xhr;
        };
        APIFactory.prototype.buildQueryString = function (query) {
            if (!query) {
                return "";
            }
            var kvs = [];
            for (var prop in query) {
                var value = query[prop] || "";
                kvs.push([encodeURIComponent(prop), encodeURIComponent(value)].join("="));
            }
            return kvs.join("&");
        };
        return APIFactory;
    }());
    Leaf.APIFactory = APIFactory;
    var APIDeclare = (function () {
        function APIDeclare(factory, option) {
            this.factory = factory;
            this.option = option;
        }
        APIDeclare.prototype.getFunction = function () {
            var fn = this.invoke.bind(this);
            fn.API = this;
            return fn;
        };
        APIDeclare.prototype.invoke = function (data, callback) {
            if (data === void 0) { data = {}; }
            var method = this.option.method;
            var url = this.factory.option.root + this.factory.option.prefix + this.option.path + this.factory.option.suffix;
            var routeParams = (url.match(/:[a-z_][a-z0-9_]*/ig) || []).map(function (item) { return item.substring(1); });
            if (this.option.data) {
                // default params
                for (var prop in this.option.data) {
                    if (typeof data[prop] == "undefined") {
                        data[prop] = this.option.data.prop;
                    }
                }
                for (var prop in data) {
                    if (routeParams.indexOf(prop) > 0) {
                        url = url.replace(new RegExp(":" + prop, "g"), encodeURIComponent(data[prop]));
                        delete data[prop];
                    }
                }
            }
            var reqOption = {
                url: url, method: method,
                query: data,
                timeout: this.option.timeout,
                headers: this.option.headers,
                withCredentials: this.option.withCredentials
            };
            this.factory.request(reqOption, callback);
        };
        return APIDeclare;
    }());
    Leaf.APIDeclare = APIDeclare;
})(Leaf || (Leaf = {}));
//# sourceMappingURL=apiFactory.js.map