//for cis3362
//By: Hua Zhang
//Date: October 04 2006

import java.io.*;

public class Playfair {
	
	private static char grid[][];
	private static int rowInfo[]; //store the row of each character
	private static int colInfo[]; //stroe the col of each character
	
	public static void main(String[] args) throws Exception{
		int iDirection; //0: encypt, 1: decrypt;
		String strKey=""; //cipher key
		String inFileName;
		String outFileName;
		String strMsg;
		String strOutMsg;
		String input;
		BufferedReader stdin = new BufferedReader (new InputStreamReader(System.in));
		
		//read in user choice
		//1. cipher key
		
		boolean done = false;
		while (!done) {
		
			System.out.println("Please input the cipher key:");
			strKey = stdin.readLine().toUpperCase();
			if (notValid(strKey))
				System.out.println("Sorry, that is not a valid key. Please try again.");
			else {
				strKey = trim(strKey);
				done = true;
			}
		}
		
		//2. encypt or decrypt
		System.out.println("Please input 0 for encryption and 1 for decryption:");
		input = stdin.readLine();
		iDirection = Integer.parseInt(input);
		
		//3. input file name
		System.out.println("Please input the name of the input file:");
		inFileName = stdin.readLine();
		
		//4. output file name
		System.out.println("Please input the name of the output file:");
		outFileName = stdin.readLine();
		
		//form the matrix
		//assume key doesn't have repeated letters
		createGrid(strKey);
		
		//read in the message
		BufferedReader fReader = new BufferedReader (new InputStreamReader
				(new FileInputStream(inFileName)));
		strMsg="";
		while ((input = fReader.readLine()) != null) strMsg +=input;
		fReader.close();

		//encrypt or decrypt
		if (iDirection == 0) {
		//encrypt
			strMsg = formatMessage(strMsg);
			strOutMsg = encrypt(strMsg);
		} else {
		//decrypt
			strOutMsg = decrypt(strMsg);
		}

		//write to output file
		BufferedWriter fWriter = new BufferedWriter (new OutputStreamWriter
				(new FileOutputStream(outFileName)));
				
		// Put newline chars every 60...
		for (int i=0; i<(strOutMsg.length()+59)/60; i++)
			fWriter.write(strOutMsg.substring(60*i,Math.min(60*i+60,strOutMsg.length()))+"\n");
		fWriter.close();
	}

	private static void createGrid(String key) {
		
		grid = new char[5][5];
		rowInfo = new int[100];
		colInfo = new int[100];
		int i,k;
		int row, col;
		char ch;
		
		//add key to the matrix
		for (i=0;i<key.length();i++) {
			ch = key.charAt(i);
			row = i/5;
			col = i%5;
			grid[row][col] = ch;
			rowInfo[(int)ch] = row;
			colInfo[(int)ch] = col;
		}
		
		//add rest characters to the matrix
		k = key.length()-1;
		
		for (i=65;i<=90;i++) {
			
			//if i != j and not in the key
			if ((i != (int)('J')) && (key.indexOf(i) == -1)) {
				k++;
				row = k/5;
				col = k%5;
				grid[row][col] = (char)i;
				rowInfo[i] = row;
				colInfo[i] = col;
			}
		}
	}
	
	private static String formatMessage(String msg) {
		String strTmp;
		StringBuffer newMsg = new StringBuffer();
		int i;
		
		//replace 'j' with 'i'
		strTmp = msg.replace('J', 'I');
		
		//add 'x' between repeated characters
		i=0;
		while (i<strTmp.length()-1){
			
			if (strTmp.charAt(i) != strTmp.charAt(i+1)) {
				
				//no need to add 'x'
				newMsg.append(strTmp.charAt(i));
				newMsg.append(strTmp.charAt(i+1));
				i=i+2;
			} 
			else {
				
				//add 'x', or 'z', if x is the double character.
				newMsg.append(strTmp.charAt(i));
				if (strTmp.charAt(i) != 'X') 	
					newMsg.append('X');
				else
					newMsg.append('Z');
				i=i+1;
			}
		}
		
		//if there's one character left
		if ((i == (strTmp.length()-1))) {
			newMsg.append(strTmp.charAt(i));
			
			// add 'x' or 'z', if x is the double character.
			if (strTmp.charAt(i) != 'X')
				newMsg.append('X');
			else
				newMsg.append('Z');
				
		}
		
		return newMsg.toString();
	}
	
	private static String encrypt(String msg) {
		StringBuffer outMsg = new StringBuffer();
		int i;
		int row1, row2, col1, col2;
		char ch1, ch2, nch1, nch2;
		
		i=0;
		while (i<msg.length()) {
			
			//read two characters
			ch1 = msg.charAt(i);
			ch2 = msg.charAt(i+1);
			
			//find row and col
			row1 = rowInfo[(int)ch1];
			row2 = rowInfo[(int)ch2];
			col1 = colInfo[(int)ch1];
			col2 = colInfo[(int)ch2];
			
			//find new characters
			if (row1 == row2) {
				
				//replace with the character behind it
				nch1 = grid[row1][(col1+1)%5];
				nch2 = grid[row1][(col2+1)%5];
			} 
			else if (col1 == col2) {
				
				//replace with the character below it
				nch1 = grid[(row1+1)%5][col1];
				nch2 = grid[(row2+1)%5][col1];
			} 
			else {
				
				//on diagonal
				nch1 = grid[row1][col2];
				nch2 = grid[row2][col1];
			}
			
			//append to output message
			outMsg.append(nch1);
			outMsg.append(nch2);
			i = i + 2;
		}
		
		return outMsg.toString();
	}
	
	private static String decrypt(String msg) {
		
		StringBuffer outMsg = new StringBuffer();
		int i;
		int row1, row2, col1, col2;
		char ch1, ch2, nch1, nch2;
		
		i=0;
		while (i<msg.length()) {
			
			//read two characters
			ch1 = msg.charAt(i);
			ch2 = msg.charAt(i+1);
			
			//find row and col
			row1 = rowInfo[(int)ch1];
			row2 = rowInfo[(int)ch2];
			col1 = colInfo[(int)ch1];
			col2 = colInfo[(int)ch2];
			
			//find new characters
			if (row1 == row2) {
				
				//replace with the character before it
				nch1 = grid[row1][(col1+4)%5];
				nch2 = grid[row1][(col2+4)%5];
				
			} 
			else if (col1 == col2) {
				
				//replace with the character above it
				nch1 = grid[(row1+4)%5][col1];
				nch2 = grid[(row2+4)%5][col1];
				
			} 
			else {
				
				//on diagonal
				nch1 = grid[row1][col2];
				nch2 = grid[row2][col1];
			}
			
			//append to output message
			outMsg.append(nch1);
			outMsg.append(nch2);
			i = i + 2;
		}
		
		return outMsg.toString();
	}	
	
	// Returns true iff strKey has a non-uppercase character.
	public static boolean notValid(String strKey) {
		
		// Check each letter.
		for (int i=0; i<strKey.length(); i++)
			if (strKey.charAt(i) < 'A' || strKey.charAt(i) > 'Z')
				return true;
				
		return false; // All were uppercase chars.
	}
	
	// Returns a string equivalent to input with all duplicate characters
	// removed. (The duplicates removed are the ones that appear later in 
	// the input.
	public static String trim(String input) {
		
		char[] tmp = input.toCharArray();
		char[] ans = new char[input.length()];
		
		// Index into the new array, ans.
		int i = 1; 
		
		// First char must be unique.
		ans[0] = tmp[0];
		
		// Loop through the whole original input.
		for (int j=1; j<tmp.length; j++) {
			
			// Look to see if this new letter is already in the keyword.
			int k;
			for (k=0; k<j; k++)
				if (ans[k] == tmp[j])
					break;
					
			// If so, stop, don't add it!
			if (k < j) continue;
			
			// We add the letter to our keyword.
			ans[i] = tmp[j];
			i++;
		}
		
		// Resize the array.
		char[] finalans = new char[i];
		for (int j=0; j<i; j++)
			finalans[j] = ans[j];
		
		return new String(finalans);
	}
}