// An RC4 implementation in JavaScript. See http://en.wikipedia.org/wiki/RC4
// Initialize an array of ints 0-255 in order.
// This array is copied and used as a base for all keys.
jsPwGenStartingArray = [];
for (var i = 0; i < 256; i++) {
  jsPwGenStartingArray.push(i);
};
jsPwGenStartingArray.push(0);
jsPwGenStartingArray.push(0);

jsPwGen = {
// One we get a pseudorandom number, it is converted by looking up a 
// printable character in this array.
// Printable characters are:
// `1234567890-=
// ~!@#$%^&*()_+
// qwertyuiop[]\
// QWERTYUIOP{}|
// asdfghjkl;'
// ASDFGHJKL:"
// zxcvbnm,./
// ZXCVBNM<>?
// To convert these character to a JS const array, I used
// def c():
//   x = raw_input('')
//   for char in x:
//     print "'%s'," % char,
  printableCharacters: [
      '`', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=',
      '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+',
      'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\\',
      'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '|',
      'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'',
      'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"',
      'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/',
      'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?'],

// Sometimes a website will not accept all printable chars, so here is a 
// lookup table for just alphanumeric characters.
  alphanumCharacters: [
      '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
      'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p',
      'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P',
      'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l',
      'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L',
      'z', 'x', 'c', 'v', 'b', 'n', 'm',
      'Z', 'X', 'C', 'V', 'B', 'N', 'M'],

// The generated key should have 258 integers. The first 256 are the values
// in the key matrix (s) and the last 2 are the integer indicies for 
// transforming s as it is used (i and j). 
  generateKey: function(password) {
    var s = jsPwGenStartingArray.slice();
    var j = 0;
    var temp;
    var passwordLength = password.length;
  
    for (var i = 0; i < 256; i++) {
      j = (j + s[i] + (password.charCodeAt(i % passwordLength))) % 256;
      temp = s[i];
      s[i] = s[j];
      s[j] = temp;
    }
    return s;
  },

  stepKey: function(keyArray) {
    var i = keyArray[256];
    var j = keyArray[257];
    var temp;
    i = (i + 1) % 256;
    j = (j + keyArray[i]) % 256;
    temp = keyArray[i];
    keyArray[i] = keyArray[j];
    keyArray[j] = temp;
    keyArray[256] = i;
    keyArray[257] = j;
  },

  nextNumber: function(keyArray) {
    jsPwGen.stepKey(keyArray);
    var i = keyArray[256];
    var j = keyArray[257];
    return keyArray[(keyArray[i] + keyArray[j]) % 256];
  },

  convertToPrintable: function(bytes, charset) {
    var charsetLength = charset.length;
    for (var i = 0; i < bytes.length; i++) {
      bytes[i] = charset[bytes[i] % charsetLength];
    }
    return bytes;
  },

// TODO: add doc string
 generatePassword: function(inputs, passwordLength, passwordCharset) {
    var cipherBytes = [];
    var currentKey;
    var i, j;
    // Initialize the array of output characters.
    for (i = 0; i < passwordLength; i++) { 
      cipherBytes.push(0);
    }
    for (i = 0; i < inputs.length; i++) {
      // Generate a key from each string in the input.
      currentKey = jsPwGen.generateKey(inputs[i]);
      // Skip the first 1,000 states in each of the keys since the key state is
      // initially pretty guessable.
      for (j = 0; j < 1000; j++) {
        jsPwGen.stepKey(currentKey);
      }
      // Get the next passwordLength values from each of the keys, and xor them
      // together. 
      for (j = 0; j < passwordLength; j++) {
        cipherBytes[j] ^= jsPwGen.nextNumber(currentKey);
      }
    }
  
    // Convert the xored value of the combined keystreams and express it in the
    // desired character set.
    return jsPwGen.convertToPrintable(cipherBytes, passwordCharset).join('');
  }
};
