// Arup Guha
// 10/28/07
// Solution CIS 3362 Homework #4

import java.io.*;
import java.util.*;

public class ArupCipher {
	
	final static int ENCRYPT = 1;
	final static int DECRYPT = 2;
	
	private String buffer;
	private String keyword;
	private int operation;
	
	// Creates an ArupCipher Object. fin must be a Scanner pointing to the
	// beginning of the desired input file, key is the keyword used for 
	// encryption, and function is the mode (encrypt or decrypt).
	public ArupCipher(Scanner fin, String key, int function) {
		
		keyword = key;
			
		buffer = "";
		while (fin.hasNext())
			buffer = buffer + fin.next();
			
		operation = function;
	}

	public static void main(String[] args) throws Exception{
		
		Scanner stdin = new Scanner(System.in);
		int option;
		String key, input, output;
		
		//read in user choice
		//1. cipher key
		System.out.println("Please input the cipher key:");
		key = stdin.next().toUpperCase();
		
		//2. encypt or decrypt
		System.out.println("Please input 1 for encryption and 2 for decryption:");
		option = stdin.nextInt();
		
		//3. input file name
		System.out.println("Please input the name of the input file:");
		input = stdin.next();
		
		//4. output file name
		System.out.println("Please input the name of the output file:");
		output = stdin.next();
		
		Scanner fin = new Scanner(new File(input));
		ArupCipher thisobj = new ArupCipher(fin, key, option);

		//encrypt or decrypt
		if (option == ENCRYPT) 
			thisobj.doEncrypt();
		else 
			thisobj.doDecrypt();
			
		//write to output file
		BufferedWriter fWriter = new BufferedWriter (new OutputStreamWriter
				(new FileOutputStream(output)));
		thisobj.output(fWriter);
		fWriter.close();		
	}

	// Encrypts the current state of the buffer with a Vigenere cipher based on the keyword.
	private void encryptVig() {
		
		char[] tmp = new char[buffer.length()];
		
		// Add the appropriate keyword letter to each corresponding buffer letter.
		for (int i=0; i<buffer.length(); i++)
			tmp[i] = (char)((buffer.charAt(i) - 'A' + keyword.charAt(i%keyword.length()) - 'A')%26 + 'A');
		
		// Change the buffer to reflect the Vigenere.
		buffer = new String(tmp);
	}
	
	// Decrypts the current state of the buffer with a Vigenere cipher based on the keyword.
	private void decryptVig() {
		
		char[] tmp = new char[buffer.length()];
		
		// Subtract the appropriate keyword letter from each corresponding buffer letter.
		for (int i=0; i<buffer.length(); i++)
			tmp[i] = (char)((buffer.charAt(i) - keyword.charAt(i%keyword.length()) + 26)%26 + 'A');
		
		// Change the buffer to reflect undoing Vigenere.
		buffer = new String(tmp);
	}
	
	// Encrypts the current state of the buffer with a column transposition only based on
	// the length of the keyword.
	private void encryptTrans() {
		
		// Pad with X's if necessary.
		while (buffer.length()%keyword.length() != 0)
			buffer = buffer + "X";
			
		char[] tmp = new char[buffer.length()];
		
		// Set up the number of rows and columns for the transposition grid.
		int numRows = buffer.length()/keyword.length();
		int numCols = keyword.length();
		
		// Switch rows and columns here...
		for (int col=0; col<numCols; col++) 
			for (int row=0; row<numRows; row++)
				tmp[col*numRows+row] = buffer.charAt(row*numCols+col);
			
		buffer = new String(tmp);
	}
	
	// Decrypts the current state of the buffer with a column transposition only based on
	// the length of the keyword.
	private void decryptTrans() {
			
		char[] tmp = new char[buffer.length()];
		
		// Set up the number of rows and columns for the transposition grid.
		int numRows = keyword.length();
		int numCols = buffer.length()/keyword.length();
		
		// Switch rows and columns here...
		for (int col=0; col<numCols; col++) 
			for (int row=0; row<numRows; row++)
				tmp[col*numRows+row] = buffer.charAt(row*numCols+col);
			
		buffer = new String(tmp);
	}
	
	// Here's the product cipher
	public void doEncrypt() {
		encryptTrans();
		encryptVig();
	}
	
	// Do the product cipher backwards, which means reversing the order.
	public void doDecrypt() {
		decryptVig();
		decryptTrans();
	}
	
	// Outputs the buffer to the file bw, writing new line characters every 60
	// characters.
	public void output(BufferedWriter bw) throws IOException {
		
		int i=0;
		
		// So long as we haven't outputted the whole string keep on going.
		while (i < buffer.length()) {
			
			// There are at least 60 characters left to output.
			if (i+60 <= buffer.length())
				bw.write(buffer.substring(i, i+60));
				
			// This is the last block to output.
			else
				bw.write(buffer.substring(i));
				
			// Move onto the next block.
			i += 60;
			bw.write("\n");
		}
	}
}
