/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

function dict(obj) {
  this.obj = {};
  if (obj) this.set(obj);
}

dict.prototype.set = function() {
  var obj, key, value;
  (typeof(arguments[0]) == "object") ? obj = arguments[0] : key = arguments[0];
  if (arguments[1]) value = arguments[1];
  if (obj) {
    if (obj.push) return;
    for (var k in obj) {
      add.call(this, k, obj[k]);
    }
  }
  if (key && value) add.call(this, key, value);

  function add(key, value) {
    if (typeof(value) == "object") {
      this.obj[key] = (value.push) ? value : new dict(value);
      /* if (value.push) {
        for (var i = 0; i < value.length; i++) {      
          this.obj[key].push(value[i]);        
        }          
      } else {        
          new dict(value);      
        } */          
    } else {
      this.obj[key] = value;
    }
  }
}

dict.prototype.get = function(key) {
  try {
    return this.obj[key];
  } catch(e) {
    return;
  }
}

dict.prototype.keys = function() {
  var result = [];
  for (var k in this.obj) {
    result.push(k);
  }
  return result;
}

dict.prototype.values = function() {
  var result = [];
  for (var k in this.obj) {
    result.push(this.obj[k]);
  }
  return result;
}

dict.prototype.items = function() {
  var result = [];
  for (var k in this.obj) {
    var v = "";
    try {
      v = this.obj[k];
    } catch(e) {
      continue;
    }
    result.push([k, v]);
  }
  return result;
}

dict.prototype.hasKey = function(key) {
  return (this.obj[key] !== undefined) ? true : false;
}

dict.prototype.each = function(fn) {
  for (var k in this.obj) {
    fn.call(this, k, this.obj[k]);
  }
}

dict.prototype.toJSON = function() {
  var result = [];
  this.each(function(k, v) {
    result.push('"' + k.replace(/([\"\\])/g, "\\$1") + '":' + (function() {
      if (v.toJSON) {
        return v.toJSON();
      } else if (v.push) {
        var ary = [];
        for (var i = 0; i < v.length; i++) {
          ary.push(v[i].replace(/([\"\\])/g, "\\$1"));
        }
        return '["' + ary.join('","') + '"]';
      } else {
        return '"' + v.replace(/([\"\\])/g, "\\$1") + '"';
      }
    })());
  });
  return "{" + result.join(",") + "}";
}

/* ------------------------------------------------------------------------- */


function Markov() {
  this.dic = new dict();
}

Markov.prototype.add = function(token) {
  token.unshift("__START__");
  while (token) {
    var prefix1 = token[0];
    var prefix2 = token[1];
    var suffix = (token[2]) ? token[2] : "__END__";
    if (this.dic.hasKey(prefix1)) {
      if (this.dic.get(prefix1).hasKey(prefix2)) {
        this.dic.get(prefix1).get(prefix2).push(suffix);
      } else {
        this.dic.get(prefix1).set(prefix2, [suffix]);
      }
    } else {
      this.dic.set(prefix1, {});
      this.dic.get(prefix1).set(prefix2, [suffix]);
    }
    if (suffix == "__END__") break;
    token.shift();
  }
}

Markov.prototype.generator = function(Max) {
  var result = [];
  var max = Max || 100;
  if (!this.dic.hasKey("__START__")) return false;
  var prefix1 = this.dic.get("__START__").keys().rand();
  result.push(prefix1);
  if (this.dic.hasKey(prefix1)) var prefix2 = this.dic.get(prefix1).keys().rand();
  result.push(prefix2);
  for (var i = 0; i < max; i++) {
    var suffix = (this.dic.hasKey(prefix1)) ?
      this.dic.get(prefix1).get(prefix2).rand() : "";
    if (suffix == "__END__") break;
    result.push(suffix);
    prefix1 = prefix2;
    prefix2 = suffix;
  }
  return result;
}

