import java.io.*;
import java.util.*;

// Programmer: Erik Krohn
// Please report errors to Erik or the professor
// Date: 3-31-2006

public class SpellCheck {
	//private Dictionary dict=new Dictionary(10501);
	private Dictionary smallDict = new Dictionary(5003);
	private Dictionary largeDict = new Dictionary(10000);

	public SpellCheck() throws Exception {
		getWords();  //puts words in dictionary
		check(promptUser());  //prompts user for file name and does work
	}

	public void getWords() throws Exception {
		//try{
			BufferedReader fin = new BufferedReader(new FileReader("words.dat"));
			String word;
			for(int i = 0; i < 500; i++)
			{	if( (word=fin.readLine()) != null )
					smallDict.insert(new Record(word));  //insert word into dictionary
			}
			while((word=fin.readLine())!=null){
				largeDict.insert(new Record(word));  //insert word into dictionary
			}
		//} catch(Exception e){
		//	System.out.println(e);
		//}
		//insert a and i into dictionary
		smallDict.insert(new Record("a"));
		smallDict.insert(new Record("i"));
	}

	//read in file names
	public String getInput(){
		try{
			BufferedReader fin = new BufferedReader(new InputStreamReader(System.in));
			return fin.readLine();
		} catch(Exception e){
			System.out.println(e);
		}
		return null;
	}

	//ask user for files
	public String promptUser(){
		System.out.print("Enter source file to spell check: ");
		return getInput();
	}

	public Record searchDictionary(String word)
	{
		Record result = null;
		if( (result = smallDict.search(word)) != null )
			return result;
		else
			return largeDict.search(word);
	}

	public void check(String file) throws Exception {
		try{
			BufferedReader fin = new BufferedReader(new FileReader(file));
			PrintWriter fout = new PrintWriter(new FileWriter(file+".out"));
			String line;
			String word = "";
			String newLine = "";
			char ch;

			//Get each line of file
			while((line=fin.readLine())!=null){
				/***************
				 *  The basic algorithm for this is:
				 *    0.  Keep track of the newLine to print out
				 *    1.  Read in a character until you have a word.  Check if word is in the dictionary, if in the dictionary
				 *		  but needs a replacement, add replacement to newLine otherwise add work to newLine along with ch (to correspond to ' or , or . etc.)
				 *    2.  If word is not in dictionary.
				 *		  a.  replace - add replaced word to newLine
				 *		  b.  replaceAll - add replacement to dictionary with word (as a lower case) as the key
				 *		  c.  ignore - add original word to newLine
				 *		  d.  ignoreAll - add word to dictionary and add to newLine
				 *	  	  e.  abort - delete file and quit
				 *	  3.  Print off newLine and repeat until no new lines
				******************/
				for (int i = 0; i <= line.length(); i++)
				{
					ch = ' ';
					if(i < line.length())
						ch = line.charAt(i);

					// word is being constructed
					if (Character.isLetter(ch) || (ch == '\047' && word.length() != 0))
					{
						word = word + ch;
					}
					else if (word.length() != 0)
					{
						//word not found in dictionary as is, need to change to lowercase and search
						Record found = searchDictionary(word.toLowerCase());
						if (found != null)
						{
							if (found.replacement == null)
							{
								//add work to line
								newLine += word;
							}
							else
							{
								//add replacement to newline
								newLine += found.replacement;
							}
						}
						else
						{
							//word not in dictionary at all
							int redo = 0;
							while (redo == 0)
							{
								String x = alert(word).toLowerCase();
								if (x.length() > 0)
								{
									char toDo = x.charAt(0);
									redo = 1;  //only for incorrect input
									switch (toDo)
									{
										case 'r':
											//replace word
											//System.out.print("Please enter a replacement > ");
											// Sriram: word needs to be turned into lower case
											// before we get replacement words
											word = getReplacementWord(word.toLowerCase());
											newLine += word; //replace wrong word 
											break;
										case 'i':
											//do nothing, simply ignoring word
											newLine += word;
											break;
										case 'n':
											//ignore all
											//add word to our dictionary
											Record temp = new Record(word.toLowerCase());
											largeDict.insert(temp);
											newLine += word;
											break;
										case 'a':
											//aborting
											fout.close();
											fin.close();
											new File(file + ".out").delete();
											System.out.println("Aborted");
											System.exit(0);
										case 'p':
											//replace all, add to dictionary
											String replace;
											//System.out.print("Please enter a replacement > ");
											// Sriram: word needs to be turned into lower case
											// before we get replacement word
											replace = getReplacementWord(word.toLowerCase());
											temp = new Record(word.toLowerCase());
											temp.replacement = replace; //create a new record with word as the key
											largeDict.insert(temp);
											newLine += replace;
											break;
										default:
											System.out.println("Invalid input, please choose correctly!");
											redo = 0;
									}
								}
								else
									System.out.println("Invalid input, please choose correctly!");
							} // end of while-redo

						} // end of if-word-not-found
						//reset word and add character that followed the word
						word = "";
						if(i < line.length())
							newLine += ch;
					}
					else
					{
						//add character to newLine.  for example, "I went to the store, and bought a beverage."  
						//This is so we include the space after the comma
						if(i < line.length())
							newLine += ch;
					}
					
				}	
				//print off our line
				fout.print(newLine);
				fout.print("\n");
				newLine = "";
			}
			fout.close();
			fin.close();
		} catch(Exception e){
			System.out.println(e);
		}
	}

	public String alert(String word){
		//Offer options for unknown word
		System.out.println("\nThe word: \""+word+"\" is unknown.");
		System.out.print("Do you want to replace (R), replace all (P), ignore (I), ignore all (N), or abort (A)? > ");
		try{
			return getInput();
		}
		catch(Exception e){};
		return null;
	}

	public String getReplacementWord(String word)
	{
		String [] words = getWords(word);
		int i;
		for(i = 0; i < words.length; i++)
		{
			if(words[i] == null) break;
			System.out.println("("+i+") "+words[i]);
		}

		System.out.println("("+i+") Use my replacement");

		boolean done = false;
		int num = 0;
		while(!done)
		{
			String reply = getInput();
			// Sriram: reply could be 0 through 10. So reading a char
			// is not enough - an int has to be read
			num = Integer.parseInt(reply);

			if( num >= 0 && num <= i)
			{
				done = true;
			}
			else
			{
				System.out.println("Invalid entry: try again.");
			}
		}

		// Sriram: If num equals i then the user is going to enter
		// her own rerplacement; otherwise we pick a replacement from
		// the generated replacement
		String result;
		if(num == i)
		{
			System.out.println("Enter your replacement word: ");
			result = getInput();
		}
		// Added by Sriram
		else
			result = words[num];

		return result;
	}

	public boolean isWord(String s)
	{
		if( smallDict.search(s) == null )
			if( largeDict.search(s) == null )
				return false;
		return true;
	}

	public boolean contains(String [] array, String s)
	{
		for(int i = 0; i < array.length; i++)
		{
			if(array[i] == null) return false;
			if(s.equals(array[i])) return true;
		}

		return false;
	}

	public String [] getWords(String word)
	{
		int replacementCount = 0;
		String [] replacements = new String[10];
		String [] onehop = new String[numNeighbors(word.length())];
		String [] twohop;

		fillArray(onehop, word, 0);

		// Scan the one hops array before wasting time on two hops
		for(int i = 0; i < onehop.length; i++)
		{
			if( isWord(onehop[i]) && !contains(replacements, onehop[i]))
			{
				replacements[replacementCount++] = onehop[i];
				if(replacementCount == 10)
					return replacements;
			}
		}

		// for each word in the one hops array, get its neighbors and
		// check them before getting the neighbors of any other words.
		for(int i = 0; i < onehop.length; i++)
		{
			twohop = new String[numNeighbors(onehop[i].length())];
			fillArray(twohop, onehop[i], 0);
			for(int j = 0; j < twohop.length; j++)
			{
				if( isWord(twohop[j]) && !contains(replacements, twohop[j]))
				{
					replacements[replacementCount++] = twohop[j];
					if(replacementCount == 10)
						return replacements;
				}
			}
		}

		return replacements;
	}

	public int fillArray(String [] S, String word, int index)
	{
		String [] S1 = extraLetter(word);
		String [] S2 = swapLetter(word);
		String [] S3 = substituteLetter(word);
		String [] S4 = missingLetter(word);

		for(int i = 0; i < S1.length; i++)
			S[index++] = S1[i];
		for(int i = 0; i < S2.length; i++)
			S[index++] = S2[i];
		for(int i = 0; i < S3.length; i++)
			S[index++] = S3[i];
		for(int i = 0; i < S4.length; i++)
			S[index++] = S4[i];

		return index;
	}

	public int numNeighbors(int n)
	{
		//n-1 + n + (n+1)*26 + n*26;
		return 54*n + 25;
	}

	public String [] extraLetter(String word)
	{
		String [] result = new String[(word.length()+1)*26];

		for(int i = 0; i < word.length()+1; i++)
		{
			for(int j = 0; j < 26; j++)
			{
				result[(i*26)+j] = word.substring(0,i) + (char)(j + 'a')
						+ word.substring(i);
			}
		}

		return result;
	}

	public String [] swapLetter(String word)
	{
		String [] result = new String[word.length()-1];

		for(int i = 0; i < word.length()-1; i++)
		{
				result[i] = word.substring(0,i) + word.charAt(i+1) + word.charAt(i)
						+ word.substring(i+2);
		}

		return result;
	}

	public String [] substituteLetter(String word)
	{
		String [] result = new String[26 * word.length()];

		for(int i = 0; i < word.length(); i++)
		{
			for(int j = 0; j < 26; j++)
			{
				result[26*i + j] = word.substring(0,i) + (char)('a' + j)
						+ word.substring(i+1);
			}
		}

		return result;
	}

	public String [] missingLetter(String word)
	{
		String [] result = new String[word.length()];

		for(int i = 0; i < word.length(); i++)
		{
				result[i] = word.substring(0,i) + word.substring(i+1);
		}

		return result;
	}

	public static void main(String[] args) throws Exception {
		new SpellCheck();
	}
}
