import java.io.*;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;

public class PrivateCrypt extends Applet implements ActionListener {

  TextArea mAText, kAText, cAText, mBText, kBText, cBText, messages;
  Choice edChoice, verChoice, asciiChoice;
  Button go;

  public void init() {
    setLayout(null);
    mAText = new TextArea("Enter message here for encrypting", 8, 50, TextArea.SCROLLBARS_BOTH);
    kAText = new TextArea("Enter key here (in ASCII or binary, 0 is the identity key)", 2, 50, TextArea.SCROLLBARS_BOTH);
    cAText = new TextArea(null, 8, 50, TextArea.SCROLLBARS_BOTH);
    mBText = new TextArea(null, 8, 50, TextArea.SCROLLBARS_BOTH);
    kBText = new TextArea(null, 2, 50, TextArea.SCROLLBARS_BOTH);
    cBText = new TextArea("Enter code (in binary) here for decrypting", 8, 50, TextArea.SCROLLBARS_BOTH);
    messages = new TextArea("No errors.");
    Label kALabel = new Label("Key:");
    Label mALabel = new Label("Message:");
    Label cALabel = new Label("Code in ASCII:");
    Label kBLabel = new Label("Key in binary:");
    Label mBLabel = new Label("Message in binary:");
    Label cBLabel = new Label("Code in binary:");
    go = new Button("Go!");
    edChoice = new Choice();
    verChoice = new Choice();
    asciiChoice = new Choice();

    edChoice.add("Encrypt");
    edChoice.add("Decrypt");
    edChoice.select("Encrypt");
    verChoice.add("Verbose");
    verChoice.add("Quick");
    verChoice.select("Verbose");
    asciiChoice.add("Normal");
    asciiChoice.add("Forced ASCII");
    asciiChoice.select("Normal");

    kBText.setEditable(false);
    mBText.setEditable(false);
    messages.setEditable(false);

    edChoice.setBounds(0, 25, 70, 25);
    verChoice.setBounds(90, 25, 70, 25);
    asciiChoice.setBounds(180, 25, 100, 25);
    go.setBounds(700, 25, 70, 25);
    messages.setBounds(305, 0, 365, 75);
    mALabel.setBounds(0, 100, 100, 25);
    mAText.setBounds(0, 125, 390, 125);
    mBLabel.setBounds(410, 100, 100, 25);
    mBText.setBounds(410, 125, 390, 125);
    kALabel.setBounds(0, 275, 100, 25);
    kAText.setBounds(0, 300, 390, 75);
    kBLabel.setBounds(410, 275, 100, 25);
    kBText.setBounds(410, 300, 390, 75);
    cBLabel.setBounds(0, 400, 100, 25);
    cBText.setBounds(0, 425, 390, 125);
    cALabel.setBounds(410, 400, 100, 25);
    cAText.setBounds(410, 425, 390, 125);

    add(messages);
    add(mAText);
    add(kAText);
    add(cAText);
    add(mALabel);
    add(kALabel);
    add(cALabel);
    add(mBText);
    add(kBText);
    add(cBText);
    add(mBLabel);
    add(kBLabel);
    add(cBLabel);
    add(go);
    add(edChoice);
    add(verChoice);
    add(asciiChoice);
  }
  
  public void start() {
    go.addActionListener(this);
  }
  
  public void stop() {
    go.addActionListener(null);
  }
  
  public void actionPerformed(ActionEvent e) {
    if (e.getSource() == go) {
      mBText.setText("");
      kBText.setText("");
      if (edChoice.getSelectedItem() == "Encrypt") {
	cBText.setText("");
	cAText.setText("");
      } else { //Decrypt
	mAText.setText("");
	if (cBText.getText().length() > 0) //binary code takes precedence
	  cAText.setText(""); 
      }
      messages.setText("No errors.");
      doIt();
    }
  }

  public void doIt() {
    String key = new String(), keyBin = new String(), message = new String(), messageBin = new String(), 
      codeBin = new String(), codeASCII = new String();
    boolean good = true;
    key = kAText.getText();
    if (key.length() < 1) {
      messages.setText("You must enter a key.");
      good = false;
    }
    if (edChoice.getSelectedItem() == "Decrypt") {
      codeBin = cBText.getText();
      if (codeBin.length() == 0) {
	codeASCII = cAText.getText();
	for (int x = 0; x < codeASCII.length(); x++) {  // remove added newlines
	  if (codeASCII.charAt(x) == '\n') {
	    codeASCII = codeASCII.substring(0, x) + codeASCII.substring(x+1, codeASCII.length());
	    x--;
	  }
	}
	codeBin = toBinary(codeASCII);
      }
      if (!isBinary(codeBin) || codeBin.length() % 8 != 0) {
	messages.setText("Non-binary code.  Attempting to parse...");
	codeBin = normalize(codeBin);
	if (codeBin.length() % 8 != 0) {
	  messages.append("\nCode is not a valid binary code.");
	  good = false;
	}
      }
    }

    if (good) {
      if (isBinary(key)) 
	keyBin = key;
      else 
	keyBin = toBinary(key);
      if (verChoice.getSelectedItem() == "Verbose")
	display(keyBin, kBText);

      if (edChoice.getSelectedItem() == "Encrypt") {
	message = mAText.getText();
	messageBin = toBinary(message);
        if (verChoice.getSelectedItem() == "Verbose")
	  display(messageBin, mBText);
	codeBin = encode(messageBin, keyBin);
	display(codeBin, cBText);
        if (verChoice.getSelectedItem() == "Verbose") {
	  codeASCII = toASCII(codeBin);
	  display(codeASCII, cAText);
	}
      } else { //Decrypt
	if (verChoice.getSelectedItem() == "Verbose") {
	  if (codeASCII.length() == 0) { //binary code, print ASCII
	    codeASCII = toASCII(codeBin);
	    display(codeASCII, cAText);
	  } else
	    display(codeBin, cBText);
	}
	messageBin = encode(codeBin, keyBin);
	if (verChoice.getSelectedItem() == "Verbose")
	  display(messageBin, mBText);
	message = toASCII(messageBin);
	displayWS(message, mAText);
      }
    }
  }

  public String encode(String code, String key) {
    String message = new String();
    if (asciiChoice.getSelectedItem() == "Forced ASCII" && edChoice.getSelectedItem() == "Decrypt") {
      code = strip(code);
    }
    for (int x = 0; x < code.length(); x++) {
      message += XOR(code.charAt(x), key.charAt(x % key.length()));
    }
    if (asciiChoice.getSelectedItem() == "Forced ASCII" && edChoice.getSelectedItem() == "Encrypt") {
      message = inflate(message);
    }
    return message;
  }

  public static void display(String message, TextArea TA) {
    for (int x = 0; x < message.length(); x++) {
      TA.append(message.substring(x, x+1));
      if (x % TA.getColumns() == 0 && x > 0)
	TA.append("\n");
    }
  }

  public static void displayWS(String message, TextArea TA) {
    int charcount = 0;
    for (int x = 0; x < message.length(); x++) {
      TA.append(message.substring(x, x+1));
      charcount++;
      if (charcount > TA.getColumns() && message.charAt(x) == ' ') {
	TA.append("\n");
	charcount = 0;
      }
    }
  }

  public static String toBinary(String message) {
    String bin = new String();
    for (int x = 0; x < message.length(); x++) {
      String s = Integer.toBinaryString (message.charAt(x));
      for (int y = s.length(); y < 8; y++) {
	s = "0" + s;
      }
      bin += s;
    }
    return bin;
  }

  public static String toASCII(String bin) {
    String message = new String();
    for (int x = 0; x < bin.length()/8; x++) {
      int sum = -128*Integer.parseInt(bin.substring(8*x, 8*x+1)) + 64*Integer.parseInt(bin.substring(8*x+1, 8*x+2)) +
	32*Integer.parseInt(bin.substring(8*x+2, 8*x+3)) + 16*Integer.parseInt(bin.substring(8*x+3, 8*x+4)) +
	8*Integer.parseInt(bin.substring(8*x+4, 8*x+5)) + 4*Integer.parseInt(bin.substring(8*x+5, 8*x+6)) +
	2*Integer.parseInt(bin.substring(8*x+6, 8*x+7)) + Integer.parseInt(bin.substring(8*x+7, 8*x+8));
      Character ch = new Character((char) sum);
      message += ch.toString();
    }
    return message;
  }

  public static String normalize(String str) {
    for (int x = 0; x < str.length(); x++) {
      if (str.charAt(x) != '0' && str.charAt(x) != '1') {
	str = str.substring(0, x) + str.substring(x+1);
	x--;
      }
    }
    return str;
  }

  public static String strip(String str) {
    for (int x = 0; x < str.length(); x++) {
      if (x % 4 == 0)
	str = str.substring(0, x) + str.substring(x+4, str.length());
    }
    return str;
  }

  public static String inflate(String str) {
    for (int x = 0; x < str.length(); x++) {
      if (x % 24 == 0) {
	str = str.substring(0, x) + "0010" + str.substring(x, str.length());
	x += 4;
      } else
	if (x % 24 == 8) {
	  str = str.substring(0, x) + "0100" + str.substring(x, str.length());
	  x+=4;
	} else {
	  if (x % 24 == 16) {
	    str = str.substring(0, x) + "0110" + str.substring(x, str.length());
	    x += 4;
	  }
	}
    }
    return str;
  }

  public static boolean isBinary(String str) {
    boolean b = true;
    for (int x = 0; x < str.length(); x++) {
      if (str.charAt(x) != '0' && str.charAt(x) != '1') {
	b = false;
	break;
      }
    }
    return b;
  }

  public static String XOR(char c1, char c2) {
    if (c1 == '0') {
      char[] CA = {c2};
      return new String(CA);
    }
    if (c2 == '0')
      return "1";
    return "0";
  }
}
