package TailCommand;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.io.File;

/**
 *
 * @author Tim Sparg
 */
public class TailCommand {

    //private File[] files = new File[]{new File("waffle.txt"), new File("goffle.txt"), new File("bigWaffle.txt")};
    private File[] files;
    private boolean printHeaders = false;
    private LinkedList linesToPrint;
    private int numToPrint = 10;
    private TailAction currentAction;
    private boolean follow = true;
    private double sleepTime = 1;
    private List<TailFile> tailFileList;
    private String lastFilename = "";

    private enum TailAction {

        BYTES, LINES
    };

    public void execute(String [] args) {
        files = new File [args.length];
        for (int i = 0; i < args.length; i++) {
            String string = args[i];
            files[i] = new File(string);
        }
        boolean isOk = true;

        if (files.length > 1) {
            printHeaders = true;
        }

        sleepTime = 5.5;

        currentAction = TailAction.BYTES;

        if (currentAction == TailAction.BYTES) {
            linesToPrint = new LinkedList<String>();
        } else {
            linesToPrint = new LinkedList<Integer>();
        }

        tailFileList = new ArrayList<TailFile>();

        for (File file : files) {
            try {
                tailFileList.add(new TailFile(file));
            } catch (FileNotFoundException ex) {
                Logger.getLogger(TailCommand.class.getName()).log(Level.SEVERE, null, ex);
                isOk = false;
            }
        }

        if (isOk) {
            do {
                for (TailFile tail : tailFileList) {
                    if (!tail.tailFile()) {
                        isOk = false;
                        break;
                    }
                }
                if (follow) {
                    try {
                        Thread.sleep((int) sleepTime * 1000);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(TailCommand.class.getName()).log(Level.SEVERE, null, ex);
                        isOk = false;
                    }
                }
            } while (follow && isOk);
        }

    //"line.separator"

    }

    public static void main(String[] args) {

        new TailCommand().execute(args);
    }

    private class TailFile {

        private final RandomAccessFile raFile;
        private long lastFileSize = 0;
        private final String fileName;
        private int numStartPosFromEnd = 1024;

        public TailFile(File file) throws FileNotFoundException {
            this.raFile = new RandomAccessFile(file, "r");
            this.fileName = file.getName();
        }

        public boolean tailFile() {
            boolean isOk = true;
            try {
                if (lastFileSize != raFile.length()) {
                    if (printHeaders && ((!lastFilename.equals(fileName)))) {
                        if ((lastFilename != null)) {
                            System.out.println();
                        }
                        System.out.print("------> ");
                        System.out.print(fileName);
                        System.out.println(" <------");
                        lastFilename = fileName;
                    }

                    switch (currentAction) {
                        case LINES:
                            tailLines();
                            break;
                        case BYTES:
                            tailBytes();
                            break;
                    }
                    lastFileSize = raFile.length();
                }
            } catch (IOException ex) {
                Logger.getLogger(TailCommand.class.getName()).log(Level.SEVERE, null, ex);
                isOk = false;
            }
            return isOk;
        }

        private void tailLines() throws IOException {
            if (raFile.length() - numStartPosFromEnd > 0) {
                if (lastFileSize == 0) {
                    raFile.seek(raFile.length() - numStartPosFromEnd);
                } else {
                    raFile.seek(lastFileSize);
                }
            }

            numStartPosFromEnd += numStartPosFromEnd;
            while (true) {
                String line = raFile.readLine();
                if (line == null) {
                    if (linesToPrint.size() != numToPrint) {
                        if (lastFileSize == 0) {
                            raFile.seek(raFile.length() - numStartPosFromEnd);
                            linesToPrint.clear();
                            continue;
                        }
                    }
                    break;
                }
                linesToPrint.add(line);
                if (linesToPrint.size() > numToPrint) {
                    linesToPrint.remove();
                }
            }

            while (!linesToPrint.isEmpty()) {
                System.out.println(linesToPrint.remove());
            }
        }

        private void tailBytes() throws IOException {
            byte[] printBytes = null;
            if (raFile.length() - numToPrint > 0) {
                if (lastFileSize == 0) {
                    raFile.seek(raFile.length() - numToPrint);
                    printBytes = new byte[numToPrint];
                } else {
                    raFile.seek(lastFileSize);
                    printBytes = new byte[(int)(raFile.length() - lastFileSize)];
                }
            }

            raFile.readFully(printBytes);
            for (byte b : printBytes) {
                System.out.print((char) b);
            }
        }
    }
}
