import { Injectable, ɵɵdefineInjectable } from '@angular/core';

/**
 * @fileoverview added by tsickle
 * Generated from: lib/interfaces/attributes.interface.ts
 * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
 */
/**
 * @record
 */
function StorageAttributes() { }
if (false) {
    /** @type {?|undefined} */
    StorageAttributes.prototype.expires;
}
/**
 * @record
 */
function CookieStorageAttributes() { }
if (false) {
    /** @type {?|undefined} */
    CookieStorageAttributes.prototype.domain;
    /** @type {?|undefined} */
    CookieStorageAttributes.prototype.path;
    /** @type {?|undefined} */
    CookieStorageAttributes.prototype.secure;
    /** @type {?|undefined} */
    CookieStorageAttributes.prototype.sameSite;
}
/**
 * @record
 */
function PolyfillStorageAttributes() { }
/**
 * @record
 */
function MemoryStorageAttributes() { }

/**
 * @fileoverview added by tsickle
 * Generated from: lib/interfaces/strategy.interface.ts
 * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
 */
/**
 * @record
 */
function StorageStrategy() { }
if (false) {
    /**
     * @template T
     * @param {?} token
     * @return {?}
     */
    StorageStrategy.prototype.get = function (token) { };
    /**
     * @template T
     * @param {?} token
     * @param {?} value
     * @param {?=} attributes
     * @return {?}
     */
    StorageStrategy.prototype.set = function (token, value, attributes) { };
    /**
     * @param {?} token
     * @param {?=} attributes
     * @return {?}
     */
    StorageStrategy.prototype.remove = function (token, attributes) { };
    /**
     * @param {?=} attributes
     * @return {?}
     */
    StorageStrategy.prototype.clear = function (attributes) { };
    /**
     * @return {?}
     */
    StorageStrategy.prototype.isSupported = function () { };
}

/**
 * @fileoverview added by tsickle
 * Generated from: lib/interfaces/memory.interface.ts
 * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
 */
/**
 * @record
 */
function MemoryStorageData() { }
/**
 * @record
 */
function MemoryStorageItem() { }
if (false) {
    /** @type {?} */
    MemoryStorageItem.prototype.value;
    /** @type {?|undefined} */
    MemoryStorageItem.prototype.expires;
}

/**
 * @fileoverview added by tsickle
 * Generated from: lib/strategies/polyfill-storage.strategy.ts
 * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
 */
class PolyfillStorage {
    /**
     * @param {?} _storage
     */
    constructor(_storage) {
        this._storage = _storage;
        // State.
        this._isSupported = false;
        this.encode = (/**
         * @template T
         * @param {?} value
         * @param {?} attributes
         * @return {?}
         */
        (value, attributes) => {
            // Make sure the expires attribute is a date and not a number.
            /** @type {?} */
            const data = { 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 = (/**
         * @return {?}
         */
        () => {
            if (typeof this._storage === 'undefined') {
                return false;
            }
            try {
                /** @type {?} */
                const 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);
            }
        });
        // Validate the storage was passed.
        if (!this._storage) {
            throw new Error('No polyfill storage was passed.');
        }
        // Check if the storage is supported.
        this._isSupported = this.testIfSupported();
    }
    /**
     * @template T
     * @param {?} token
     * @return {?}
     */
    get(token) {
        // Validate that the storage is supported.
        if (!this._isSupported) {
            return undefined;
        }
        return this.decode(token);
    }
    /**
     * @template T
     * @param {?} token
     * @param {?} value
     * @param {?=} attributes
     * @return {?}
     */
    set(token, value, attributes) {
        // Validate that the storage is supported.
        if (!this._isSupported) {
            return;
        }
        // Stringify the data.
        /** @type {?} */
        const rawData = this.encode(value, Object.assign({}, attributes));
        // Set the raw data in the storage.
        this._storage.setItem(token, rawData);
    }
    /**
     * @param {?} token
     * @param {?=} attributes
     * @return {?}
     */
    remove(token, attributes) {
        // Validate that the storage is supported.
        if (!this._isSupported) {
            return;
        }
        // Remove the item in the storage.
        this._storage.removeItem(token);
    }
    /**
     * @param {?=} attributes
     * @return {?}
     */
    clear(attributes) {
        // Validate that the storage is supported.
        if (!this._isSupported) {
            return;
        }
        // Clear the storage.
        this._storage.clear();
    }
    /**
     * @return {?}
     */
    isSupported() {
        return this._isSupported;
    }
    /**
     * @private
     * @template T
     * @param {?} token
     * @return {?}
     */
    decode(token) {
        // Retrieve the raw data from the storage.
        /** @type {?} */
        const rawData = this._storage.getItem(token);
        // Try to parse the data.
        /** @type {?} */
        let parsedData = undefined;
        // Only parse the data when it is not null.
        if (rawData !== null) {
            try {
                const { value, expires } = JSON.parse(rawData);
                if (expires !== undefined) {
                    /** @type {?} */
                    const now = new Date().getTime();
                    /** @type {?} */
                    const expireDate = new Date(expires).getTime();
                    if (expireDate <= now) {
                        this.remove(token);
                        return undefined;
                    }
                }
                parsedData = value;
            }
            catch (_a) {
                // We dont do anything with failed parsing.
            }
        }
        // Return the parsed data.
        return parsedData;
    }
}
if (false) {
    /**
     * @type {?}
     * @private
     */
    PolyfillStorage.prototype._isSupported;
    /**
     * @type {?}
     * @private
     */
    PolyfillStorage.prototype.encode;
    /**
     * @type {?}
     * @private
     */
    PolyfillStorage.prototype.testIfSupported;
    /**
     * @type {?}
     * @private
     */
    PolyfillStorage.prototype._storage;
}

/**
 * @fileoverview added by tsickle
 * Generated from: lib/strategies/cookie-storage.strategy.ts
 * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
 */
class CookieStorage {
    constructor() {
        // State.
        this._isSupported = false;
        this.decode = (/**
         * @param {?} cookieString
         * @return {?}
         */
        (cookieString) => {
            /** @type {?} */
            const result = {};
            /** @type {?} */
            const cookies = cookieString ? cookieString.split('; ') : [];
            /** @type {?} */
            const rdecode = /(%[0-9A-Z]{2})+/g;
            for (let index = 0; index < cookies.length; index++) {
                /** @type {?} */
                const parts = cookies[index].split('=');
                /** @type {?} */
                let cookie = parts.slice(1).join('=');
                if (cookie.charAt(0) === '"') {
                    cookie = cookie.slice(1, -1);
                }
                try {
                    /** @type {?} */
                    const name = parts[0].replace(rdecode, decodeURIComponent);
                    /** @type {?} */
                    const value = cookie.replace(rdecode, decodeURIComponent);
                    result[name] = this.jsonParse(value);
                }
                catch (_a) {
                    // Ignore cookies with an invalid name/value encoding.
                }
            }
            return result;
        });
        this.jsonParse = (/**
         * @template T
         * @param {?} rawValue
         * @return {?}
         */
        (rawValue) => {
            // Try to parse the data.
            /** @type {?} */
            let 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 = (/**
         * @param {?} token
         * @param {?} value
         * @return {?}
         */
        (token, value) => {
            if (!value) {
                return '';
            }
            /** @type {?} */
            const stringified = `; ${token}`;
            // Boolean attributes should not have a value.
            if (value === true) {
                return stringified;
            }
            return stringified + `= ${value}`;
        });
        this.testIfSupported = (/**
         * @return {?}
         */
        () => {
            return (navigator && navigator.cookieEnabled) || false;
        });
        // Check if the storage is supported.
        this._isSupported = this.testIfSupported();
    }
    /**
     * @template T
     * @param {?} token
     * @return {?}
     */
    get(token) {
        // Validate that the storage is supported.
        if (!this._isSupported) {
            return undefined;
        }
        // Get all values and pick one out.
        /** @type {?} */
        const values = this.all();
        return (/** @type {?} */ (values[token]));
    }
    /**
     * @template T
     * @param {?} token
     * @param {?} value
     * @param {?=} attributes
     * @return {?}
     */
    set(token, value, attributes) {
        // Validate that the storage is supported.
        if (!this._isSupported) {
            return;
        }
        // Write a new cookie.
        document.cookie = this.encode(token, value, Object.assign({ path: '/' }, attributes));
    }
    /**
     * @param {?} token
     * @param {?=} attributes
     * @return {?}
     */
    remove(token, attributes) {
        // Validate that the storage is supported.
        if (!this._isSupported) {
            return;
        }
        this.set(token, '', Object.assign({}, attributes, { expires: -1 }));
    }
    /**
     * @param {?=} attributes
     * @return {?}
     */
    clear(attributes) {
        // Validate that the storage is supported.
        if (!this._isSupported) {
            return;
        }
        /** @type {?} */
        const items = this.all();
        Object.keys(items)
            .forEach((/**
         * @param {?} token
         * @return {?}
         */
        token => this.remove(token, attributes)));
    }
    /**
     * @return {?}
     */
    isSupported() {
        return this._isSupported;
    }
    /**
     * @private
     * @return {?}
     */
    all() {
        return this.decode(document.cookie);
    }
    /**
     * @private
     * @param {?} token
     * @param {?} value
     * @param {?} attributes
     * @return {?}
     */
    encode(token, value, attributes) {
        // Encode the token.
        /** @type {?} */
        const encodedToken = encodeURIComponent(token)
            .replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent)
            .replace(/\(/g, '%28').replace(/\)/g, '%29');
        /** @type {?} */
        const rawValue = JSON.stringify(value);
        // Encode the value.
        /** @type {?} */
        const 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('');
    }
    /**
     * @private
     * @param {?} attributes
     * @return {?}
     */
    stringifyCookie(attributes) {
        // Make sure the expires attribute is a date and not a number.
        if (typeof attributes.expires === 'number') {
            /** @type {?} */
            const 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);
    }
}
if (false) {
    /**
     * @type {?}
     * @private
     */
    CookieStorage.prototype._isSupported;
    /**
     * @type {?}
     * @private
     */
    CookieStorage.prototype.decode;
    /**
     * @type {?}
     * @private
     */
    CookieStorage.prototype.jsonParse;
    /**
     * @type {?}
     * @private
     */
    CookieStorage.prototype.stringifyAttribute;
    /**
     * @type {?}
     * @private
     */
    CookieStorage.prototype.testIfSupported;
}

/**
 * @fileoverview added by tsickle
 * Generated from: lib/strategies/memory-storage.strategy.ts
 * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
 */
class MemoryStorage {
    constructor() {
        // State.
        this._storage = {};
        this.encode = (/**
         * @template T
         * @param {?} value
         * @param {?} attributes
         * @return {?}
         */
        (value, attributes) => {
            /** @type {?} */
            const data = { 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;
        });
    }
    /**
     * @template T
     * @param {?} token
     * @return {?}
     */
    get(token) {
        // Grab the data from the memory storage.
        /** @type {?} */
        const result = this._storage[token];
        // Validate the data exists.
        if (result === undefined) {
            return undefined;
        }
        // Extract the value and expire date.
        const { value, expires } = result;
        // Validate the value is still valid.
        if (expires !== undefined) {
            /** @type {?} */
            const now = new Date().getTime();
            /** @type {?} */
            const 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;
    }
    /**
     * @template T
     * @param {?} token
     * @param {?} value
     * @param {?=} attributes
     * @return {?}
     */
    set(token, value, attributes) {
        // Save the encoded value with the attributes.
        this._storage[token] = this.encode(value, Object.assign({}, attributes));
    }
    /**
     * @param {?} token
     * @param {?=} attributes
     * @return {?}
     */
    remove(token, attributes) {
        delete this._storage[token];
    }
    /**
     * @param {?=} attributes
     * @return {?}
     */
    clear(attributes) {
        this._storage = {};
    }
    /**
     * @return {?}
     */
    isSupported() {
        // The memory storage is always supported.
        return true;
    }
}
if (false) {
    /**
     * @type {?}
     * @private
     */
    MemoryStorage.prototype._storage;
    /**
     * @type {?}
     * @private
     */
    MemoryStorage.prototype.encode;
}

/**
 * @fileoverview added by tsickle
 * Generated from: lib/storage.service.ts
 * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
 */
class Storage {
    constructor() {
        // All available strategies (sorted best to last).
        this._strategies = {
            local: new PolyfillStorage(localStorage),
            cookie: new CookieStorage(),
            session: new PolyfillStorage(sessionStorage),
            memory: new MemoryStorage()
        };
        // Current selected storage strategy.
        this._current = null;
        this._current = this.findBestStorageStrategy();
    }
    /**
     * @return {?}
     */
    get local() {
        return this._strategies.local;
    }
    /**
     * @return {?}
     */
    get cookie() {
        return this._strategies.cookie;
    }
    /**
     * @return {?}
     */
    get session() {
        return this._strategies.session;
    }
    /**
     * @return {?}
     */
    get memory() {
        return this._strategies.memory;
    }
    /**
     * @template T
     * @param {?} token
     * @return {?}
     */
    get(token) {
        return this._current.get(token);
    }
    /**
     * @template T
     * @param {?} token
     * @param {?} value
     * @param {?=} attributes
     * @return {?}
     */
    set(token, value, attributes) {
        this._current.set(token, value, attributes);
    }
    /**
     * @param {?} token
     * @param {?=} attributes
     * @return {?}
     */
    remove(token, attributes) {
        this._current.remove(token, attributes);
    }
    /**
     * @param {?=} attributes
     * @return {?}
     */
    clear(attributes) {
        this._current.clear(attributes);
    }
    /**
     * @return {?}
     */
    isSupported() {
        return !!this._current;
    }
    /**
     * @private
     * @return {?}
     */
    findBestStorageStrategy() {
        /** @type {?} */
        const types = Object.keys(this._strategies);
        // Loop through all available strategies to find the best.
        for (let index = 0; index < types.length; index++) {
            /** @type {?} */
            const type = types[index];
            /** @type {?} */
            const strategy = this._strategies[type];
            // The first supported strategy is the best.
            if (strategy.isSupported()) {
                return strategy;
            }
        }
        // If no strategy was returned we emit an error.
        throw new Error('No storage strategy was available.');
    }
}
Storage.decorators = [
    { type: Injectable, args: [{
                providedIn: 'root'
            },] }
];
/** @nocollapse */
Storage.ctorParameters = () => [];
/** @nocollapse */ Storage.ngInjectableDef = ɵɵdefineInjectable({ factory: function Storage_Factory() { return new Storage(); }, token: Storage, providedIn: "root" });
if (false) {
    /**
     * @type {?}
     * @private
     */
    Storage.prototype._strategies;
    /**
     * @type {?}
     * @private
     */
    Storage.prototype._current;
}

/**
 * @fileoverview added by tsickle
 * Generated from: public_api.ts
 * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
 */

/**
 * @fileoverview added by tsickle
 * Generated from: bravobit-bb-foundation-storage.ts
 * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
 */

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