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

rwthapp.connection.requestCache = (function () {
    var cache = [];
    var cacheSize = 0;
    var maxCacheSize = 1024 * 1024; // Around 1 MB
    var maxExpirationTime = 15 * 60; // Set maximal cache time to 15 minutes

    var add = function (url, data, expirationTime) {
        // Do not store entry if caching is disabled
        if (rwthapp.options.isInappcachingEnabled() === false) {
            return;
        }
        // Do not cache if expirationTime set to zero or data not set
        if (expirationTime === 0 || data === null) {
            return;
        } else if (!expirationTime) {
            expirationTime = 60; // Default to 1 minute
        }

        if (expirationTime > maxExpirationTime) {
            expirationTime = maxExpirationTime;
        }

        var normalizedUrl = normalizeUrl(url);
        var entrySize = JSON.stringify(data).length;

        if (entrySize <= maxCacheSize) {
            // Save timeout id to remove entry after expiration
            var timeout = setTimeout(remove, expirationTime * 1000 + 10, url);

            // Create a copy if data is an object
            data = copyIfNecessary(data);

            var cacheEntry = {
                url: normalizedUrl,
                data: data,
                expirationTime: Date.now() + expirationTime * 1000,
                size: entrySize,
                timeoutId: timeout,
            };

            cacheSize += entrySize;

            rwthapp.logging.logger.debug('Added cache entry: "' + normalizedUrl + '" (' + expirationTime + 's, ' + entrySize + ' bytes)');

            cache.push(cacheEntry);

            // Check if cache too big
            if (cacheSize > maxCacheSize) {
                rwthapp.logging.logger.debug('Cleaning up cache...');
                cleanupCache();
            }
        }
    };

    var remove = function (url) {
        url = normalizeUrl(url);

        for (var i = cache.length - 1; i >= 0; i--) {
            if (cache[i].url === url) {
                // Remove entry
                removeAt(i);
            }
        }
    };

    var removeAt = function (index) {
        rwthapp.logging.logger.debug('Removed cache entry: "' + cache[index].url + '"');
        clearTimeout(cache[index].timeoutId); // Avoid removing entries twice
        cacheSize -= cache[index].size;
        cache.splice(index, 1);
    };

    var cleanupCache = function () {
        // TODO implement lru or something similar? Could be implemented by rearranging array in get method
        while (cacheSize > maxCacheSize && cache.length > 0) {
            removeAt(0);
        }
    };

    var clear = function () {
        for (var i = cache.length - 1; i >= 0; i--) {
            removeAt(i);
        }
    };

    var get = function (url) {
        // Do not retrieve entry if caching is disabled
        if (rwthapp.options.isInappcachingEnabled() === false) {
            return null;
        }
        url = normalizeUrl(url);

        for (var i = 0; i < cache.length; i++) {
            if (cache[i].url === url && Date.now() < cache[i].expirationTime) {
                rwthapp.logging.logger.debug('Cache hit: "' + url + '"');

                // Return a copy if data is an object
                return copyIfNecessary(cache[i].data);
            }
        }

        // Entry not found in cache
        rwthapp.logging.logger.debug('Cache miss: "' + url + '"');
        return null;
    };

    var normalizeUrl = function (url) {
        url = removeParameter(url, 'token');
        url = removeParameter(url, '_');
        url = removeParameter(url, 'clearCache'); // Used by UB requests

        // TODO sort parameters

        return url;
    };

    var removeParameter = function (url, parameter) {
        var urlNew = url;

        // Only attempt to remove parameter if parameters in url
        if (url.indexOf('?') >= 0) {
            var parameters = extractParameters(url);
            for (var i = parameters.length - 1; i >= 0; i--) {
                if (parameters[i].key === parameter) {
                    parameters.splice(i, 1); // remove parameter
                }
            }

            // Check if there are parameters left
            if (parameters.length > 0) {
                urlNew = url.substr(0, url.indexOf('?') + 1) + joinParameters(parameters);
            } else {
                // No parameters left
                urlNew = url.substr(0, url.indexOf('?')); // No + 1 to remove '?'
            }
        }

        return urlNew;
    };

    var extractParameters = function (url) {
        var result = [];

        if (url.indexOf('?') >= 0) {
            var queryString = url.substr(url.indexOf('?') + 1);
            var parameters = queryString.split('&');

            for (var i = 0; i < parameters.length; i++) {
                var split = parameters[i].split('=');

                if (split.length === 2) {
                    var key = decodeURIComponent(split[0]);
                    var value = decodeURIComponent(split[1]);

                    result.push({
                        key: key,
                        value: value
                    });
                }
            }
        }

        return result;
    };

    var joinParameters = function (parameters) {
        var parameterStrings = [];
        for (var i = 0; i < parameters.length; i++) {
            parameterStrings.push(encodeURIComponent(parameters[i].key) + '=' + encodeURIComponent(parameters[i].value));
        }

        return parameterStrings.join('&');
    };

    var copyIfNecessary = function (data) {
        if (data === null) {
            return null;
        } else if (Array.isArray(data)) {
            return $.extend(true, [], data);
        } else if (typeof data === "object") {
            return $.extend(true, {}, data);
        } else {
            return data;
        }
    };

    return {
        add: add,
        get: get,
        remove: remove,
        clear: clear
    };
})();