var Serializer = function() {

    this.decodeValue = function(cookie){
        var re = /^(a|n|d|b|s|o)\:(.*)$/;
        var matches = re.exec(unescape(cookie));
        if(!matches || !matches[1]) return; // non state cookie
        var type = matches[1];
        var v = matches[2];
        switch(type){
            case "n":
                return parseFloat(v);
            case "d":
                return new Date(Date.parse(v));
            case "b":
                return (v == "1");
            case "a":
                var all = [];
                var values = v.split("^");
                for(var i = 0, len = values.length; i < len; i++){
                    all.push(this.decodeValue(values[i]));
                }
                return all;
           case "o":
                var all = {};
                var values = v.split("^");
                for(var i = 0, len = values.length; i < len; i++){
                    var kv = values[i].split("=");
                    all[kv[0]] = this.decodeValue(kv[1]);
                }
                return all;
           default:
                return v;
        }
    },
    
    this.encodeValue = function(v){
        var enc;
        if(typeof v == "number"){
            enc = "n:" + v;
        }else if(typeof v == "boolean"){
            enc = "b:" + (v ? "1" : "0");
        }else if(v && typeof v.getFullYear == 'function'){
            enc = "d:" + v.toGMTString();
        }else if(v && typeof v.length == 'number' && typeof v.splice == 'function'){
            var flat = "";
            for(var i = 0, len = v.length; i < len; i++){
                flat += this.encodeValue(v[i]);
                if(i != len-1) flat += "^";
            }
            enc = "a:" + flat;
        }else if(typeof v == "object"){
            var flat = "";
            for(var key in v){
                if(typeof v[key] != "function" && v[key] !== undefined){
                    flat += key + "=" + this.encodeValue(v[key]) + "^";
                }
            }
            enc = "o:" + flat.substring(0, flat.length-1);
        }else{
            enc = "s:" + v;
        }
        return escape(enc);        
    }
	
	return this;

}();

var AjaxHandler = function(config) {
	this.config = config;
	
	this.save = function(name, data) {
		$.post(this.config.saveURL, {'key': name, 'data':data}, function (data, status) {
			//console.log("data", data); 	 //{status: 'SAVED'}
			//console.log("status", status); //success
		}, "json");
	}
	
	this.load = function(name) {
		return g_objUserStore.data[name];
	}
	return this;
};



var CookieHandler = function(config) {
	this.config = config;
	
	this.createCookie = function(name,value,days) {
		if (days) {
			var date = new Date();
			date.setTime(date.getTime()+(days*24*60*60*1000));
			var expires = "; expires="+date.toGMTString();
		}
		else var expires = "";
		document.cookie = name+"="+value+expires+"; path=/";
	}

	this.readCookie = function(name) {
		var nameEQ = name + "=";
		var ca = document.cookie.split(';');
		for(var i=0;i < ca.length;i++) {
			var c = ca[i];
			while (c.charAt(0)==' ') c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
		}
		return null;
	}

	this.eraseCookie = function(name) {
		this.createCookie(name,"",-1);
	}

	this.writePagedData = function(strName, strData) {
		var i = 1;
		
		while (strData.length > 0) {
			strPage = strData.substring(0, this.config.pageSize);
			
			this.createCookie(strName + i, strPage, this.config.cookieExpire);
			
			if (strData.length > this.config.pageSize)
				strData = strData.substring(this.config.pageSize);
			else
				strData = "";
			
			i++;
		}
		this.eraseCookie(strName + i);
	}	
	
	this.readPagedData = function(strName) {
		var i = 1;
		var strData = this.readCookie(strName + i);
		var strReturn = null
		
		
		while (strData != null && strData != 'null') {
			if (strReturn == null)
				strReturn = strData;
			else
				strReturn += strData;
			
			i++;
			strData = this.readCookie(strName + i);
		}
		
		return strReturn;
	}

	this.save = function(name, data) {
		this.writePagedData(name, data);
	}
	
	this.load = function(name) {
		return this.readPagedData(name);
	}

	return this;
}


var PersistenceProvider = function(config) {
	this.config = config;
	var provider = this;
	
	var ajaxHandler 	= new AjaxHandler(this.config);
	var cookieHandler 	= new CookieHandler(this.config);
	
	this.registered = g_objUserStore.registered || false;
	
	function applyObject(target, source){
		for(var p in source){
			target[p] = source[p];
		}
	}
		
	this.saveSlot = function(type, key, value) {
		//console.log(type+" Slot saved " + key+":"+value+" -- > " );		
		if(this.registered)
			ajaxHandler.save(key, value);
		else
			cookieHandler.save(key, value);
	}

	this.loadSlot = function(type, key) {
		var value = "";

		if(this.registered) {
			value = ajaxHandler.load(key);
			if(value == null || value == "") {
				value = cookieHandler.load(key);
				this.saveSlot(type, key, value);
			}
		}
		else
			value = cookieHandler.load(key);
		
		return value;
	}
	
	this.keyValueStoreHandlers 	= {};

	this.keyValueStoreHandlerTemplate = {
		"base" : {
			setValue: function(value, save) {
				save = save || true;
				
				this.slot = value;

				if(save)
					this.fireSlotSet(this.name, this.slot);
			},

			getValue: function() {
				return this.slot;
			},

			fireSlotSet : function (key, value) {
				//console.log("base Slot set " + key+":"+value);
				this.saveSlot(key, value);
			},

			saveSlot : function (key, value) {
				provider.saveSlot(this.type, key, value);
			},

			destroy : function() {
			}
		},
		
		"delayed" : {
			id : -1,

			setValue: function(value, save) {
				save = save || true;

				this.slot = value;

				if(save)
					this.fireSlotSet(this.name, this.slot);
			},

			getValue: function() {
				return this.slot;
			},

			fireSlotSet : function (key, value) {
				var self = this;
				
				//console.log("delayed Slot set " + key+":"+value);
				if(this.id != -1) {
					clearInterval(this.id);
				}
				
				this.id = window.setTimeout(function() {
					self.saveSlot(key, value);
					self.id = -1;
				}, this.timeout || 1000);
			},
			
			saveSlot : function (key, value) {
				provider.saveSlot(this.type, key, value);
			},

			destroy : function() {
				if(this.id != -1) {
					clearInterval(this.id);
					this.id = -1;
					this.saveSlot(this.name, this.slot);
				}
			}
		}
	};	

	
	this.getKeyValue = function(name) {
		return this.getHandler(name).getValue();
	}
	
	this.setKeyValue = function(name, value) {
		this.getHandler(name).setValue(value);
	}

	this.getKeyObject = function(name) {
		return Serializer.decodeValue(this.getHandler(name).getValue());
	}
	
	this.setKeyObject = function(name, value) {
		this.getHandler(name).setValue(Serializer.encodeValue(value));
	}

	this.registerSlot = function(name, type, config) {
		
		type = type || "base";
		config = config || {};
		
		config['name'] = name;
		config['type'] = type;
	
		config['slot'] = this.loadSlot(type, name);

		applyObject(config, this.keyValueStoreHandlerTemplate[type]);
		
		this.keyValueStoreHandlers[name] = config;
	
		return config;
	}

	this.getHandler = function(name) {
		return this.keyValueStoreHandlers[name] || this.registerSlot(name);
	}
	
	return this;
}

$(document).ready(function() {
	PP = new PersistenceProvider(cfgPersist);
});
