/// <reference path="./Variable.ts" />
/// <reference path="./IVariableState.ts" />
/// <reference path="./IVariableConfigOptions.ts" />
/// <reference path="../env/SessionStorage.ts" />
/// <reference path="../env/LocalStorage.ts" />
var econda;
(function (econda) {
    var storage;
    (function (storage) {
        var Variable = econda.storage.Variable;
        var SessionStorage = econda.env.SessionStorage;
        var LocalStorage = econda.env.LocalStorage;
        /**
         * Just a place where we can store data in browsers local and / or session storage
         * @class econda.storage.ClientBag
         * @experimental
         */
        var ClientBag = (function () {
            function ClientBag(cfg) {
                /**
                 * setTimeout handle for save operation. See {@link _triggerSaveInBrowser} for details
                 */
                this._triggerSaveTimeout = null;
                /**
                 * Timestamp when bag was saved.
                 * @private
                 * @property {Date} [_lastSaved=null]
                 */
                this._lastSaved = null;
                /**
                 * Name of client bag. Defaults to "default". In most cases there's no sense to use multiple (named) client bags.
                 * @cfg {String} [name=default]
                 * @accessor
                 */
                this._name = 'default';
                /**
                 * Auto save data to browser
                 * @cfg {Boolean} [autoSave=true]
                 * @accessor
                 */
                this._autoSave = true;
                this._variables = {};
                this._variableStates = {};
                if (cfg instanceof ClientBag) {
                    return cfg;
                }
                if (typeof cfg === 'object' && cfg !== null) {
                    if (typeof cfg.name !== 'undefined') {
                        this.setName((cfg.name));
                    }
                    if (typeof cfg.autoSave !== 'undefined') {
                        this.setAutoSave(cfg.autoSave);
                    }
                }
            }
            ClientBag.prototype.setName = function (name) {
                this._name = name;
                return this;
            };
            ClientBag.prototype.getName = function () {
                return this._name;
            };
            ClientBag.prototype.setAutoSave = function (autoSave) {
                this._autoSave = autoSave;
                return this;
            };
            //noinspection JSUnusedGlobalSymbols
            ClientBag.prototype.getAutoSave = function () {
                return this._autoSave;
            };
            /**
             * Get key that we'll use to store data in browser
             * @returns {String}
             */
            ClientBag.prototype.getStorageKey = function () {
                return 'econda.storage.ClientBag.' + this._name;
            };
            /**
             * Get list of all loaded variables
             * @returns {econda.storage.Variable[]}
             */
            ClientBag.prototype.getAllVariables = function () {
                var ret = [];
                for (var variableName in this._variables) {
                    ret.push(this._variables[variableName]);
                }
                return ret;
            };
            /**
             * Get variable by name
             * @param {String} variableName
             * @returns {econda.storage.Variable} variable or null
             */
            ClientBag.prototype.get = function (variableName) {
                if (typeof this._variables[variableName] === 'undefined') {
                    return null;
                }
                else {
                    return this._variables[variableName];
                }
            };
            /**
             * Get state info for variable
             * @returns {Object}
             */
            ClientBag.prototype.getState = function (variableName) {
                if (typeof this._variableStates[variableName] === 'undefined') {
                    return null;
                }
                else {
                    return this._variableStates[variableName];
                }
            };
            ClientBag.prototype.set = function (variable, replace) {
                if (replace === void 0) { replace = false; }
                variable = new Variable(variable);
                if (!variable.getName()) {
                    throw 'Cannot set variable. Name attribute is required but empty.';
                }
                var variableName = variable.getName();
                if (replace === true || typeof this._variables[variableName] === 'undefined') {
                    this._variables[variableName] = variable;
                }
                this._variableStates[variableName] = {
                    added: new Date(),
                    pageViews: 0
                };
                if (this._autoSave) {
                    this._triggerSaveInBrowser();
                }
                return this;
            };
            /**
             * Update variable properties
             */
            ClientBag.prototype.update = function (variableName, properties) {
                var c = this.get(variableName);
                if (c !== null) {
                    c.set(properties);
                }
                if (this._autoSave) {
                    this._triggerSaveInBrowser();
                }
                return this;
            };
            /**
             * Triggers save operation. Will wait 100ms so we'll have one save for multiple changes
             * @private
             */
            ClientBag.prototype._triggerSaveInBrowser = function () {
                var cmp = this;
                if (this._triggerSaveTimeout !== null) {
                    window.clearTimeout(this._triggerSaveTimeout);
                }
                this._triggerSaveTimeout = window.setTimeout(function () {
                    cmp.saveInBrowser();
                }, 100);
            };
            /**
             * Save all data to browser. If using the default bag, there's no reason to call this function.
             */
            ClientBag.prototype.saveInBrowser = function () {
                var forSessionStorage = { state: {}, variables: [] };
                var forLocalStorage = { state: {}, variables: [] };
                for (var variableName in this._variables) {
                    var c = this._variables[variableName];
                    if (c.getPermanent()) {
                        forLocalStorage.variables.push(c);
                        if (typeof this._variableStates[variableName] !== 'undefined') {
                            forLocalStorage.state[variableName] = this._variableStates[variableName];
                        }
                    }
                    else {
                        forSessionStorage.variables.push(c);
                        if (typeof this._variableStates[variableName] !== 'undefined') {
                            forSessionStorage.state[variableName] = this._variableStates[variableName];
                        }
                    }
                }
                var s = new econda.serialization.JsonSerializer();
                LocalStorage.setItem(this.getStorageKey(), s.serialize(forLocalStorage));
                SessionStorage.setItem(this.getStorageKey(), s.serialize(forSessionStorage));
                this._lastSaved = new Date();
                return this;
            };
            /**
             * Load data from browser. If using the default bag, there's no reason to call this function
             */
            ClientBag.prototype.loadFromBrowser = function () {
                var dataFromSessionStorage;
                var dataFromLocalStorage;
                var s = new econda.serialization.JsonSerializer();
                dataFromLocalStorage = LocalStorage.getItem(this.getStorageKey());
                dataFromSessionStorage = SessionStorage.getItem(this.getStorageKey());
                if (dataFromLocalStorage) {
                    var objectsFromLocalStorage = s.deserialize(dataFromLocalStorage);
                    if (typeof objectsFromLocalStorage.state === 'undefined') {
                        objectsFromLocalStorage.state = {};
                    }
                    for (var n = 0; n < objectsFromLocalStorage.variables.length; n++) {
                        var variableName = objectsFromLocalStorage.variables[n].getName();
                        this._variables[variableName] = objectsFromLocalStorage.variables[n];
                        if (typeof objectsFromLocalStorage.state[variableName] === 'object' && objectsFromLocalStorage.state[variableName] !== null) {
                            this._variableStates[variableName] = objectsFromLocalStorage.state[variableName];
                        }
                    }
                }
                if (dataFromSessionStorage) {
                    var objectsFromSessionStorage = s.deserialize(dataFromSessionStorage);
                    if (typeof objectsFromSessionStorage.state === 'undefined') {
                        objectsFromSessionStorage.state = {};
                    }
                    for (var n = 0; n < objectsFromSessionStorage.variables.length; n++) {
                        var variableName = objectsFromSessionStorage.variables[n].getName();
                        this._variables[variableName] = objectsFromSessionStorage.variables[n];
                        if (typeof objectsFromSessionStorage.state[variableName] === 'object' && objectsFromSessionStorage.state[variableName] !== null) {
                            this._variableStates[variableName] = objectsFromSessionStorage.state[variableName];
                        }
                    }
                }
                return this;
            };
            /**
             * Remove outdated variables.
             */
            ClientBag.prototype.init = function () {
                this.loadFromBrowser()
                    ._initVariablesAndState()
                    .saveInBrowser();
                return this;
            };
            ClientBag.prototype._initVariablesAndState = function () {
                for (var variableName in this._variables) {
                    this._updateVariableState(this._variables[variableName]);
                }
                // cleanup unused states
                for (var variableName in this._variableStates) {
                    if (typeof this._variables[variableName] === 'undefined') {
                        delete this._variableStates[variableName];
                    }
                }
                return this;
            };
            ClientBag.prototype._updateVariableState = function (variable) {
                var variableName = variable.getName();
                var state = (typeof this._variableStates[variableName] !== 'undefined') ? this._variableStates[variableName] : { added: new Date(), pageViews: 0 };
                // update state
                state.pageViews++;
                // check if variable is still valid
                var isValid = true;
                var maxPageViews = variable.getPageViewsToLive();
                isValid = isValid && (maxPageViews === null || state.pageViews <= maxPageViews);
                var expires = variable.getExpires();
                isValid = isValid && (expires === null || expires >= (new Date()));
                // invalidate if required
                if (isValid === false) {
                    this._invalidateVariable(variableName);
                }
                return this;
            };
            ClientBag.prototype._invalidateVariable = function (variableName) {
                var c = this._variables[variableName];
                switch (c.getInvalidationAction()) {
                    case Variable.INVALIDATION_ACTION_REMOVE:
                        this.destroy(variableName);
                        break;
                    case Variable.INVALIDATION_ACTION_KEEP:
                        c._setIsValid(false);
                        break;
                    default:
                        throw "Invalid invalidation action.";
                }
                return this;
            };
            /**
             * Remove and destroy variable.
             * @param {String} variableName
             * @chainable
             */
            ClientBag.prototype.destroy = function (variableName) {
                delete this._variables[variableName];
                delete this._variableStates[variableName];
                return this;
            };
            return ClientBag;
        }());
        storage.ClientBag = ClientBag;
    })(storage = econda.storage || (econda.storage = {}));
})(econda || (econda = {}));