package net.openhft.chronicle.core.threads;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class MonitorProfileAnalyserMain {

    private static final String PROFILE_OF_THE_THREAD = "profile of the thread";
    private static final String THREAD_HAS_BLOCKED_FOR = "thread has blocked for";

    /**
     * Reads one or more log files and looks for thread profiles to summarise
     */
    public static void main(String[] args) throws IOException {
        if (args.length == 0)
            System.err.println("No input file(s) provided");

        int interval = Integer.getInteger("interval", 0);
        if (interval <= 0) {
            main0(args);
        } else {
            for (; ; ) {
                main0(args);
                JitterSampler.sleepSilently(interval * 1000);
                System.out.println("\n---\n");
            }
        }
    }

    public static void main0(String[] args) throws IOException {
        System.out.println("Grouped by line");
        Map<String, Integer> stackCount = new LinkedHashMap<>();

        for (String arg : args) {
            StringBuilder sb = new StringBuilder();
            int lineCount = -1;
            try (BufferedReader br = Files.newBufferedReader(Paths.get(arg))) {
                // TODO: PrintGCApplicationStoppedTime

                for (String line; (line = br.readLine()) != null; ) {
                    if (line.contains(PROFILE_OF_THE_THREAD) || line.contains(THREAD_HAS_BLOCKED_FOR)) {
                        if (sb.length() > 0) {
                            String lines = sb.toString();
                            stackCount.compute(lines, (k, v) -> v == null ? 1 : v + 1);
                        }
                        lineCount = 0;
                        sb.setLength(0);

                    } else if (partOfStackTrace(line) && lineCount >= 0) {
                        if (++lineCount <= 8) {
                            sb.append(line).append("\n");
                        }
                    } else if (sb.length() > 0) {
                        String lines = sb.toString();
                        stackCount.compute(lines, (k, v) -> v == null ? 1 : v + 1);
                        sb.setLength(0);
                    }
                }
            }
        }
        List<Map.Entry<String, Integer>> stackSortedByCount =
                stackCount.entrySet().stream()
                        .filter(e -> e.getValue() > 2)
                        .sorted(Comparator.comparing(e -> -e.getValue())) // reversed
                        .limit(20)
                        .collect(Collectors.toList());
        stackSortedByCount
                .stream()
                .peek(e -> stackCount.remove(e.getKey()))
                .forEach(e -> System.out.println(e.getValue() + e.getKey()));

        System.out.println("Grouped by method.");
        Map<String, Integer> methodCount = new LinkedHashMap<>();
        for (Map.Entry<String, Integer> entry : stackCount.entrySet()) {
            String stack = entry.getKey().replaceFirst("\\(.*?\\)", "( * )");
            methodCount.compute(stack, (k, v) -> (v == null ? 0 : v) + entry.getValue());
        }

        List<Map.Entry<String, Integer>> methodSortedByCount =
                methodCount.entrySet().stream()
                        .filter(e -> e.getValue() > 2)
                        .sorted(Comparator.comparing(e -> -e.getValue())) // reversed
                        .limit(20)
                        .collect(Collectors.toList());
        methodSortedByCount
                .forEach(e -> System.out.println(e.getValue() + e.getKey()));
    }

    private static boolean partOfStackTrace(String line) {
        // make this more robust in the face of copy/pasting etc.
        return line.startsWith("\tat ") || line.matches("\\s+at .*");
    }
}

@@ -174,95 +174,9 @@
 til.
-Comparator;%0Aimport java.util.LinkedHashMap;%0Aimport java.util.List;%0Aimport java.util.Map
+*
 ;%0Aim
@@ -253,16 +253,95 @@
 Main %7B%0A%0A
+    private static final int MAX_LINES = Integer.getInteger(%22st.maxlines%22, 8);%0A
     priv
@@ -743,24 +743,127 @@
 rovided%22);%0A%0A
+        List%3CString%3E ignoreSubStrings = Arrays.asList(System.getProperty(%22st.ignore%22, %22%22).split(%22,%22));%0A
         int 
@@ -947,32 +947,50 @@
           main0(
+ignoreSubStrings, 
 args);%0A        %7D
@@ -1044,16 +1044,34 @@
   main0(
+ignoreSubStrings, 
 args);%0A 
@@ -1238,16 +1238,47 @@
 d main0(
+List%3CString%3E ignoreSubStrings, 
 String%5B%5D
@@ -1926,123 +1926,56 @@
     
-String lines = sb.toString();%0A                            stackCount.compute(lines, (k, v) -%3E v == null ? 1 : v + 1
+addToStackCount(ignoreSubStrings, stackCount, sb
 );%0A 
@@ -2198,17 +2198,25 @@
 ount %3C= 
-8
+MAX_LINES
 ) %7B%0A    
@@ -2373,119 +2373,56 @@
     
-String lines = sb.toString();%0A                        stackCount.compute(lines, (k, v) -%3E v == null ? 1 : v + 1
+addToStackCount(ignoreSubStrings, stackCount, sb
 );%0A 
@@ -3874,24 +3874,364 @@
 )));%0A    %7D%0A%0A
+    private static void addToStackCount(List%3CString%3E ignoreSubStrings, Map%3CString, Integer%3E stackCount, StringBuilder sb) %7B%0A        String lines = sb.toString();%0A        for (String ss : ignoreSubStrings)%0A            if (lines.contains(ss))%0A                return;%0A        stackCount.compute(lines, (k, v) -%3E v == null ? 1 : v + 1);%0A    %7D%0A%0A
     private 

allow some stack traces to be ignored in MonitorProfileAnalyserMain