import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;

/**
 * A suggested solution to Assignment #1
 * CS 146
 * Summer 2015
 * 
 * @author Ron Mak
 */
public class Searcher2
{
    // Names to search for.
    private static final String BORIS  = "Boris Drubetskoy";
    private static final String JOSEPH = "Joseph Bazdeev";
    private static final String MAKAR  = "Makar Alexeevich";
    
    private static final String NAMES[] = {
    	BORIS, JOSEPH, MAKAR
    };
    
    private static boolean firstIteration = true;
   
    private String firstNames[], lastNames[];
    private int    prevLineIndexes[];
    
    private boolean firstName = true;    
    private Scanner input;
    
    private Searcher2()
    {
    	firstNames      = new String[NAMES.length];
    	lastNames       = new String[NAMES.length];
    	prevLineIndexes = new int[NAMES.length];
    	
    	int i = 0;
    	for (String name : NAMES) {
    		String parts[] = name.split(" ");
    		firstNames[i]  = parts[0];
    		lastNames[i]   = parts[1];
    		
    		prevLineIndexes[i] = -1;
    		i++;
		}
    }

    /**
     * Search for names using String.index().
     * Also find names that span two lines.
     * For optimal performance, the while loop should run
     * as fast as possible.
     * @throws IOException if an I/O error occurs
     */
    private void search() throws IOException
    {
    	int lineNumber = 0;
        String line;
        
        // Loop until the end of the input file.
        while (input.hasNextLine()) {
        	line = input.nextLine().trim();

        	for (int i = 0; i < NAMES.length; i++) {
        		String name = NAMES[i];
        		int index = -1;
        		
        		if (prevLineIndexes[i] > 0) {
        			String last = lastNames[i];
        			index = line.indexOf(last);
        			if (index == 0) {
        				printName(lineNumber-1, prevLineIndexes[i], name);
        			}
         			prevLineIndexes[i] = -1;
        		}
        		
        		index = line.indexOf(name);
        		if (index >= 0) {
        			printName(lineNumber, index, name);
        		}
        		else {
        			String first = firstNames[i];
        			index = line.indexOf(first);
        			if ((index >= 0) && (index + first.length() >= line.length())) {
        				prevLineIndexes[i] = index;
        			}
        		}
        	}
        	
        	lineNumber++;
        }
    }
    
    /**
     * Print a name that was just found.
     */
    private void printName(int lineNumber, int index, String name)
    {
    	if (!firstIteration) return;
    	
        // Header line.
        if (firstName) {
            System.out.printf("%5s  %8s  %s\n", 
                              "LINE", "POSITION", "NAME");
            firstName = false;
        }
        
        System.out.printf("%5d  %8d  %s\n", lineNumber+1, index+1, name);
    }    
    
    /**
     * Start to search an input file. Open the input file,
     * do the search, and then close the file.
     * @param filePath path of the input file
     * @throws IOException if an I/O error occurred
     */
    private void go(String filePath) throws IOException
    {
        try {
            input = new Scanner(new File(filePath));
            search();
        }
        catch (FileNotFoundException ex) {
            System.out.println("*** ERROR: File not found: " + filePath);
        }
        finally {
            if (input != null) input.close();
        }
    }
    
    /**
     * Main
     * @param args Arg 0: input file path; 
     *             Arg 1: processor speed in GHz
     *             Arg 2: (optional) number of iterations (default 1)
     */
    public static void main(String args[])
    {
        ArrayList<Long> times = new ArrayList<Long>();  // execution times
                    
        if (args.length < 2) {
            System.out.println("Usage: java Searcher2 <file> <speed> <iterations>");
            System.out.println("where <file> is the file to search,");
            System.out.println("      <speed> is your processor speed in GHz (ex: 2.3)");
            System.out.println("      <iterations> is the number of iterations (default 1)");
            System.exit(0);
        }

        try {
            double speed = Double.parseDouble(args[1]);
            int iterations = args.length > 2
                                 ? Integer.parseInt(args[2])
                                 : 1;
            
            // Do a complete search each time through the loop.
            // Time how long each iteration took.
            for (int i = 1; i <= iterations; i++) {
                long start = System.currentTimeMillis();
                
                // Kick off the searcher.
                (new Searcher2()).go(args[0]);

                long elapsed = System.currentTimeMillis() - start;
                times.add(elapsed);
                if (firstIteration) System.out.println();
                System.out.printf("Iteration %3d: %3d ms\n", 
                                  i, elapsed);
            
                firstIteration = false;
            }
            
            printStats(times, speed);
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    
    /*
     * Print the timing statistics from each iteration.
     * @param the list of times
     */
    private static void printStats(ArrayList<Long> times, double speed)
    {
        Collections.sort(times);  // sort the times
        
        int size = times.size();
        int half = size/2;        // midpoint index for the median time
        
        long   minTime = times.get(0);
        long   maxTime = times.get(size-1);
        double median  = half << 1 < size 
                            ? times.get(half)
                            : (times.get(half-1) + times.get(half))/2.0;
        
        System.out.printf("\nProcessor speed: %3.1f GHz\n\n", speed);
        System.out.printf("Mininum time: %3d   ms, performance %6.1f\n", 
                          minTime, minTime*speed);
        System.out.printf("Maximum time: %3d   ms, performance %6.1f\n", 
                          maxTime, maxTime*speed);
        System.out.printf("Median  time: %5.1f ms, performance %6.1f\n", 
                          median, median*speed);
    }
}