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


/**
 * Program that reads all files specified on the command line and 
 * tracks how many times each word read occurs.
 *
 * Can be configured to print only the top N words that occur most
 * frequently (20 by default).
 *
 * @author Robert C. Duvall
 */
public class Woof
{
    //////////////////////////////////////////////////////////////
    // Constants
    public static final int DEFAULT_COUNT = 20;
    public static final String PRINT_LIMIT = "-n";

    //////////////////////////////////////////////////////////////
    // State
    private Map myWords;
    private int myNumToPrint;
    private int myWordCount;


    //////////////////////////////////////////////////////////////
    // Constructors
    /**
     * Create a class that keeps track of the top 20 words 
     * occurring in a data set
     */
    public Woof ()
    {
        this(DEFAULT_COUNT);
    }

    /**
     * Create a class that keeps track of the top numToPrint
     * words occurring in a data set
     */
    public Woof (int numToPrint)
    {
        myWords = new TreeMap();
        myNumToPrint = numToPrint;
        myWordCount = 0;
    }


    //////////////////////////////////////////////////////////////
    // Public behavior
    /**
     * Track the counts of a collection of words
     */
    public void insert (Scanner input)
    {
        while (input.hasNext())
        {
            insert(stripPunc(input.nextString()));
        }
    }

    /**
     * Track a single word --- if never seen before, add it to collection;
     * else, update its count by one.
     */
    public void insert (String word)
    {
        Debug.print("***" + word);

        Counter value = (Counter)(myWords.get(word));
        if (value == null)
        {
            value = new Counter(0);
            myWords.put(word, value);
            Debug.print(" " + 1);
        }
        value.increment();
        myWordCount++;

        Debug.println();
    }

    /**
     * Display top N words based on how often each appears, greatest first
     * Note, this method actually determines the top N when called
     */
    public String toString ()
    {
        String result = "";

        result = "total words counted = " + myWordCount + "\n";            
        if (myWordCount > 0)
        {
                  Set entries = new TreeSet(new ReverseComparator(new ValueComparator()));
            entries.addAll(myWords.entrySet());

            int numPrinted = 0;
            Iterator iter = entries.iterator();
            while (iter.hasNext() && numPrinted < myNumToPrint)
            {
                Map.Entry entry = (Map.Entry)iter.next();
                result += entry.getKey() + "\t" + entry.getValue() + "\n";
                numPrinted++;
            }
        }

        return result;
    }


    //////////////////////////////////////////////////////////////
    // Private behavior
    /**
     * Strip off any leading and trailing punctuation in word
     */
    private String stripPunc (String word)
    {
        int start = 0;
        while (start < word.length() && ! Character.isLetter(word.charAt(start)))
        {
            start++;
        }

        int end = word.length();
        while (end > 0 && ! Character.isLetter(word.charAt(end - 1)))
        {
            end--;
        }

        // substring cannot handle crossed indicies
        if (start < end)    return word.substring(start, end).toLowerCase();
        else                    return "";
    }


    //////////////////////////////////////////////////////////////
    // Main method
    // This is where the program begins
    public static void main (String args[])
    {
        Debug.turnOff();
        int numToPrint = DEFAULT_COUNT;

        // find non-file command-line arguments
        int k = 0;
        if (args[k].toLowerCase().equals(PRINT_LIMIT))
        {
            numToPrint = FromString.toInt(args[k + 1]);
            k += 2;
        }

        Woof woofer = new Woof(numToPrint);
        // process each file
        for( ; k < args.length; k++)
        {
            woofer.insert(new Scanner(new File(args[k])));
        }

        // print results
        System.out.println(woofer);
    }
}
