var rwthapp = rwthapp || {};
rwthapp.connection = rwthapp.connection || {};

rwthapp.connection.requestHelper = (function () {
    var normalTimeout = 30000;
    var shortTimeout = 5000;

    var defaultSettingsGeneral = {
        timeout: normalTimeout
    };

    var defaultSettingsProxy = {
        queryParameters: {},
        appendToken: true,
        cacheLocal: true,
        timeout: normalTimeout
    };

    var ajax = function (settings) {
        return new Promise(function (resolve, reject) {
            settings = $.extend(true, {}, defaultSettingsGeneral, settings);

            var success = function (data, textStatus, jqXHR) {
                resolve(data);
            };

            var error = function (jqXHR, textStatus, errorThrown) {
                reject(rwthapp.error.createErrorFromXHR(jqXHR, textStatus));
            };

            addJQueryHandler(settings, 'success', success);
            addJQueryHandler(settings, 'error', error);

            $.ajax(settings);
        });
    };

    var addJQueryHandler = function (jqSettingsObject, jqHandlerName, handlerFunction) {
        if (typeof jqSettingsObject[jqHandlerName] === "function") {
            // Handler already bound
            jqSettingsObject[jqHandlerName] = new Array(jqSettingsObject[jqHandlerName]);
            jqSettingsObject[jqHandlerName].push(handlerFunction);
        } else if (jqSettingsObject[jqHandlerName] instanceof Array) {
            // Handler already bound to multiple callbacks
            jqSettingsObject[jqHandlerName].push(handlerFunction);
        } else {
            // Handler not bound yet
            jqSettingsObject[jqHandlerName] = handlerFunction;
        }
    };

    var ajaxProxyApi = function (method, settings) {
        /// <summary>Wrapper function to send ajax calls to the proxy api.</summary>
        /// <param name="method" type="string">Method name of webservice method.</param>
        /// <param name="settings" type="object">jquery.ajax settings object + appendToken (true/false), queryParameters (object) and cacheLocal (true/false)</param>
        /// <returns type="Promise">Promise with Data attribute of response on success, error object on failure.</returns>

        return new Promise(function (resolve, reject) {
            var url; // Will be generated later (after token has been fetched if necessary)

            var isPostRequest = settings && settings.type && settings.type.toLowerCase() === "post";

            // Set-up settings object
            settings = $.extend(true, {}, defaultSettingsProxy, settings);
            if (isPostRequest && !settings.contentType) {
                // Set contentType to binary if post request and not set by user to avoid problems with server-side checks
                settings.contentType = "text/plain";
            }
            if (!isPostRequest) {
                // Do not let $.ajax automatically build the url
                settings.processData = false;
            }

            // Query or clear cache based on request settings
            var tempUrl = createApiUrl(method, settings);
            if (settings.cacheLocal && !isPostRequest) {
                // Try to find result in cache
                var cachedData = rwthapp.connection.requestCache.get(tempUrl);
                if (cachedData !== null) {
                    resolve(cachedData);
                    return;
                }
            } else {
                // Avoid leaving outdated data in cache
                rwthapp.connection.requestCache.remove(tempUrl);
            }

            // Only perform actions if internet connection available
            if (!isInternetConnectionAvailable()) {
                reject(new rwthapp.error.Error("No internet connection.", 10, rwthapp.localization.getLocalizedString("errors.request.noConnection"), false));
                return;
            }

            var success = function (data, textStatus, jqXHR) {
                // Check for error from API
                if (data.IsError) {
                    reject(rwthapp.error.createErrorFromProxyApiResponse(data)); // TODO include proxy api method
                } else {
                    if (settings.cacheLocal && !isPostRequest) {
                        // Get expiration time if supplied by server
                        var parsedExpirationTime = parseInt(jqXHR.getResponseHeader('X-RWTHApp-Cache-Expiration'));
                        var expirationTime = isNaN(parsedExpirationTime) ? null : parsedExpirationTime;
                        // Add data to cache
                        rwthapp.connection.requestCache.add(url, data.Data, expirationTime);
                    }
                    // Unwrap and return result
                    resolve(data.Data);
                }
            };

            var error = function (jqXHR, textStatus, errorThrown) {
                reject(rwthapp.error.createErrorFromXHR(jqXHR, textStatus));
            };

            addJQueryHandler(settings, 'success', success);
            addJQueryHandler(settings, 'error', error);

            if (settings.appendToken) {
                rwthapp.tokenManager.getToken().then(function (token) {
                    // Token is always expected in url
                    if (settings.type && settings.type.toLowerCase() === "post") {
                        settings.queryParameters = settings.queryParameters || {};
                        settings.queryParameters.token = token;
                    } else {
                        settings.data = settings.data || {};
                        settings.data.token = token;
                    }

                    url = createApiUrl(method, settings);
                    if (!isPostRequest) {
                        settings.data = null; // Set data to null to avoid automatic appending by jquery (for get requests)
                    }
                    $.ajax(url, settings);
                }).catch(function (error) {
                    reject(rwthapp.error.createErrorFromOAuthError(error));
                });
                
            } else {
                url = createApiUrl(method, settings);
                if (!isPostRequest) {
                    settings.data = null; // Set data to null to avoid automatic appending by jquery (for get requests)
                }
                $.ajax(url, settings);
            }


        });
    };

    var createQueryString = function (parameters) {
        var url = "";
        if (parameters) {
            for (var key in parameters) {
                if (parameters.hasOwnProperty(key) && parameters[key] !== undefined && parameters[key] !== null) {
                    url += "&" + encodeURIComponent(key) + "=" + encodeURIComponent(parameters[key]);
                } 
            }
            url = url.substr(1);
        }
        return url;
    };

    var createApiUrl = function (method, settings) {
        var url = rwthapp.options.getProxyUrl() + method;

        if (settings) {
            var separator = url.indexOf("?") >= 0 ? "&" : "?";

            if (settings.type && settings.type.toLowerCase() === "post" && settings.queryParameters) {
                url += separator;
                url += rwthapp.connection.requestHelper.createQueryString(settings.queryParameters);
            } else if (settings.data) {
                url += separator;
                url += rwthapp.connection.requestHelper.createQueryString(settings.data);
            }
        }

        return url;
    };

    var isInternetConnectionAvailable = function () {
        if (navigator.connection.type === null || navigator.connection.type === Connection.NONE) {
            return false;
        }
        return true;
    };

    var activateShortTimeouts = function () {
        defaultSettingsGeneral.timeout = shortTimeout;
        defaultSettingsProxy.timeout = shortTimeout;
    };

    var deactivateShortTimeouts = function () {
        defaultSettingsGeneral.timeout = normalTimeout;
        defaultSettingsProxy.timeout = normalTimeout;
    };

    return {
        ajax: ajax,
        ajaxProxyApi: ajaxProxyApi,
        createQueryString: createQueryString,
        isInternetConnectionAvailable: isInternetConnectionAvailable,
        activateShortTimeouts: activateShortTimeouts,
        deactivateShortTimeouts: deactivateShortTimeouts,
    };
})();