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

/** Class and demo program for word prediction and word completion.<p>
*
*  Invocation:
*
* <pre>
*     PROMPT>java WordPredict file n
*
*     where file = a word+freq dictionary file
*           n = maximum number of predicted words
* </pre>
*
* Here is an example dialogue: (User input is underlined)
*
* <pre>
*     PROMPT><u>java WordPredict d1-wordfreq.txt 10</u>
*     Dictionary contains 64566 words
*     Most frequent 10 words...
*     the of and to a in that it is was
*     Enter word or word stem...
*     <u>t</u>
*     the to that this they their there them time two
*     <u>th</u>
*     the that this they their there them then these than
*     <u>the</u>
*     the they their there them then these themselves therefore theory
*     <u>psy</u>
*     psychological psychology psychiatric psychologists
*     <u>ja</u>
*     january james japan jack japanese jane jacket jackson jan jazz
*     <u>^z</u>
* </pre>
*
* @author Scott MacKenzie, 2002-2010
*/
public class WordPredict
{
   public static void main(String[] args) throws IOException
   {
      // check command line arguments
      if (args.length < 2)
      {
         System.out.println("usage: java WordPredict file n");
         System.out.println();
         System.out.println("where file = a word-frequency file");
         System.out.println("      n = number of candidate words to output");
         return;
      }

      // get name of dictionary file
      String file = args[0];
      
      

      // get maximum number of choices to display
      int max = Integer.parseInt(args[1]);

      // open stdin for keyboard input
      BufferedReader stdin =
         new BufferedReader(new InputStreamReader(System.in), 1);

	   // the heavy lifting is done in the WordPredict object
		WordPredict wp = new WordPredict(file);

		// output size of dictionary
      System.out.println("Dictionary contains " + wp.getSizeOfDictionary() + " words");

		// word prediction first		
		
		System.out.println("Most frequent " + max + " words...");
		String[] candidates = wp.getWords("", max);
		for (int i = 0; i < max; ++i)
			System.out.print(candidates[i] + " ");
		System.out.println();

		// word completion next

      String stem;
      System.out.println("Enter word or word stem...");
      while ((stem = stdin.readLine()) != null)
      {
			candidates = wp.getWords(stem, max);
			for (int i = 0; candidates != null && i < candidates.length; ++i)
				System.out.print(candidates[i] + " ");
			System.out.println();
      }
   }

	/** Construct a WordPredict object.
	* <p>
	* 
	* A WordPredict object is used for
	* word completion (given a word stem) or word prediction (given
	* an empty word stem).
	* <p>
	*
	* @param wordFreqFile a word-frequency dictionary file, for example,
	* <a href="d1-wordfreq.txt">d1-wordfreq.txt</a>.
	*/
	public WordPredict(String wordFreqFile) throws IOException
	{
      w = Word.loadDictionary(wordFreqFile);
      Arrays.sort(w, new WordByWord());
	}

	private Word[] w;

	/** Get an array of words completing the specified word stem.
	*
	* The array contains either <i>predicted words</i>, if the word stem is a
	* empty
	* string, or <i>completed words</i> if the word stem contains
	* at least one letter.  In either case, the list is sorted by word frequency.
	* <p>
	*
	* This method includes "discount" word prediction.  If the word stem is 
	* an empty string, the
	* list is simply the <code>max</cod> most frequent words in the 
	* dictionary.
	* <p>
	*
	* Note that the array may be less than <code>max</code>, depending on the 
	* word stem and the dictionary.
	* <p>
	*
	* @param stem a word stem
	* @param max maximum number of words to return
	* @return a string array containing a list of words based on the
	* word stem (see above), or <code>null</code> if there are no matches or if 
	* <code>stem</code> is null
	*/
	public String[] getWords(String stem, int max)
	{
		if (stem == null)
			return null;

		// word prediction: if stem = empty string, return top 'max' words
		else if (stem.equals(""))
		{
			Arrays.sort(w, new WordByFreq());
			String[] candidates = new String[max];
			for (int i = 0; i < max; ++i)
			{
				candidates[i] = w[i].getWord();
			}
			return candidates;
		} else // word completion: if stem != empty string, build candidate list
		{
			Arrays.sort(w, new WordByWord());
			String s2 = stem.substring(0, stem.length() - 1)
					 + (char)(stem.charAt(stem.length() - 1) + 1);

			// find position in array of word entered and s2
			int n1 = Arrays.binarySearch(w, new Word(stem, 0), new WordByWord());
			int n2 = Arrays.binarySearch(w, new Word(s2, 0), new WordByWord());

			// if either doesn't exit, that's OK, but use +ve index anyway
			if (n1 < 0) n1 = -n1 - 1;
			if (n2 < 0) n2 = -n2 - 1;

			//System.out.println("n1=" + n1 + ", n2=" + n2);
			if (n1 == n2)
			{
				//System.out.println("No match!");
				return null;
			}

			String[] candidates = null;
			// proceed only if there are some choices for the entered word
			if (n2 - n1 > 0)
			{
				// make a new Word array for all choices beginning with stem
				Word[] w2 = new Word[n2 - n1];
				int i, j;
				for (i = n1, j = 0; i < n2; i++, j++)
				{
					w2[j] = w[i];
				}

				// sort the new array by frequency
				Arrays.sort(w2, new WordByFreq());

				int n = w2.length > max ? max : w2.length;
				candidates = new String[n];
				for (i = 0; i < n; ++i)
					candidates[i] = w2[i].getWord();
			}
			return candidates;
		}
	}

	/** Return the size of the dictionary.
	*
	* @return the size of the dictioary
	*/
	public int getSizeOfDictionary()
	{
		return w.length;
	}
}