var Cookie = function() {
  this.path = '/';
  this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
  this.domain = null;
  this.secure = false;
  this.state = this.readCookies();
};

Cookie.prototype = {
  /**
  * Returns the current value for a key
  * @param {String} name The key name
  * @param {Mixed} defaultValue A default value to return if the key's value is not found
  * @return {Mixed} The state data
  */
  get : function(name, defaultValue){
    return typeof this.state[name] == "undefined" ? defaultValue : this.decodeValue(this.state[name]);
  },
  /**
  * Sets the value for a key
  * @param {String} name The key name
  * @param {Mixed} value The value to set
  * @param {Long} expiration The expiration date to be set
  */
  set : function(name, value, expiration){
    if(typeof value == 'undefined' || value === null){
      this.clear(name);
      return;
    }
    this.setCookie(name, this.encodeValue(value), expiration);
    this.state[name] = this.encodeValue(value);
  },
  /**
  * Clears a value from the state
  * @param {String} name The key name
  */
  clear : function(name){
    this.clearCookie(name);
    delete this.state[name];
  },
  /**
  * Decodes a string previously encoded with {@link #encodeValue}.
  * @param {String} value The value to decode
  * @return {Mixed} The decoded value (or the raw value if no encoded type)
  */
  decodeValue : function(cookie){
    var re = /^(a|n|d|b|s|o)\:(.*)$/;
    var matches = re.exec(unescape(cookie));
    if(!matches || !matches[1]) return (cookie === 'null') ? null : cookie; // non state cookie, just return the raw value (unless its null [NFE-387])
    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;
    }
  },
  /**
  * Encodes a value including type information.  Decode with {@link #decodeValue}.
  * @param {Mixed} value The value to encode
  * @return {String} The encoded value
  */
  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 instanceof Date){
      enc = "d:" + v.toGMTString();
    }else if(v instanceof Array){
      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"){
          flat += key + "=" + this.encodeValue(v[key]) + "^";
        }
      }
      enc = "o:" + flat.substring(0, flat.length-1);
    }else{
      enc = "s:" + v;
    }
    return escape(enc);
  },
  readCookies : function(){
    var cookies = {};
    var c = document.cookie + ';';
    var re = /\s?(.*?)=(.*?);/g;
    var matches;
    while((matches = re.exec(c)) != null){
      var name = matches[1];
      var value = matches[2];
      cookies[name] = value;
    }
    return cookies;
  },
  setCookie : function(name, value, expiration){
    // expiration is an optional parameter
    var expirationDate;
    if(expiration == null){
      expirationDate = this.expires;
    } else {
      expirationDate = expiration;
    }
    document.cookie = name + " = " + value +
      ((expirationDate == null) ? "" : ("; expires=" + expirationDate.toGMTString())) +
      ((this.path == null) ? "" : ("; path=" + this.path)) +
      ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
      ((this.secure == true) ? "; secure" : "");
  },
  clearCookie : function(name){
    document.cookie = name + '=null; expires=Thu, 01-Jan-70 00:00:01 GMT' +
      ((this.path == null) ? "" : ("; path=" + this.path)) +
      ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
      ((this.secure == true) ? "; secure" : "");
  }
};

