import { __assign, __values, __decorate } from 'tslib';
import { ɵɵdefineInjectable, Injectable } from '@angular/core';

var PolyfillStorage = /** @class */ (function () {
    function PolyfillStorage(_storage) {
        var _this = this;
        this._storage = _storage;
        // State.
        this._isSupported = false;
        this.encode = function (value, attributes) {
            // Make sure the expires attribute is a date and not a number.
            var data = { value: value };
            if (typeof attributes.expires === 'number') {
                attributes.expires = new Date(attributes.expires);
            }
            if (attributes.expires) {
                data['expires'] = attributes.expires;
            }
            return JSON.stringify(data);
        };
        this.testIfSupported = function () {
            if (typeof _this._storage === 'undefined') {
                return false;
            }
            try {
                var x = '__storage_test__';
                _this._storage.setItem(x, x);
                _this._storage.removeItem(x);
                return true;
            }
            catch (error) {
                return error instanceof DOMException
                    && (error.code === 22 ||
                        error.code === 1014 ||
                        error.name === 'QuotaExceededError' ||
                        error.name === 'NS_ERROR_DOM_QUOTA_REACHED')
                    && (_this._storage && _this._storage.length !== 0);
            }
        };
        // Check if the storage is supported.
        this._isSupported = this.testIfSupported();
    }
    PolyfillStorage.prototype.get = function (token) {
        // Validate that the storage is supported.
        if (!this._isSupported) {
            return undefined;
        }
        return this.decode(token);
    };
    PolyfillStorage.prototype.set = function (token, value, attributes) {
        // Validate that the storage is supported.
        if (!this._isSupported) {
            return;
        }
        // Stringify the data.
        var rawData = this.encode(value, __assign({}, attributes));
        // Set the raw data in the storage.
        this._storage.setItem(token, rawData);
    };
    PolyfillStorage.prototype.remove = function (token, attributes) {
        // Validate that the storage is supported.
        if (!this._isSupported) {
            return;
        }
        // Remove the item in the storage.
        this._storage.removeItem(token);
    };
    PolyfillStorage.prototype.clear = function (attributes) {
        // Validate that the storage is supported.
        if (!this._isSupported) {
            return;
        }
        // Clear the storage.
        this._storage.clear();
    };
    PolyfillStorage.prototype.isSupported = function () {
        return this._isSupported;
    };
    PolyfillStorage.prototype.decode = function (token) {
        // Retrieve the raw data from the storage.
        var rawData = this._storage.getItem(token);
        // Try to parse the data.
        var parsedData = undefined;
        // Only parse the data when it is not null.
        if (rawData !== null) {
            try {
                var _a = JSON.parse(rawData), value = _a.value, expires = _a.expires;
                if (expires !== undefined) {
                    var now = new Date().getTime();
                    var expireDate = new Date(expires).getTime();
                    if (expireDate <= now) {
                        this.remove(token);
                        return undefined;
                    }
                }
                parsedData = value;
            }
            catch (_b) {
                // We dont do anything with failed parsing.
            }
        }
        // Return the parsed data.
        return parsedData;
    };
    return PolyfillStorage;
}());

var CookieStorage = /** @class */ (function () {
    function CookieStorage() {
        var _this = this;
        // State.
        this._isSupported = false;
        this.decode = function (cookieString) {
            var result = {};
            var cookies = cookieString ? cookieString.split('; ') : [];
            var rdecode = /(%[0-9A-Z]{2})+/g;
            for (var index = 0; index < cookies.length; index++) {
                var parts = cookies[index].split('=');
                var cookie = parts.slice(1).join('=');
                if (cookie.charAt(0) === '"') {
                    cookie = cookie.slice(1, -1);
                }
                try {
                    var name_1 = parts[0].replace(rdecode, decodeURIComponent);
                    var value = cookie.replace(rdecode, decodeURIComponent);
                    result[name_1] = _this.jsonParse(value);
                }
                catch (_a) {
                    // Ignore cookies with an invalid name/value encoding.
                }
            }
            return result;
        };
        this.jsonParse = function (rawValue) {
            // Try to parse the data.
            var parsedValue = undefined;
            // Only parse the value when it is not null.
            if (rawValue !== undefined) {
                try {
                    parsedValue = JSON.parse(rawValue);
                }
                catch (_a) {
                    // The string could not be parsed to JSON.
                }
            }
            // Return the parsed value.
            return parsedValue;
        };
        this.stringifyAttribute = function (token, value) {
            if (!value) {
                return '';
            }
            var stringified = "; " + token;
            // Boolean attributes should not have a value.
            if (value === true) {
                return stringified;
            }
            return stringified + ("= " + value);
        };
        this.testIfSupported = function () {
            return (navigator && navigator.cookieEnabled) || false;
        };
        // Check if the storage is supported.
        this._isSupported = this.testIfSupported();
    }
    CookieStorage.prototype.get = function (token) {
        // Validate that the storage is supported.
        if (!this._isSupported) {
            return undefined;
        }
        // Get all values and pick one out.
        var values = this.all();
        return values[token];
    };
    CookieStorage.prototype.set = function (token, value, attributes) {
        // Validate that the storage is supported.
        if (!this._isSupported) {
            return;
        }
        // Write a new cookie.
        document.cookie = this.encode(token, value, __assign({ path: '/' }, attributes));
    };
    CookieStorage.prototype.remove = function (token, attributes) {
        // Validate that the storage is supported.
        if (!this._isSupported) {
            return;
        }
        this.set(token, '', __assign(__assign({}, attributes), { expires: -1 }));
    };
    CookieStorage.prototype.clear = function (attributes) {
        var _this = this;
        // Validate that the storage is supported.
        if (!this._isSupported) {
            return;
        }
        var items = this.all();
        Object.keys(items)
            .forEach(function (token) { return _this.remove(token, attributes); });
    };
    CookieStorage.prototype.isSupported = function () {
        return this._isSupported;
    };
    CookieStorage.prototype.all = function () {
        return this.decode(document.cookie);
    };
    CookieStorage.prototype.encode = function (token, value, attributes) {
        // Encode the token.
        var encodedToken = encodeURIComponent(token)
            .replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent)
            .replace(/\(/g, '%28').replace(/\)/g, '%29');
        var rawValue = JSON.stringify(value);
        // Encode the value.
        var encodedValue = encodeURIComponent(rawValue)
            .replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
        // Return the completed encoded string.
        return [encodedToken, '=', encodedValue, this.stringifyCookie(attributes)].join('');
    };
    CookieStorage.prototype.stringifyCookie = function (attributes) {
        // Make sure the expires attribute is a date and not a number.
        if (typeof attributes.expires === 'number') {
            var expires = new Date();
            expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5);
            attributes.expires = expires;
        }
        // Stringify all attributes.
        return this.stringifyAttribute('Expires', attributes.expires ? attributes.expires.toUTCString() : '')
            + this.stringifyAttribute('Domain', attributes.domain)
            + this.stringifyAttribute('Path', attributes.path)
            + this.stringifyAttribute('Secure', attributes.secure)
            + this.stringifyAttribute('SameSite', attributes.sameSite);
    };
    return CookieStorage;
}());

var MemoryStorage = /** @class */ (function () {
    function MemoryStorage() {
        // State.
        this._storage = {};
        this.encode = function (value, attributes) {
            var data = { value: value };
            // Make sure the expires is a date object.
            if (typeof attributes.expires === 'number') {
                attributes.expires = new Date(attributes.expires);
            }
            // Add the expire date if it was set.
            if (attributes.expires) {
                data['expires'] = attributes.expires;
            }
            // Return the encoded data.
            return data;
        };
    }
    MemoryStorage.prototype.get = function (token) {
        // Grab the data from the memory storage.
        var result = this._storage[token];
        // Validate the data exists.
        if (result === undefined) {
            return undefined;
        }
        // Extract the value and expire date.
        var value = result.value, expires = result.expires;
        // Validate the value is still valid.
        if (expires !== undefined) {
            var now = new Date().getTime();
            var expireDate = new Date(expires).getTime();
            // Remove the value if it is expired.
            if (expireDate <= now) {
                this.remove(token);
                return undefined;
            }
        }
        // Return the value.
        return value;
    };
    MemoryStorage.prototype.set = function (token, value, attributes) {
        // Save the encoded value with the attributes.
        this._storage[token] = this.encode(value, __assign({}, attributes));
    };
    MemoryStorage.prototype.remove = function (token, attributes) {
        delete this._storage[token];
    };
    MemoryStorage.prototype.clear = function (attributes) {
        this._storage = {};
    };
    MemoryStorage.prototype.isSupported = function () {
        // The memory storage is always supported.
        return true;
    };
    return MemoryStorage;
}());

var Storage = /** @class */ (function () {
    function Storage() {
        // Current selected storage strategy.
        this._current = null;
        this.getLocalStorage = function () {
            return (typeof window !== 'undefined')
                ? window === null || window === void 0 ? void 0 : window.localStorage : null;
        };
        this.getSessionStorage = function () {
            return (typeof window !== 'undefined')
                ? window === null || window === void 0 ? void 0 : window.sessionStorage : null;
        };
        this._strategies = {
            local: new PolyfillStorage(this.getLocalStorage()),
            cookie: new CookieStorage(),
            session: new PolyfillStorage(this.getSessionStorage()),
            memory: new MemoryStorage()
        };
        this._current = this.findBestStorageStrategy();
    }
    Object.defineProperty(Storage.prototype, "local", {
        get: function () {
            return this._strategies.local;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Storage.prototype, "cookie", {
        get: function () {
            return this._strategies.cookie;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Storage.prototype, "session", {
        get: function () {
            return this._strategies.session;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Storage.prototype, "memory", {
        get: function () {
            return this._strategies.memory;
        },
        enumerable: true,
        configurable: true
    });
    Storage.prototype.get = function (token) {
        return this._current.get(token);
    };
    Storage.prototype.set = function (token, value, attributes) {
        this._current.set(token, value, attributes);
    };
    Storage.prototype.remove = function (token, attributes) {
        this._current.remove(token, attributes);
    };
    Storage.prototype.clear = function (attributes) {
        this._current.clear(attributes);
    };
    Storage.prototype.isSupported = function () {
        return !!this._current;
    };
    Storage.prototype.findBestStorageStrategy = function () {
        var e_1, _a;
        var types = Object.keys(this._strategies);
        try {
            // Loop through all available strategies to find the best.
            for (var types_1 = __values(types), types_1_1 = types_1.next(); !types_1_1.done; types_1_1 = types_1.next()) {
                var type = types_1_1.value;
                var strategy = this._strategies[type];
                // The first supported strategy is the best.
                if (strategy.isSupported()) {
                    return strategy;
                }
            }
        }
        catch (e_1_1) { e_1 = { error: e_1_1 }; }
        finally {
            try {
                if (types_1_1 && !types_1_1.done && (_a = types_1.return)) _a.call(types_1);
            }
            finally { if (e_1) throw e_1.error; }
        }
        // If no strategy was returned we emit an error.
        throw new Error('No storage strategy was available.');
    };
    Storage.ɵprov = ɵɵdefineInjectable({ factory: function Storage_Factory() { return new Storage(); }, token: Storage, providedIn: "root" });
    Storage = __decorate([
        Injectable({
            providedIn: 'root'
        })
    ], Storage);
    return Storage;
}());

/**
 * Generated bundle index. Do not edit.
 */

export { CookieStorage, MemoryStorage, PolyfillStorage, Storage };
//# sourceMappingURL=bravobit-bb-foundation-storage.js.map
