(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@angular/common'), require('@angular/common/http'), require('@wm/core'), require('rxjs'), require('@wavemaker/variables'), require('lodash-es')) :
    typeof define === 'function' && define.amd ? define(['exports', '@angular/core', '@angular/common', '@angular/common/http', '@wm/core', 'rxjs', '@wavemaker/variables', 'lodash-es'], factory) :
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.wm = global.wm || {}, global.wm.http = {}), global.ng.core, global.ng.common, global.ng.common.http, global.wm.core, global.rxjs, global.wm_common_variables, global._));
})(this, (function (exports, i0, common, i1, core, rxjs, variables, lodashEs) { 'use strict';

    function _interopNamespaceDefault(e) {
        var n = Object.create(null);
        if (e) {
            Object.keys(e).forEach(function (k) {
                if (k !== 'default') {
                    var d = Object.getOwnPropertyDescriptor(e, k);
                    Object.defineProperty(n, k, d.get ? d : {
                        enumerable: true,
                        get: function () { return e[k]; }
                    });
                }
            });
        }
        n.default = e;
        return Object.freeze(n);
    }

    var i0__namespace = /*#__PURE__*/_interopNamespaceDefault(i0);
    var i1__namespace = /*#__PURE__*/_interopNamespaceDefault(i1);

    var HTTP_EVENT_TYPE;
    (function (HTTP_EVENT_TYPE) {
        HTTP_EVENT_TYPE[HTTP_EVENT_TYPE["Sent"] = 0] = "Sent";
        HTTP_EVENT_TYPE[HTTP_EVENT_TYPE["UploadProgress"] = 1] = "UploadProgress";
        HTTP_EVENT_TYPE[HTTP_EVENT_TYPE["ResponseHeader"] = 2] = "ResponseHeader";
        HTTP_EVENT_TYPE[HTTP_EVENT_TYPE["DownloadProgress"] = 3] = "DownloadProgress";
        HTTP_EVENT_TYPE[HTTP_EVENT_TYPE["Response"] = 4] = "Response";
        HTTP_EVENT_TYPE[HTTP_EVENT_TYPE["User"] = 5] = "User";
    })(HTTP_EVENT_TYPE || (HTTP_EVENT_TYPE = {}));
    class HttpServiceImpl extends core.AbstractHttpService {
        constructor(httpClient) {
            super();
            this.httpClient = httpClient;
            this.nonBodyTypeMethods = ['GET', 'DELETE', 'HEAD', 'OPTIONS', 'JSONP'];
            this.sessionTimeoutObservable = new rxjs.Subject();
            this.sessionTimeoutQueue = [];
        }
        /**
         * This method handles session timeout.
         * @param options
         * @param resolve
         * @param reject
         */
        handleSessionTimeout(options, subscriber401) {
            this.sessionTimeoutQueue.push({
                requestInfo: options,
                sub401: subscriber401
            });
            this.on401();
        }
        /**
         * Generates a request with provided options
         * @param options, request params/options
         */
        generateRequest(options) {
            let reqHeaders = new i1.HttpHeaders(), reqParams = new i1.HttpParams();
            const headers = options.headers;
            const params = options.params;
            const responseType = options.responseType;
            const withCredentials = options.withCredentials;
            // this header is not to be sent with non-proxy calls from service variable
            if (!options.isDirectCall) {
                reqHeaders = reqHeaders.append('X-Requested-With', 'XMLHttpRequest');
            }
            // headers
            if (headers) {
                Object.entries(headers).forEach(([k, v]) => {
                    if (lodashEs.isNumber(v) || lodashEs.isBoolean(v)) {
                        v = v.toString();
                    }
                    reqHeaders = reqHeaders.append(k, v);
                });
            }
            // params
            if (params) {
                Object.entries(params).forEach(([k, v]) => reqParams = reqParams.append(k, v));
            }
            let third, fourth;
            const reqOptions = {
                headers: reqHeaders,
                params: reqParams,
                responseType: responseType,
                withCredentials: withCredentials
            };
            // Even if method is not expecting request body, if body is passed in request, pass it on.
            if (lodashEs.includes(this.nonBodyTypeMethods, options.method && options.method.toUpperCase()) && options.data === undefined) {
                third = reqOptions;
                fourth = null;
            }
            else {
                third = options.data;
                fourth = reqOptions;
            }
            return new i1.HttpRequest(options.method, options.url, third, fourth);
        }
        /**
         * Make a http call and returns an observable that can be cancelled
         * @param options, options using which the call needs to be made
         */
        sendCallAsObservable(options, params) {
            const req = this.generateRequest(options);
            return this.httpClient.request(req);
        }
        sendCall(requestParams, variable, params) {
            return new Promise((resolve, reject) => {
                variable._observable = this.sendCallAsObservable(requestParams, params).subscribe((response) => {
                    if (response && response.type) {
                        resolve(response);
                    }
                }, (err) => {
                    reject(err);
                });
            });
        }
        /**
         * Makes a http call and return a promise
         * @param options, options using which the call needs to be made
         */
        send(options) {
            const req = this.generateRequest(options);
            return new Promise((resolve, reject) => {
                this.httpClient.request(req).toPromise().then((response) => {
                    resolve(response);
                }, (err) => {
                    if (this.isPlatformSessionTimeout(err)) {
                        err._401Subscriber.asObservable().subscribe((response) => {
                            resolve(response);
                        }, (e) => {
                            reject(e);
                        });
                    }
                    else {
                        const errMsg = variables.getErrMessage(err, this.getLocale());
                        reject({
                            error: errMsg,
                            details: err
                        });
                    }
                });
            });
        }
        cancel(variable, $file) {
            if (variable._observable) {
                variable._observable.unsubscribe();
            }
            if ($file && $file._uploadProgress) {
                $file._uploadProgress.unsubscribe();
            }
        }
        setLocale(locale) {
            this.localeObject = locale;
        }
        getLocale() {
            return this.localeObject;
        }
        getHeader(error, headerKey) {
            return error.headers.get(headerKey);
        }
        isPlatformSessionTimeout(error) {
            const MSG_SESSION_NOT_FOUND = 'Session Not Found';
            return error.status === 401 && this.getHeader(error, 'x-wm-login-errormessage') === MSG_SESSION_NOT_FOUND;
        }
        get(url, options) {
            options = options || {};
            options.url = url;
            options.method = 'get';
            return this.send(options).then((response) => response.body);
        }
        post(url, data, options) {
            options = options || {};
            options.url = url;
            options.method = 'post';
            options.data = data;
            return this.send(options);
        }
        put(url, data, options) {
            options = options || {};
            options.url = url;
            options.method = 'put';
            options.data = data;
            return this.send(options);
        }
        patch(url, data, options) {
            options = options || {};
            options.url = url;
            options.method = 'patch';
            options.data = data;
            return this.send(options);
        }
        delete(url, options) {
            options = options || {};
            options.url = url;
            options.method = 'delete';
            return this.send(options);
        }
        head(url, options) {
            options = options || {};
            options.url = url;
            options.method = 'head';
            return this.send(options);
        }
        jsonp(url, options) {
            options = options || {};
            options.url = url;
            options.method = 'jsonp';
            return this.send(options);
        }
        upload(url, data, options) {
            const req = new i1.HttpRequest('POST', url, data, {
                reportProgress: true // for progress data
            });
            return this.httpClient.request(req);
            // return this.httpClient.post(url, data, {
            //     reportProgress: true
            // });
        }
        uploadFile(url, data, variable, options) {
            return new Promise((resolve, reject) => {
                variable.request = this.upload(url, data, options).subscribe((event) => {
                    if (event.type === HTTP_EVENT_TYPE.UploadProgress) {
                        const uploadProgress = Math.round(100 * event.loaded / event.total);
                        options.notify(uploadProgress);
                    }
                    if (event.type === HTTP_EVENT_TYPE.Response) {
                        resolve(event.body);
                    }
                }, error => {
                    reject(error);
                });
            });
        }
        /**
         * registers a callback to be trigerred on session timeout
         * @param callback
         */
        registerOnSessionTimeout(callback) {
            this.sessionTimeoutObservable.asObservable().subscribe(callback);
        }
        /**
         * trigger the registered methods on session timeout
         */
        on401() {
            this.sessionTimeoutObservable.next(undefined);
        }
        pushToSessionFailureQueue(callback) {
            this.sessionTimeoutQueue.push({
                callback: callback
            });
        }
        /**
         * Execute queued requests, failed due to session timeout
         */
        executeSessionFailureRequests() {
            const queue = this.sessionTimeoutQueue;
            const that = this;
            that.sessionTimeoutQueue = [];
            queue.forEach(data => {
                if (lodashEs.isFunction(data.callback)) {
                    data.callback();
                }
                else {
                    data.requestInfo.headers.headers.delete('x-wm-xsrf-token');
                    that.httpClient.request(data.requestInfo)
                        .subscribe(response => {
                        if (response && response.type && data.sub401) {
                            data.sub401.next(response);
                        }
                    }, (reason) => {
                        data.sub401.error(reason);
                    });
                }
            });
        }
        static { this.ɵfac = function HttpServiceImpl_Factory(t) { return new (t || HttpServiceImpl)(i0__namespace.ɵɵinject(i1__namespace.HttpClient)); }; }
        static { this.ɵprov = /*@__PURE__*/ i0__namespace.ɵɵdefineInjectable({ token: HttpServiceImpl, factory: HttpServiceImpl.ɵfac }); }
    }
    (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0__namespace.ɵsetClassMetadata(HttpServiceImpl, [{
            type: i0.Injectable
        }], () => [{ type: i1__namespace.HttpClient }], null); })();

    /**
     * This class creates a custom WmHttpRequest.
     */
    class WmHttpRequest {
        constructor() {
            this.headers = [];
        }
        /**
         * This method converts the given wm HttpRequest to angular HttpRequest
         * @param reqObj, the wm HttpRequest
         */
        wmToAngularRequest(reqObj) {
            let angReq;
            angReq = new i1.HttpRequest(reqObj.method, reqObj.url);
            angReq = angReq.clone();
            angReq.body = reqObj.body;
            angReq.method = reqObj.method;
            angReq.responseType = reqObj.responseType;
            angReq.reportProgress = reqObj.reportProgress;
            angReq.urlWithParams = reqObj.url;
            angReq.withCredentials = reqObj.withCredentials;
            reqObj.headers.forEach((value, header) => {
                angReq.headers = angReq.headers.append(header, value);
            });
            return angReq;
        }
        /**
         * This method converts the given angular HttpRequest to wm HttpRequest
         * @param req, the angular HttpRequest
         */
        angularToWmRequest(req) {
            let wmReq = new WmHttpRequest();
            let headerMap = new Map();
            wmReq.body = req.body;
            wmReq.method = req.method;
            wmReq.responseType = req.responseType;
            wmReq.reportProgress = req.reportProgress;
            wmReq.url = req.urlWithParams;
            wmReq.withCredentials = req.withCredentials;
            req.headers.keys().forEach((header) => {
                headerMap.set(header, req.headers.headers.get(header.toLowerCase())[0]);
            });
            wmReq.headers = headerMap;
            return wmReq;
        }
    }

    /**
     * This class creates a custom WmHttpRequest.
     */
    class WmHttpResponse extends WmHttpRequest {
        constructor() {
            super();
        }
        /**
         * This method converts the given wm HttpResponse to angular HttpResponse
         * @param respObj, the wm HttpResponse
         */
        wmToAngularResponse(respObj) {
            let angResp;
            angResp = new i1.HttpResponse();
            angResp = angResp.clone();
            angResp.body = respObj.body;
            angResp.error = respObj.error;
            angResp.message = respObj.message;
            angResp.url = respObj.url;
            angResp.ok = respObj.ok;
            angResp.status = respObj.status;
            angResp.statusText = respObj.statusText;
            respObj.headers.forEach((value, header) => {
                angResp.headers = angResp.headers.append(header, value);
            });
            return angResp;
        }
        /**
         * This method converts the given wm HttpResponse to angular HttpResponse
         * @param respObj, the angular HttpResponse
         */
        angularToWmResponse(respObj) {
            let wmResp;
            let headerMap = new Map();
            wmResp = new WmHttpResponse();
            wmResp.body = respObj.body;
            wmResp.error = respObj.error;
            wmResp.message = respObj.message;
            wmResp.url = respObj.url;
            wmResp.ok = respObj.ok;
            wmResp.status = respObj.status;
            wmResp.statusText = respObj.statusText;
            respObj.headers.keys().forEach((header) => {
                headerMap.set(header, respObj.headers.headers.get(header.toLowerCase())[0]);
            });
            wmResp.headers = headerMap;
            return wmResp;
        }
    }

    class HttpServiceModule {
        static forRoot() {
            return {
                ngModule: common.CommonModule,
                providers: [
                    { provide: core.AbstractHttpService, useClass: HttpServiceImpl }
                ]
            };
        }
        static { this.ɵfac = function HttpServiceModule_Factory(t) { return new (t || HttpServiceModule)(); }; }
        static { this.ɵmod = /*@__PURE__*/ i0__namespace.ɵɵdefineNgModule({ type: HttpServiceModule }); }
        static { this.ɵinj = /*@__PURE__*/ i0__namespace.ɵɵdefineInjector({ imports: [common.CommonModule,
                i1.HttpClientModule] }); }
    }
    (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0__namespace.ɵsetClassMetadata(HttpServiceModule, [{
            type: i0.NgModule,
            args: [{
                    imports: [
                        common.CommonModule,
                        i1.HttpClientModule
                    ],
                    declarations: []
                }]
        }], null, null); })();
    (function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0__namespace.ɵɵsetNgModuleScope(HttpServiceModule, { imports: [common.CommonModule,
            i1.HttpClientModule] }); })();

    exports.HttpServiceImpl = HttpServiceImpl;
    exports.HttpServiceModule = HttpServiceModule;
    exports.WmHttpRequest = WmHttpRequest;
    exports.WmHttpResponse = WmHttpResponse;

}));
