var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
(function (factory) {
    if (typeof module === "object" && typeof module.exports === "object") {
        var v = factory(require, exports);
        if (v !== undefined) module.exports = v;
    }
    else if (typeof define === "function" && define.amd) {
        define(["require", "exports", "./AjaxOptions", "../../App", "../../Atom", "../../core/AtomBridge", "../../core/types", "../../di/Inject", "../../di/TypeKey", "../CacheService", "../JsonService", "./JsonError"], factory);
    }
})(function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.BaseService = exports.ServiceParameter = exports.Cancel = exports.Patch = exports.Put = exports.Delete = exports.Get = exports.Post = exports.XmlBody = exports.BodyFormModel = exports.RawBody = exports.Body = exports.Queries = exports.Query = exports.Header = exports.Path = void 0;
    var AjaxOptions_1 = require("./AjaxOptions");
    var App_1 = require("../../App");
    var Atom_1 = require("../../Atom");
    var AtomBridge_1 = require("../../core/AtomBridge");
    var types_1 = require("../../core/types");
    var Inject_1 = require("../../di/Inject");
    var TypeKey_1 = require("../../di/TypeKey");
    var CacheService_1 = require("../CacheService");
    var JsonService_1 = require("../JsonService");
    var JsonError_1 = require("./JsonError");
    // tslint:disable-next-line
    function methodBuilder(method) {
        // tslint:disable-next-line
        return function (url, options) {
            // tslint:disable-next-line
            return function (target, propertyKey, descriptor) {
                target.methods = target.methods || {};
                var a = target.methods[propertyKey];
                var oldFunction = descriptor.value;
                // tslint:disable-next-line:typedef
                descriptor.value = function () {
                    var _this = this;
                    var args = [];
                    for (var _i = 0; _i < arguments.length; _i++) {
                        args[_i] = arguments[_i];
                    }
                    if (this.testMode || Atom_1.Atom.designMode) {
                        // tslint:disable-next-line:no-console
                        console.log("Test Design Mode: " + url + " .. " + args.join(","));
                        var ro = oldFunction.apply(this, args);
                        if (ro) {
                            return ro;
                        }
                    }
                    var jsCache = options ? options.jsCacheSeconds : 0;
                    if (jsCache) {
                        var cacheService = this.app.resolve(CacheService_1.default);
                        var jArgs = args.map(function (arg) { return arg instanceof types_1.CancelToken ? null : arg; });
                        var key = this.constructor.name + ":" + method + ":" + url + ":" + JSON.stringify(jArgs);
                        return cacheService.getOrCreate(key, function (e) {
                            e.ttlSeconds = jsCache;
                            return _this.invoke(url, method, a, args, options);
                        });
                    }
                    return this.invoke(url, method, a, args, options);
                };
            };
        };
    }
    // tslint:disable-next-line
    function parameterBuilder(paramName, defaultValue) {
        // tslint:disable-next-line
        return function (key) {
            // console.log("Declaration");
            // console.log({ key:key});
            // tslint:disable-next-line
            return function (target, propertyKey, parameterIndex) {
                // console.log("Instance");
                // console.log({ key:key, propertyKey: propertyKey,parameterIndex: parameterIndex });
                target.methods = target.methods || {};
                var a = target.methods[propertyKey];
                if (!a) {
                    a = [];
                    target.methods[propertyKey] = a;
                }
                a[parameterIndex] = new ServiceParameter(paramName, key, defaultValue);
            };
        };
    }
    /**
     * This will register Url path fragment on parameter.
     *
     * @example
     *
     *      @Get("/api/products/{category}")
     *      async getProducts(
     *          @Path("category")  category: number
     *      ): Promise<Product[]> {
     *      }
     *
     * @export
     * @function Path
     * @param {name} - Name of the parameter
     */
    exports.Path = parameterBuilder("Path");
    /**
     * This will register header on parameter.
     *
     * @example
     *
     *      @Get("/api/products/{category}")
     *      async getProducts(
     *          @Header("x-http-auth")  category: number
     *      ): Promise<Product[]> {
     *      }
     *
     * @export
     * @function Path
     * @param {name} - Name of the parameter
     */
    exports.Header = parameterBuilder("Header");
    /**
     * This will register Url query fragment on parameter.
     *
     * @example
     *
     *      @Get("/api/products")
     *      async getProducts(
     *          @Query("category")  category: number
     *      ): Promise<Product[]> {
     *      }
     *
     * @export
     * @function Query
     * @param {name} - Name of the parameter
     */
    exports.Query = parameterBuilder("Query");
    /**
     * This will register Url query fragments on parameter of type object
     *
     * @example
     *
     *     @Get("/api/products")
     *     async getProducts(
     *          @Queries queries: { [key: string]: string | number | boolean | null }
     *     ): Promise<Product[]> {
     *         return null;
     * }
     */
    exports.Queries = parameterBuilder("Queries")("");
    /**
     * This will register data fragment on ajax.
     *
     * @example
     *
     *      @Post("/api/products")
     *      async getProducts(
     *          @Query("id")  id: number,
     *          @Body product: Product
     *      ): Promise<Product[]> {
     *      }
     *
     * @export
     * @function Body
     */
    exports.Body = parameterBuilder("Body")("");
    exports.RawBody = parameterBuilder("RawBody")("");
    /**
     * This will register data fragment on ajax in old formModel way.
     *
     * @example
     *
     *      @Post("/api/products")
     *      async getProducts(
     *          @Query("id")  id: number,
     *          @BodyFormModel product: Product
     *      ): Promise<Product[]> {
     *      }
     *
     * @export
     * @function BodyFormModel
     */
    exports.BodyFormModel = parameterBuilder("BodyFormModel")("");
    exports.XmlBody = parameterBuilder("XmlBody")("");
    /**
     * Http Post method
     * @example
     *
     *      @Post("/api/products")
     *      async saveProduct(
     *          @Body product: Product
     *      ): Promise<Product> {
     *      }
     *
     * @export
     * @function Post
     * @param {url} - Url for the operation
     */
    exports.Post = methodBuilder("Post");
    /**
     * Http Get Method
     *
     * @example
     *
     *      @Get("/api/products/{category}")
     *      async getProducts(
     *          @Path("category") category?:string
     *      ): Promise<Product[]> {
     *      }
     *
     * @export
     * @function Body
     */
    exports.Get = methodBuilder("Get");
    /**
     * Http Delete method
     * @example
     *
     *      @Delete("/api/products")
     *      async deleteProduct(
     *          @Body product: Product
     *      ): Promise<Product> {
     *      }
     *
     * @export
     * @function Delete
     * @param {url} - Url for the operation
     */
    exports.Delete = methodBuilder("Delete");
    /**
     * Http Put method
     * @example
     *
     *      @Put("/api/products")
     *      async saveProduct(
     *          @Body product: Product
     *      ): Promise<Product> {
     *      }
     *
     * @export
     * @function Put
     * @param {url} - Url for the operation
     */
    exports.Put = methodBuilder("Put");
    /**
     * Http Patch method
     * @example
     *
     *      @Patch("/api/products")
     *      async saveProduct(
     *          @Body product: any
     *      ): Promise<Product> {
     *      }
     *
     * @export
     * @function Patch
     * @param {url} - Url for the operation
     */
    exports.Patch = methodBuilder("Patch");
    /**
     * Cancellation token
     * @example
     *
     *      @Put("/api/products")
     *      async saveProduct(
     *          @Body product: Product
     *          @Cancel cancel: CancelToken
     *      ): Promise<Product> {
     *      }
     *
     * @export
     * @function Put
     * @param {url} - Url for the operation
     */
    function Cancel(target, propertyKey, parameterIndex) {
        if (!target.methods) {
            target.methods = {};
        }
        var a = target.methods[propertyKey];
        if (!a) {
            a = [];
            target.methods[propertyKey] = a;
        }
        a[parameterIndex] = new ServiceParameter("cancel", "");
    }
    exports.Cancel = Cancel;
    var ServiceParameter = /** @class */ (function () {
        function ServiceParameter(type, key, defaultValue) {
            this.type = type;
            this.key = key;
            this.defaultValue = defaultValue;
            this.type = type.toLowerCase();
            this.key = key;
        }
        return ServiceParameter;
    }());
    exports.ServiceParameter = ServiceParameter;
    function BaseUrl(baseUrl) {
        return function (target) {
            var key = TypeKey_1.TypeKey.get(target);
            BaseService.baseUrls[key] = baseUrl;
        };
    }
    exports.default = BaseUrl;
    var globalNS = (typeof global !== "undefined") ? global : window;
    if (!globalNS.XMLHttpRequest) {
        // tslint:disable-next-line: no-var-requires
        globalNS.XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
    }
    /**
     *
     *
     * @export
     * @class BaseService
     */
    var BaseService = /** @class */ (function () {
        function BaseService(app, jsonService) {
            this.app = app;
            this.jsonService = jsonService;
            this.testMode = false;
            this.showProgress = true;
            this.showError = false;
            // bs
            this.methods = {};
            this.methodReturns = {};
            this.jsonOptions = null;
            this.jsonOptions = __assign({}, this.jsonService.options);
        }
        BaseService.prototype.encodeData = function (o) {
            o.dataType = "application/json";
            o.data = this.jsonService.stringify(o.data, this.jsonOptions);
            o.contentType = "application/json";
            return o;
        };
        BaseService.prototype.sendResult = function (result, error) {
            return new Promise(function (resolve, reject) {
                if (error) {
                    setTimeout(function () {
                        reject(error);
                    }, 1);
                    return;
                }
                setTimeout(function () {
                    resolve(result);
                }, 1);
            });
        };
        BaseService.prototype.invoke = function (url, method, bag, values, methodOptions) {
            return __awaiter(this, void 0, void 0, function () {
                var p, t, bu, busyIndicator, options, headers, jsonOptions, i, p, vi, v, vs, replacer, key, element, xhr, response;
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            if (this.baseUrl === undefined) {
                                p = Object.getPrototypeOf(this);
                                while (p) {
                                    t = TypeKey_1.TypeKey.get(p.constructor || p);
                                    bu = BaseService.baseUrls[t];
                                    if (bu) {
                                        this.baseUrl = bu;
                                        break;
                                    }
                                    p = Object.getPrototypeOf(p);
                                }
                                if (this.baseUrl === undefined) {
                                    this.baseUrl = null;
                                }
                            }
                            if (this.baseUrl) {
                                if (!/^\//.test(url)) {
                                    url = "" + this.baseUrl + url;
                                }
                            }
                            busyIndicator = this.showProgress ? (this.app.createBusyIndicator()) : null;
                            _a.label = 1;
                        case 1:
                            _a.trys.push([1, , 3, 4]);
                            url = UMD.resolvePath(url);
                            options = new AjaxOptions_1.AjaxOptions();
                            options.method = method;
                            if (methodOptions) {
                                options.headers = methodOptions.headers;
                                options.dataType = methodOptions.accept;
                            }
                            headers = options.headers = options.headers || {};
                            // this is necessary to support IsAjaxRequest in ASP.NET MVC
                            if (!headers["X-Requested-With"]) {
                                headers["X-Requested-With"] = "XMLHttpRequest";
                            }
                            options.dataType = options.dataType || "application/json";
                            jsonOptions = __assign(__assign({}, this.jsonOptions), (methodOptions ? methodOptions.jsonOptions : {}));
                            if (bag) {
                                for (i = 0; i < bag.length; i++) {
                                    p = bag[i];
                                    vi = values[i];
                                    v = vi === undefined ? p.defaultValue : vi;
                                    if (v instanceof types_1.CancelToken) {
                                        options.cancel = v;
                                        continue;
                                    }
                                    switch (p.type) {
                                        case "path":
                                            if (v === undefined) {
                                                continue;
                                            }
                                            vs = v + "";
                                            replacer = "{" + p.key + "}";
                                            url = url.split(replacer).join(vs);
                                            break;
                                        case "query":
                                            if (v === undefined) {
                                                continue;
                                            }
                                            if (url.indexOf("?") === -1) {
                                                url += "?";
                                            }
                                            if (!/(\&|\?)$/.test(url)) {
                                                url += "&";
                                            }
                                            url += encodeURIComponent(p.key) + "=" + encodeURIComponent(v);
                                            break;
                                        case "queries":
                                            if (url.indexOf("?") === -1) {
                                                url += "?";
                                            }
                                            if (!/(\&|\?)$/.test(url)) {
                                                url += "&";
                                            }
                                            for (key in v) {
                                                if (v.hasOwnProperty(key)) {
                                                    element = v[key];
                                                    if (element) {
                                                        url += encodeURIComponent(key) + "=" + encodeURIComponent(element) + "&";
                                                    }
                                                }
                                            }
                                            break;
                                        case "body":
                                            options.data = v;
                                            options = this.encodeData(options);
                                            break;
                                        case "bodyformmodel":
                                            options.data = v;
                                            break;
                                        case "rawbody":
                                            options.data = v;
                                            break;
                                        case "xmlbody":
                                            options.contentType = "text/xml";
                                            options.data = v;
                                            break;
                                        case "cancel":
                                            options.cancel = v;
                                            break;
                                        case "header":
                                            if (v === undefined) {
                                                continue;
                                            }
                                            headers[p.key] = v;
                                            break;
                                    }
                                }
                            }
                            options.url = url;
                            return [4 /*yield*/, this.ajax(url, options)];
                        case 2:
                            xhr = _a.sent();
                            if (/json/i.test(xhr.responseType)) {
                                response = this.jsonService.parse(xhr.responseText, jsonOptions);
                                if (xhr.status >= 400) {
                                    throw new JsonError_1.default(response.exceptionMessage ||
                                        response.message ||
                                        "Json Server Error", response);
                                }
                                if (methodOptions && methodOptions.returnHeaders) {
                                    return [2 /*return*/, {
                                            headers: this.parseHeaders(xhr.responseHeaders),
                                            value: response
                                        }];
                                }
                                return [2 /*return*/, response];
                            }
                            if (xhr.status >= 400) {
                                throw new Error(xhr.responseText || "Server Error");
                            }
                            if (methodOptions && methodOptions.returnHeaders) {
                                return [2 /*return*/, {
                                        headers: this.parseHeaders(xhr.responseHeaders),
                                        value: xhr.responseText
                                    }];
                            }
                            return [2 /*return*/, xhr.responseText];
                        case 3:
                            if (busyIndicator) {
                                busyIndicator.dispose();
                            }
                            return [7 /*endfinally*/];
                        case 4: return [2 /*return*/];
                    }
                });
            });
        };
        BaseService.prototype.parseHeaders = function (headers) {
            if (typeof headers === "object") {
                return headers;
            }
            return (headers || "")
                .split("\n")
                .reduce(function (pv, c) {
                var cv = c.split(":");
                pv[cv[0]] = (cv[1] || "").trim();
                return pv;
            }, {});
        };
        BaseService.prototype.ajax = function (url, options) {
            return __awaiter(this, void 0, void 0, function () {
                var xhr;
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            // return new CancellablePromise();
                            url = url || options.url;
                            // no longer needed, watch must provide functionality of waiting and cancelling
                            // await Atom.delay(1, options.cancel);
                            if (options.cancel && options.cancel.cancelled) {
                                throw new Error("cancelled");
                            }
                            if (!AtomBridge_1.AtomBridge.instance.ajax) return [3 /*break*/, 2];
                            return [4 /*yield*/, new Promise(function (resolve, reject) {
                                    AtomBridge_1.AtomBridge.instance.ajax(url, options, function (r) {
                                        resolve(options);
                                    }, function (e) {
                                        reject(e);
                                    }, null);
                                })];
                        case 1: return [2 /*return*/, _a.sent()];
                        case 2:
                            xhr = new XMLHttpRequest();
                            return [4 /*yield*/, new Promise(function (resolve, reject) {
                                    if (options.cancel && options.cancel.cancelled) {
                                        reject(options.cancel.cancelled);
                                        return;
                                    }
                                    if (options.cancel) {
                                        options.cancel.registerForCancel(function (r) {
                                            xhr.abort();
                                            reject(r);
                                            return;
                                        });
                                    }
                                    xhr.onreadystatechange = function (e) {
                                        if (xhr.readyState === XMLHttpRequest.DONE) {
                                            options.status = xhr.status;
                                            options.responseText = xhr.responseText;
                                            // options.responseHeaders = (xhr.getAllResponseHeaders())
                                            //     .split("\n")
                                            //     .map((s) => s.trim().split(":"))
                                            //     .reduce((pv, cv) => pv[cv[0]] = cv[1], {});
                                            options.responseHeaders = xhr.getAllResponseHeaders();
                                            var ct = xhr.getResponseHeader("content-type");
                                            options.responseType = ct || xhr.responseType;
                                            resolve(options);
                                        }
                                    };
                                    xhr.open(options.method, url, true);
                                    if (options.dataType) {
                                        xhr.setRequestHeader("accept", options.dataType);
                                    }
                                    if (options.contentType) {
                                        xhr.setRequestHeader("content-type", options.contentType);
                                    }
                                    var h = options.headers;
                                    if (h) {
                                        for (var key in h) {
                                            if (h.hasOwnProperty(key)) {
                                                var element = h[key];
                                                xhr.setRequestHeader(key, element.toString());
                                            }
                                        }
                                    }
                                    try {
                                        xhr.send(options.data);
                                    }
                                    catch (e) {
                                        options.status = xhr.status;
                                        options.responseText = xhr.responseText;
                                        // options.responseHeaders = (xhr.getAllResponseHeaders())
                                        //     .split("\n")
                                        //     .map((s) => s.trim().split(":"))
                                        //     .reduce((pv, cv) => pv[cv[0]] = cv[1], {});
                                        options.responseHeaders = xhr.getAllResponseHeaders();
                                        var ct = xhr.getResponseHeader("content-type");
                                        options.responseType = ct || xhr.responseType;
                                        resolve(options);
                                    }
                                })];
                        case 3: return [2 /*return*/, _a.sent()];
                    }
                });
            });
        };
        BaseService.baseUrls = {};
        BaseService = __decorate([
            __param(0, Inject_1.Inject),
            __param(1, Inject_1.Inject),
            __metadata("design:paramtypes", [App_1.App,
                JsonService_1.JsonService])
        ], BaseService);
        return BaseService;
    }());
    exports.BaseService = BaseService;
});
//# sourceMappingURL=RestService.js.map