package com.github.chrishantha.jfr.flamegraph.output;

import com.beust.jcommander.IStringConverter;
import com.jrockit.mc.flightrecorder.spi.IEvent;

import java.util.Arrays;

/**
 * Different types of events possibly available in a JFR recording.
 * <p>
 * Each type can be activated using a command line option and can match one or many
 * JFR event types. Each type knows how to convert the event into a numeric value
 * that will make the flame graph most meaningful. For allocation events this would
 * be the number of bytes allocated, while for file reads it would be the duration of
 * the read operation.
 */
public enum EventType {

    METHOD_PROFILING_SAMPLE("cpu", null, "Method Profiling Sample"),
    ALLOCATION_IN_NEW_TLAB("allocation-tlab", "tlabSize", "Allocation in new TLAB"),
    ALLOCATION_OUTSIDE_TLAB("allocation-outside-tlab", "allocationSize", "Allocation outside TLAB"),
    JAVA_EXCEPTION("exceptions", null, "Java Exception"),
    JAVA_MONITOR_BLOCKED("monitor-blocked", "(duration)", "Java Monitor Blocked"),
    IO("io", "(duration)", "File Read", "File Write", "Socket Read", "Socket Write");

    private final String commandLineOption;
    private final String valueField;
    private final String[] eventNames;

    EventType(String commandLineOption, String valueField, String... eventNames) {
        this.eventNames = eventNames;
        this.commandLineOption = commandLineOption;
        this.valueField = valueField;
    }

    public boolean matches(IEvent event) {
        String name = event.getEventType().getName();
        return Arrays.stream(eventNames).anyMatch(name::equals);
    }

    public long getValue(IEvent event) {
        if (valueField == null) {
            return 1;
        }
        return (long) event.getValue(valueField);
    }

    @Override
    public String toString() {
        return commandLineOption;
    }


    public static final class EventTypeConverter implements IStringConverter<EventType> {
        @Override
        public EventType convert(String commandLineOption) {
            switch (commandLineOption) {
                case "allocation-tlab":
                    return ALLOCATION_IN_NEW_TLAB;
                case "allocation-outside-tlab":
                    return ALLOCATION_OUTSIDE_TLAB;
                case "exceptions":
                    return JAVA_EXCEPTION;
                case "monitor-blocked":
                    return JAVA_MONITOR_BLOCKED;
                case "cpu":
                    return METHOD_PROFILING_SAMPLE;
                case "io":
                    return IO;
                default:
                    throw new IllegalArgumentException("Event type [" + commandLineOption + "] does not exist.");
            }
        }
    }
}

@@ -168,16 +168,54 @@
 .Arrays;
+%0Aimport java.util.concurrent.TimeUnit;
 %0A%0A/**%0A *
@@ -710,20 +710,32 @@
 (%22cpu%22, 
-null
+ValueField.COUNT
 , %22Metho
@@ -806,18 +806,28 @@
 b%22, 
-%22tlabSize%22
+ValueField.TLAB_SIZE
 , %22A
@@ -910,24 +910,34 @@
 b%22, 
-%22allocationSize%22
+ValueField.ALLOCATION_SIZE
 , %22A
@@ -995,20 +995,32 @@
 tions%22, 
-null
+ValueField.COUNT
 , %22Java 
@@ -1076,28 +1076,35 @@
 ocked%22, 
-%22(duration)%22
+ValueField.DURATION
 , %22Java 
@@ -1139,20 +1139,27 @@
 o%22, 
-%22(duration)%22
+ValueField.DURATION
 , %22F
@@ -1278,22 +1278,26 @@
 e final 
-String
+ValueField
  valueFi
@@ -1381,22 +1381,26 @@
 Option, 
-String
+ValueField
  valueFi
@@ -1785,12 +1785,15 @@
     
-if (
+return 
 valu
@@ -1802,99 +1802,23 @@
 ield
- == null) %7B%0A            return 1;%0A        %7D%0A        return (long) event.getValue(valueField
+.getValue(event
 );%0A 
@@ -2784,11 +2784,842 @@
  %7D%0A    %7D
+%0A%0A    private enum ValueField %7B%0A        COUNT %7B%0A            @Override%0A            public long getValue(IEvent event) %7B%0A                return 1;%0A            %7D%0A        %7D,%0A        DURATION %7B%0A            @Override%0A            public long getValue(IEvent event) %7B%0A                long nanos = (long) event.getValue(%22(duration)%22);%0A                return TimeUnit.NANOSECONDS.toMillis(nanos);%0A            %7D%0A        %7D,%0A        ALLOCATION_SIZE %7B%0A            @Override%0A            public long getValue(IEvent event) %7B%0A                return (long) event.getValue(%22allocationSize%22);%0A            %7D%0A        %7D,%0A        TLAB_SIZE %7B%0A            @Override%0A            public long getValue(IEvent event) %7B%0A                return (long) event.getValue(%22tlabSize%22);%0A            %7D%0A        %7D;%0A%0A        public abstract long getValue(IEvent event);%0A    %7D
 %0A%7D%0A

Convert durations to ms for flame graphs