function Homophonic() {
  this.decrypt_filter = function(character) {return (constants.ascii_punctuation + constants.digits + " \r\n").indexOf(character) > -1};
  this.encrypt_filter = this.decrypt_filter;
  this.ctkey = [0,0,0,0];
  this.valid_letters = "abcdefghiklmnopqrstuvwxyz";
  
  this.get_letter = function(start,value) {
    if (start == 0)
      return "-";
    var diff = value - start;
    if (diff < 0)
      diff = (25 - Math.abs(diff));
    return this.valid_letters[diff];
  }
  
  this.__proto__decrypt = this.decrypt;
  this.decrypt = function(text) {
    var output = "";
    var extrachars = constants.ascii_punctuation + "\r\n";
    lines = this.__proto__decrypt(text).split("\n");
    for (i = 0;i < lines.length;i++)
    {
       letters = lines[i].split(" ");
       for (j = 0;j < letters.length;j++)
       {
         var letterVal = parseInt(letters[j],10);
         if (letterVal == -1)
           output += letters[j]
         else
           output += this.get_letter(this.ctkey[Math.floor((letterVal - 1) / 25)],letterVal);
       }
       output += "\n";
    }
    return output;
  }
 
  this.__proto__encrypt = this.encrypt;
  this.encrypt = function(text) {
    text = this.__proto__encrypt(text);
    return text;
  }
  
  this.set = function(ct, pt) {
    var alphaIndex = Math.floor((ct - 1) / 25);
    if (pt == "")
      this.ctkey[alphaIndex] = 0;
    else {
      var diff = this.valid_letters.indexOf(pt);
      var startVal = (alphaIndex * 25);
      var startDiff = ct - (startVal + diff);
      if (startDiff < 1 || startDiff > 25)
        startDiff = (25 - Math.abs(startDiff));
      this.ctkey[alphaIndex] = startVal + startDiff;
    }
  }
}
Homophonic.prototype = new Cipher();

function HomophonicSolver () {
  this.cipher = new Homophonic();
  this._shortcuts["f"] = "frequency";
  this._shortcuts["k"] = "keys";
  this._shortcuts["s"] = "set";
  this._shortcuts["ts"] = "test_set";

  this.display = function() {
    var data = "";
    var ct = this.text().split("\n");
    var pt = this.cipher.decrypt().split("\n");
    for (i = 0;i < ct.length;i++)
    {
      var ctletters = ct[i].split(" ");
      var ctline = "";
      var ptline = "";
      for (j = 0;j < ctletters.length;j++)
      {
        ctline += padr(ctletters[j]," ",3);
        ptline += padr(pt[i][j]," ",3);
      }
      data += ctline + "\n";
      data += ptline + "\n\n";
    }
    return "<pre>" + data + "</pre>";
  }

  this.frequency = function(length) {
    if (arguments.length != 0)
      throw CommandException(constants.err_argnum);
    var items = strRemoveOthers(this.text().replace(/\n/g," "),constants.digits + " ").split(" ");
    var counts = [new Array(),new Array(),new Array(),new Array()];
    for (i=0;i<items.length;i++)
    {
      var letter = items[i];
      var alphaIndex = Math.floor((parseInt(letter,10) - 1) / 25);
      if (counts[alphaIndex][letter] == null)
        counts[alphaIndex][letter] = 0;
      counts[alphaIndex][letter] += 1;
    }
    var output = ""
    for (i=0;i<4;i++)
      output += this._print_counts(counts[i]) + "\n";
    return output;
  }
  
  this.keys = function() {
    var ct = "";
    var pt = "";
    for (i=0;i<4;i++) {
      var startIndex = (i * 25) + 1;
      ct += padr(padl(startIndex.toString(),"0",2)," ",4);
      pt += padr(this.cipher.get_letter(this.cipher.ctkey[i],startIndex)," ",4);
    }
    return ct + "\n" + pt;
  }

  this.set = function(ct,pt) {
    if (pt == null)
      pt = "";
    else if (arguments.length < 2)
      throw CommandException(constants.err_argnum);
    ct = parseInt(ct,10);
    pt = pt.toLowerCase();
    if (ct < 1 || ct > 100)
      throw CommandException("Ciphertext argument must be 1-100.");
    else if ((pt < "a" || pt > "z") && pt != "")
      throw CommandException("Plaintext argument must be a-z.");
    this.cipher.set(ct,pt);
    this.save();
    return this.display();
  }
  
  this.test_set = function(ct,pt) {
    var temp_ct = this.cipher.ctkey.slice();
    var result = this.set(ct,pt);
    this.cipher.ctkey = temp_ct;
    return result;
  }
  
  this.__proto___load__ = this._load_;
  this._load_ = function(data) {
    this.__proto___load__(data);
    if (data.ctkey != null)  
      this.cipher.ctkey = data.ctkey;
  }
  
  this.__proto___save__ = this._save_;
  this._save_ = function(data) {
    data = this.__proto___save__(data);
    data.ctkey = this.cipher.ctkey;
    return data;
  }
}
HomophonicSolver.prototype = new CipherSolver();
document.solvers.register("Homophonic",HomophonicSolver);
