/**
* DevExtreme (ui.grid_core.state_storing_core.js)
* Version: 22.1.7 (build 23032-0308)
* Build date: Wed Feb 01 2023
*
* Copyright (c) 2012 - 2023 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/
import eventsEngine from '../../events/core/events_engine';
import { getWindow } from '../../core/utils/window';
import modules from './ui.grid_core.modules';
import errors from '../widget/ui.errors';
import { sessionStorage } from '../../core/utils/storage';
import { extend } from '../../core/utils/extend';
import { each } from '../../core/utils/iterator';
import { isDefined, isPlainObject, isEmptyObject } from '../../core/utils/type';
import { fromPromise } from '../../core/utils/deferred';

const DATE_REGEX = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/;

const parseDates = function(state) {
    if(!state) return;
    each(state, function(key, value) {
        if(isPlainObject(value) || Array.isArray(value)) {
            parseDates(value);
        } else if(typeof value === 'string') {
            const date = DATE_REGEX.exec(value);
            if(date) {
                state[key] = new Date(Date.UTC(+date[1], +date[2] - 1, +date[3], +date[4], +date[5], +date[6]));
            }
        }
    });
};

export const StateStoringController = modules.ViewController.inherit((function() {
    const getStorage = function(options) {
        const storage = options.type === 'sessionStorage' ? sessionStorage() : getWindow().localStorage;

        if(!storage) {
            throw new Error('E1007');
        }

        return storage;
    };

    const getUniqueStorageKey = function(options) {
        return isDefined(options.storageKey) ? options.storageKey : 'storage';
    };

    return {
        _loadState: function() {
            const options = this.option('stateStoring');

            if(options.type === 'custom') {
                return options.customLoad && options.customLoad();
            }
            try {
                return JSON.parse(getStorage(options).getItem(getUniqueStorageKey(options)));
            } catch(e) {
                errors.log(e.message);
            }
        },

        _saveState: function(state) {
            const options = this.option('stateStoring');

            if(options.type === 'custom') {
                options.customSave && options.customSave(state);
                return;
            }
            try {
                getStorage(options).setItem(getUniqueStorageKey(options), JSON.stringify(state));
            } catch(e) {
                errors.log(e.message);
            }
        },

        publicMethods: function() {
            return ['state'];
        },

        isEnabled: function() {
            return this.option('stateStoring.enabled');
        },

        init: function() {
            const that = this;

            that._state = {};
            that._isLoaded = false;
            that._isLoading = false;

            that._windowUnloadHandler = function() {
                if(that._savingTimeoutID !== undefined) {
                    that._saveState(that.state());
                }
            };

            eventsEngine.on(getWindow(), 'unload', that._windowUnloadHandler);

            return that;
        },

        isLoaded: function() {
            return this._isLoaded;
        },

        isLoading: function() {
            return this._isLoading;
        },

        load: function() {
            this._isLoading = true;
            const loadResult = fromPromise(this._loadState());
            loadResult.always(() => {
                this._isLoaded = true;
                this._isLoading = false;
            }).done((state) => {
                if(state !== null && !isEmptyObject(state)) {
                    this.state(state);
                }
            });
            return loadResult;
        },

        state: function(state) {
            const that = this;

            if(!arguments.length) {
                return extend(true, {}, that._state);
            } else {
                that._state = extend({}, state);
                parseDates(that._state);
            }
        },

        save: function() {
            const that = this;

            clearTimeout(that._savingTimeoutID);
            that._savingTimeoutID = setTimeout(function() {
                that._saveState(that.state());
                that._savingTimeoutID = undefined;
            }, that.option('stateStoring.savingTimeout'));
        },

        optionChanged: function(args) {
            const that = this;

            switch(args.name) {
                case 'stateStoring':
                    if(that.isEnabled() && !that.isLoading()) {
                        that.load();
                    }

                    args.handled = true;
                    break;
                default:
                    that.callBase(args);
            }
        },

        dispose: function() {
            clearTimeout(this._savingTimeoutID);
            eventsEngine.off(getWindow(), 'unload', this._windowUnloadHandler);
        }
    };
})());
