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

/**
 * This class was created for Java Network Programming.
 * It implements a basic version of the unix command's "tail" program.
 *
 * @author JJ Johns
 * @version 1.0
 **/

public class tail {

    /**
     * This field denotes if the call to tail should block and wait, or not.
     **/

    private static boolean threaded = false;

    /**
     * This is the number of lines that tail should print.
     **/

    private static int num_lines = 10;


    /**
     * This is the mail function, which parses command line parameters.
     *
     * This will set flags for -f, which will wait for more data to appear
     * at the end of the file, and print it when it comes.
     * This will also accept -n, which tells the program how many lines from
     * the end to print out.
     *
     * @param argv The command line arguments.
     **/

    public static void main(String argv[]) {

	String filename = null;
	// parse the command line
	try {
	    for( int i=0; i<argv.length; i++ ) {
		// arguments
		if (argv[i].equals("-f")) {
		    threaded=true;
		    continue;
		}
		if (argv[i].equals("-n")) {
		    i++;
		    try {
			num_lines = Integer.parseInt(argv[i]);
		    }
		    catch (NumberFormatException nfe) { 
			throw new IllegalArgumentException();
		    }

		    continue;
		}
		filename = argv[i];
	    }
	}
	catch (IllegalArgumentException iae) {
	    System.err.println("Illegal Usage: ");
	    System.err.println("java tail [-n number of lines] [-f] filename");
	    System.exit(0);
	}
	tail t = new tail(filename);
    }

    /**
     * This is the constructor for tail.
     *
     * @param filename The name of the file to print the ending of.
     **/

    public tail(String filename) {

	File theFile;

	BufferedReader br = null;
	LinkedList lines = new LinkedList();
	String temp = null;

      	try {
	    theFile = new File(filename);
	    br = new BufferedReader(new FileReader(theFile));
	
	    // read the entire file
	    while (true) {
		temp= br.readLine();
		if (temp != null) {

		    // push the line on the queue, and remove the first
		    // line if necessary.
		    lines.addLast(temp);
		    if (lines.size()>num_lines)
			lines.remove( lines.getFirst());
		}
		else
		    break;
	    }	    
	}
	catch (FileNotFoundException fnfe) {
	    System.err.println("The file " + filename + " was not found.");
	    System.exit(0);
	    try {
		br.close();
	    } catch (IOException ioe) { }
	}
	catch(IOException ioe) {
	    System.out.println("Error reading from file " + filename );
	    System.exit(0);	    
	    try {
		br.close();
	    } catch (IOException ioe2) { }      
	}

	// print out the specified number of lines.
	for (int i=0; i<lines.size(); i++)
	    System.out.println(lines.get(i));
	
	// If the -f command was given, start a thread to wait for the command
	// line signal to terminte, and check the file ever 200 ms for updates.

	if (threaded) {

	    // starts up the thread to read from standard input.
	    Thread t = new Thread() {
		    public void run() {
			try {
			    System.in.read();
			}
			catch(IOException ioe) {
			    System.err.println("Error reading from standard input.");
			    System.exit(0);	    
			}

		    }

	    };
	    t.start();


	    while (t.isAlive()) {

		try {
		    temp = br.readLine();
		}
		catch(IOException ioe) {} // this will happen ever time, but it
		// doesn' really matter

		if (temp != null) // test to make sure we actually got something.
		    System.out.println(temp);

		try {
		    Thread.sleep(200);
		} catch (InterruptedException ie) { }

	    }
		
	    // always close the stream
	    try {
		br.close();
	    } catch (IOException ioe) { }
	}

    }
}

