/* This state provider keeps part of the state inside the browser history.
 *
 * We compress (shorten) url using dictionary based compression, i.e., we use
 * column separated list instead of url encoded hash:
 *  #v\d*       version/format
 *  :=          indicates string values
 *  :\d+        lookup value in dictionary hash
 *  #v1:=value1:5:=value2:=value3:...
*/

Ext.define('PVE.StateProvider', {
    extend: 'Ext.state.LocalStorageProvider',

    // private
    setHV: function(name, newvalue, fireEvents) {
	let me = this;

	let changes = false;
	let oldtext = Ext.encode(me.UIState[name]);
	let newtext = Ext.encode(newvalue);
	if (newtext !== oldtext) {
	    changes = true;
	    me.UIState[name] = newvalue;
	    if (fireEvents) {
		me.fireEvent("statechange", me, name, { value: newvalue });
	    }
	}
	return changes;
    },

    // private
    hslist: [
	// order is important for notifications
	// [ name, default ]
	['view', 'server'],
	['rid', 'root'],
	['ltab', 'tasks'],
	['nodetab', ''],
	['storagetab', ''],
	['sdntab', ''],
	['pooltab', ''],
	['kvmtab', ''],
	['lxctab', ''],
	['dctab', ''],
    ],

    hprefix: 'v1',

    compDict: {
        tfa: 54,
	sdn: 53,
	cloudinit: 52,
	replication: 51,
	system: 50,
	monitor: 49,
	'ha-fencing': 48,
	'ha-groups': 47,
	'ha-resources': 46,
	'ceph-log': 45,
	'ceph-crushmap': 44,
	'ceph-pools': 43,
	'ceph-osdtree': 42,
	'ceph-disklist': 41,
	'ceph-monlist': 40,
	'ceph-config': 39,
	ceph: 38,
	'firewall-fwlog': 37,
	'firewall-options': 36,
	'firewall-ipset': 35,
	'firewall-aliases': 34,
	'firewall-sg': 33,
	firewall: 32,
	apt: 31,
	members: 30,
	snapshot: 29,
	ha: 28,
	support: 27,
	pools: 26,
	syslog: 25,
	ubc: 24,
	initlog: 23,
	openvz: 22,
	backup: 21,
	resources: 20,
	content: 19,
	root: 18,
	domains: 17,
	roles: 16,
	groups: 15,
	users: 14,
	time: 13,
	dns: 12,
	network: 11,
	services: 10,
	options: 9,
	console: 8,
	hardware: 7,
	permissions: 6,
	summary: 5,
	tasks: 4,
	clog: 3,
	storage: 2,
	folder: 1,
	server: 0,
    },

    decodeHToken: function(token) {
	let me = this;

	let state = {};
	if (!token) {
	    me.hslist.forEach(([k, v]) => { state[k] = v; });
	    return state;
	}

	let [prefix, ...items] = token.split(':');

	if (prefix !== me.hprefix) {
	    return me.decodeHToken();
	}

	Ext.Array.each(me.hslist, function(rec) {
	    let value = items.shift();
	    if (value) {
		if (value[0] === '=') {
		    value = decodeURIComponent(value.slice(1));
		}
		for (const [key, hash] of Object.entries(me.compDict)) {
		    if (String(value) === String(hash)) {
			value = key;
			break;
		    }
		}
	    }
	    state[rec[0]] = value;
	});

	return state;
    },

    encodeHToken: function(state) {
	let me = this;

	let ctoken = me.hprefix;
	Ext.Array.each(me.hslist, function(rec) {
	    let value = state[rec[0]];
	    if (!Ext.isDefined(value)) {
		value = rec[1];
	    }
	    value = encodeURIComponent(value);
	    if (!value) {
		ctoken += ':';
	    } else if (Ext.isDefined(me.compDict[value])) {
		ctoken += ":" + me.compDict[value];
	    } else {
		ctoken += ":=" + value;
	    }
	});

	return ctoken;
    },

    constructor: function(config) {
	let me = this;

	me.callParent([config]);

	me.UIState = me.decodeHToken(); // set default

	let history_change_cb = function(token) {
	    if (!token) {
		Ext.History.back();
		return;
	    }

	    let newstate = me.decodeHToken(token);
	    Ext.Array.each(me.hslist, function(rec) {
		if (typeof newstate[rec[0]] === "undefined") {
		    return;
		}
		me.setHV(rec[0], newstate[rec[0]], true);
	    });
	};

	let start_token = Ext.History.getToken();
	if (start_token) {
	    history_change_cb(start_token);
	} else {
	    let htext = me.encodeHToken(me.UIState);
	    Ext.History.add(htext);
	}

	Ext.History.on('change', history_change_cb);
    },

    get: function(name, defaultValue) {
	let me = this;

	let data;
	if (typeof me.UIState[name] !== "undefined") {
	    data = { value: me.UIState[name] };
	} else {
	    data = me.callParent(arguments);
	    if (!data && name === 'GuiCap') {
		data = {
		    vms: {},
		    storage: {},
		    access: {},
		    nodes: {},
		    dc: {},
		    sdn: {},
		};
	    }
	}
	return data;
    },

    clear: function(name) {
	let me = this;

	if (typeof me.UIState[name] !== "undefined") {
	    me.UIState[name] = null;
	}
	me.callParent(arguments);
    },

    set: function(name, value, fireevent) {
        let me = this;

	if (typeof me.UIState[name] !== "undefined") {
	    var newvalue = value ? value.value : null;
	    if (me.setHV(name, newvalue, fireevent)) {
		let htext = me.encodeHToken(me.UIState);
		Ext.History.add(htext);
	    }
	} else {
	    me.callParent(arguments);
	}
    },
});
