/*
 * bogoparser - A simple logfile analyzer for bogobot.
 * 
 * Bogoparser.java
 * 2004 The I2P Project
 * http://www.i2p.net
 * This code is public domain.
 */

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author hypercubus
 * @version 0.4
 */
public class Bogoparser {

    private static void displayUsageAndExit() {
        System.out.println("\r\nUsage:\r\n\r\n    java Bogoparser [--by-duration] <logfile>\r\n");
        System.exit(1);
    }

    public static void main(String[] args) {

        Bogoparser bogoparser;

        if (args.length < 1 || args.length > 2)
            displayUsageAndExit();

        if (args.length == 2) {
            if (!args[0].equals("--by-duration"))
                displayUsageAndExit();
            bogoparser = new Bogoparser(args[1], true);
        }

        if (args.length == 1)
            bogoparser = new Bogoparser(args[0], false);
    }

    private Bogoparser(String logfile, boolean sortByDuration) {

        ArrayList sortedSessions;

        if (sortByDuration) {
            sortedSessions = sortSessionsByDuration(calculateSessionDurations(sortSessionsByTime(readLogfile(logfile))));
            formatAndOutputByDuration(sortedSessions);
        } else {
            sortedSessions = calculateSessionDurations(sortSessionsByQuitReason(sortSessionsByNick(sortSessionsByTime(readLogfile(logfile)))));
            formatAndOutput(sortedSessions);
        }
    }

    private ArrayList calculateSessionDurations(ArrayList sortedSessionsByQuitReasonOrDuration) {

        ArrayList calculatedSessionDurations = new ArrayList();

        for (int i = 0; i+1 < sortedSessionsByQuitReasonOrDuration.size(); i += 2) {

            String   joinsEntry       = (String) sortedSessionsByQuitReasonOrDuration.get(i);
            String[] joinsEntryFields = joinsEntry.split(" ");

            String  quitsEntry = (String) sortedSessionsByQuitReasonOrDuration.get(i+1);
            Pattern p          = Pattern.compile("^([^ ]+) [^ ]+ ([^ ]+) (.*)$");
            Matcher m          = p.matcher(quitsEntry);

            if (m.matches()) {

                String currentJoinTime             = joinsEntryFields[0];
                String currentNick                 = m.group(2);
                String currentQuitReason           = m.group(3);
                String currentQuitTime             = m.group(1);
                long   joinsTimeInMilliseconds;
                long   quitsTimeInMilliseconds;
                long   sessionLengthInMilliseconds;
                
                joinsTimeInMilliseconds = Long.parseLong(currentJoinTime);
                quitsTimeInMilliseconds = Long.parseLong(currentQuitTime);
                sessionLengthInMilliseconds = quitsTimeInMilliseconds - joinsTimeInMilliseconds;
                
                String hours   = "" + sessionLengthInMilliseconds/1000/60/60;
                String minutes = "" + (sessionLengthInMilliseconds/1000/60)%60;

                if (hours.length() < 2)
                    hours = "0" + hours;

                if (hours.length() < 3)
                    hours = "0" + hours;

                if (minutes.length() < 2)
                    minutes = "0" + minutes;

                int    columnPadding       = 19-currentNick.length();
                String columnPaddingString = " ";

                for (int j = 0; j < columnPadding; j++)
                    columnPaddingString = columnPaddingString + " ";

                calculatedSessionDurations.add(sessionLengthInMilliseconds + " " + currentNick + columnPaddingString + " online " + hours + " hours " + minutes + " minutes " + currentQuitReason);
            } else {
                System.out.println("\r\nError: Unexpected entry in logfile: " + quitsEntry);
                System.exit(1);
            }
        }
        return calculatedSessionDurations;
    }

    private void formatAndOutput(ArrayList sortedSessions) {

        String quitReason = null;

        for (int i = 0; i < sortedSessions.size(); i++) {

            String  entry = (String) sortedSessions.get(i);
            Pattern p     = Pattern.compile("^[\\d]+ ([^ ]+ +online [\\d]+ hours [\\d]+ minutes) (.*)$");
            Matcher m     = p.matcher(entry);

            if (m.matches()) {

                if (quitReason == null) {
                    quitReason = m.group(2);
                    System.out.println("\r\nQUIT: " + ((m.group(2).equals("")) ? "No Reason Given" : quitReason) + "\r\n");
                }

                String tempQuitReason = m.group(2);
                String tempSession    = m.group(1);

                if (tempQuitReason.equals(quitReason)) {
                    System.out.println("    " + tempSession);
                } else {
                    quitReason = null;
                    i -= 1;
                    continue;
                }
            } else {
                System.out.println("\r\nError: Unexpected entry in logfile: " + entry);
                System.exit(1);
            }
        }
        System.out.println("\r\n");
    }

    private void formatAndOutputByDuration(ArrayList sortedSessions) {
        System.out.println("\r\n");

        for (int i = 0; i < sortedSessions.size(); i++) {
            String[] columns = ((String) sortedSessions.get(i)).split(" ", 2);
            System.out.println(columns[1]);
        }

        System.out.println("\r\n");
    }

    private ArrayList readLogfile(String logfile) {

        ArrayList log = new ArrayList();

        try {
            BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(logfile)));

            for (String line; (line = in.readLine()) != null; )
                log.add(line);

            in.close();

        } catch (FileNotFoundException e) {
            System.out.println("\r\nError: Can't find logfile '" + logfile + "'.\r\n");
            System.exit(1);

        } catch (IOException e) {
            System.out.println("\r\nError: Can't read logfile '" + logfile + "'.\r\n");
            System.exit(1);
        }
        return log;
    }

    /*
     * Performs an odd-even transposition sort.
     */
    private ArrayList sortSessionsByDuration(ArrayList calculatedSessionDurations) {

        for (int i = 0; i < calculatedSessionDurations.size()/2; i++) {
            for (int j = 0; j+1 < calculatedSessionDurations.size(); j += 2) {

                String[] currentDurationString = ((String) calculatedSessionDurations.get(j)).split(" ", 2);
                long     currentDuration       = Long.parseLong(currentDurationString[0]);
                String[] nextDurationString    = ((String) calculatedSessionDurations.get(j+1)).split(" ", 2);
                long     nextDuration          = Long.parseLong(nextDurationString[0]);

                if (currentDuration > nextDuration) {
                    calculatedSessionDurations.add(j, calculatedSessionDurations.get(j+1));
                    calculatedSessionDurations.remove(j+2);
                }
            }

            for (int j = 1; j+1 < calculatedSessionDurations.size(); j += 2) {

                String[] currentDurationString = ((String) calculatedSessionDurations.get(j)).split(" ", 2);
                long     currentDuration       = Long.parseLong(currentDurationString[0]);
                String[] nextDurationString    = ((String) calculatedSessionDurations.get(j+1)).split(" ", 2);
                long     nextDuration          = Long.parseLong(nextDurationString[0]);

                if (currentDuration > nextDuration) {
                    calculatedSessionDurations.add(j, calculatedSessionDurations.get(j+1));
                    calculatedSessionDurations.remove(j+2);
                }
            }
        }
        return calculatedSessionDurations;
    }

    private ArrayList sortSessionsByNick(ArrayList sortedSessionsByTime) {

        ArrayList sortedSessionsByNick = new ArrayList();

        while (sortedSessionsByTime.size() != 0) {

            String   entry       = (String) sortedSessionsByTime.get(0);
            String[] entryFields = entry.split(" ");
            String   currentNick = entryFields[2];

            sortedSessionsByNick.add(entry);
            sortedSessionsByNick.add(sortedSessionsByTime.get(1));
            sortedSessionsByTime.remove(0);
            sortedSessionsByTime.remove(0);
            for (int i = 0; i+1 < sortedSessionsByTime.size(); i += 2) {

                String   nextEntry       = (String) sortedSessionsByTime.get(i);
                String[] nextEntryFields = nextEntry.split(" ");

                if (nextEntryFields[2].equals(currentNick)) {
                    sortedSessionsByNick.add(nextEntry);
                    sortedSessionsByNick.add(sortedSessionsByTime.get(i+1));
                    sortedSessionsByTime.remove(i);
                    sortedSessionsByTime.remove(i);
                    i -= 2;
                }
            }
        }
        return sortedSessionsByNick;
    }

    private ArrayList sortSessionsByQuitReason(ArrayList sortedSessionsByNick) {

        ArrayList sortedSessionsByQuitReason = new ArrayList();

        while (sortedSessionsByNick.size() != 0) {

            String  entry = (String) sortedSessionsByNick.get(1);
            Pattern p     = Pattern.compile("^[^ ]+ [^ ]+ [^ ]+ (.*)$");
            Matcher m     = p.matcher(entry);

            if (m.matches()) {

                String currentQuitReason = m.group(1);

                sortedSessionsByQuitReason.add(sortedSessionsByNick.get(0));
                sortedSessionsByQuitReason.add(entry);
                sortedSessionsByNick.remove(0);
                sortedSessionsByNick.remove(0);
                for (int i = 0; i+1 < sortedSessionsByNick.size(); i += 2) {

                    String  nextEntry = (String) sortedSessionsByNick.get(i+1);
                    Pattern p2        = Pattern.compile("^[^ ]+ [^ ]+ [^ ]+ (.*)$");
                    Matcher m2        = p2.matcher(nextEntry);

                    if (m2.matches()) {

                        String nextQuitReason = m2.group(1);
 
                        if (nextQuitReason.equals(currentQuitReason)) {
                            sortedSessionsByQuitReason.add(sortedSessionsByNick.get(i));
                            sortedSessionsByQuitReason.add(nextEntry);
                            sortedSessionsByNick.remove(i);
                            sortedSessionsByNick.remove(i);
                            i -= 2;
                        }
                    } else {
                        System.out.println("\r\nError: Unexpected entry in logfile: " + nextEntry);
                        System.exit(1);
                    }
                }
            } else {
                System.out.println("\r\nError: Unexpected entry in logfile: " + entry);
                System.exit(1);
            }
        }
        return sortedSessionsByQuitReason;
    }

    /**
     * Sessions terminated with "parts" messages instead of "quits" are filtered
     * out.
     */
    private ArrayList sortSessionsByTime(ArrayList log) {

        ArrayList sortedSessionsByTime = new ArrayList();

        mainLoop:
            while (log.size() > 0) {
                
                String   entry       = (String) log.get(0);
                String[] entryFields = entry.split(" ");
                
                if (entryFields[1].equals("quits") && !entryFields[1].equals("joins")) {
                    /*
                     * Discard entry. The specified log either doesn't contain
                     * the corresponding "joins" time for this quit entry or the
                     * entry is a "parts" or unknown message, and in both cases
                     * the entry's data is useless.
                     */
                    log.remove(0);
                    continue;
                }
                
                for (int i = 1; i < log.size(); i++) {  // Find corresponding "quits" entry.
                    
                    String   tempEntry       = (String) log.get(i);
                    String[] tempEntryFields = tempEntry.split(" ");
                    
                    if (tempEntryFields[2].equals(entryFields[2])) {  // Check if the nick fields for the two entries match.
                        if (!tempEntryFields[1].equals("quits")) {
                            if (tempEntryFields[1].equals("joins")) {  // Don't discard a subsequent "joins" entry.
                                log.remove(0);
                                continue mainLoop;
                            }
                            log.remove(i);
                            continue;
                        }
                        sortedSessionsByTime.add(entry);
                        sortedSessionsByTime.add(tempEntry);
                        log.remove(i);
                        break;
                    }
                }
                /*
                 * Discard "joins" entry. The specified log doesn't contain the
                 * corresponding "quits" time for this entry so the entry's
                 * data is useless.
                 */
                
                log.remove(0);
            }

        return sortedSessionsByTime;
    }
}
