package org.graalvm.visualvm.modules.tracer.collections.resources;

import org.openjdk.btrace.core.Profiler;
import org.openjdk.btrace.core.annotations.*;
import static org.openjdk.btrace.core.BTraceUtils.*;

@BTrace(name="CollectionsStats") public class CollectionsTracer {
    // <fragment name="sorting">
    @Property(name="sortingProfiler") private static Profiler sortingProfiler = Profiling.newProfiler(2);

    @OnMethod(
        clazz="java.util.Collections",
        method="sort"
    )
    public static void collectionsSortBefore() {
        Profiling.recordEntry(sortingProfiler, "Collections.sort");
    }

    @OnMethod(
        clazz="java.util.Collections",
        method="sort",
        location=@Location(Kind.RETURN)
    )
    public static void collectionsSortAfter(@Duration long time) {
        Profiling.recordExit(sortingProfiler, "Collections.sort", time);
    }

    @OnMethod(
        clazz="java.util.Arrays",
        method="sort"
    )
    public static void arraysSortBefore() {
        Profiling.recordEntry(sortingProfiler, "Arrays.sort");
    }

    @OnMethod(
        clazz="java.util.Arrays",
        method="sort",
        location=@Location(Kind.RETURN)
    )
    public static void arraysSortAfter(@Duration long time) {
        Profiling.recordExit(sortingProfiler, "Arrays.sort", time);
    }
    // </fragment>

    // <fragment name="conversions">
    @Property(name="conversionsProfiler") private static Profiler conversionsProfiler = Profiling.newProfiler(4);

    @OnMethod(
        clazz="+java.util.Set",
        method="toArray"
    )
    public static void setToArrayBefore() {
        Profiling.recordEntry(conversionsProfiler, "Set.toArray");
    }

    @OnMethod(
        clazz="+java.util.Set",
        method="toArray",
        location=@Location(Kind.RETURN)
    )
    public static void setToArrayAfter(@Duration long time) {
        Profiling.recordExit(conversionsProfiler, "Set.toArray", time);
    }

    @OnMethod(
        clazz="+java.util.List",
        method="toArray"
    )
    public static void listToArrayBefore() {
        Profiling.recordEntry(conversionsProfiler, "List.toArray");
    }

    @OnMethod(
        clazz="+java.util.List",
        method="toArray",
        location=@Location(Kind.RETURN)
    )
    public static void listToArrayAfter(@Duration long time) {
        Profiling.recordExit(conversionsProfiler, "List.toArray", time);
    }

    @OnMethod(
        clazz="+java.util.Queue",
        method="toArray"
    )
    public static void queueToArrayBefore() {
        Profiling.recordEntry(conversionsProfiler, "Queue.toArray");
    }

    @OnMethod(
        clazz="+java.util.Queue",
        method="toArray",
        location=@Location(Kind.RETURN)
    )
    public static void queueToArrayAfter(@Duration long time) {
        Profiling.recordExit(conversionsProfiler, "List.Queue", time);
    }

    @OnMethod(
        clazz="java.util.Arrays",
        method="asList"
    )
    public static void arraysAsListBefore() {
        Profiling.recordEntry(conversionsProfiler, "Arrays.asList");
    }

    @OnMethod(
        clazz="java.util.Arrays",
        method="asList",
        location=@Location(Kind.RETURN)
    )
    public static void arraysAsListAfter(@Duration long time) {
        Profiling.recordExit(conversionsProfiler, "Arrays.asList", time);
    }
    // </fragment>

    // <fragment name="general_search">
    @Property(name="generalSearchProfiler") private static Profiler generalSearchProfiler = Profiling.newProfiler(2);

    @OnMethod(
        clazz="java.util.Collections",
        method="binarySearch"
    )
    public static void collectionsSearchBefore() {
        Profiling.recordEntry(generalSearchProfiler, "Collections.binarySearch");
    }

    @OnMethod(
        clazz="java.util.Collections",
        method="binarySearch",
        location=@Location(Kind.RETURN)
    )
    public static void collectionsSearchAfter(@Duration long time) {
        Profiling.recordExit(generalSearchProfiler, "Collections.binarySearch", time);
    }

    @OnMethod(
        clazz="java.util.Arrays",
        method="binarySearch"
    )
    public static void arraysSearchBefore() {
        Profiling.recordEntry(generalSearchProfiler, "Arrays.binarySearch");
    }

    @OnMethod(
        clazz="java.util.Arrays",
        method="binarySearch",
        location=@Location(Kind.RETURN)
    )
    public static void arraysSearchAfter(@Duration long time) {
        Profiling.recordExit(generalSearchProfiler, "Arrays.binarySearch", time);
    }
    // </fragment>

    // <fragment name="collections_search">
    @Property(name="collectionsSearchProfiler") private static Profiler collectionsSearchProfiler = Profiling.newProfiler(7);

    @OnMethod(
        clazz="+java.util.Set",
        method="contains"
    )
    public static void setContainsBefore() {
        Profiling.recordEntry(collectionsSearchProfiler, "Set.contains");
    }

    @OnMethod(
        clazz="+java.util.Set",
        method="contains",
        location=@Location(Kind.RETURN)
    )
    public static void setContainsAfter(@Duration long time) {
        Profiling.recordExit(collectionsSearchProfiler, "Set.contains", time);
    }

    @OnMethod(
        clazz="+java.util.Set",
        method="containsAll"
    )
    public static void setContainsAllBefore() {
        Profiling.recordEntry(collectionsSearchProfiler, "Set.containsAll");
    }

    @OnMethod(
        clazz="+java.util.Set",
        method="containsAll",
        location=@Location(Kind.RETURN)
    )
    public static void setContainsAllAfter(@Duration long time) {
        Profiling.recordExit(collectionsSearchProfiler, "Set.containsAll", time);
    }

    @OnMethod(
        clazz="+java.util.List",
        method="contains"
    )
    public static void listContainsBefore() {
        Profiling.recordEntry(collectionsSearchProfiler, "List.contains");
    }

    @OnMethod(
        clazz="+java.util.List",
        method="contains",
        location=@Location(Kind.RETURN)
    )
    public static void listContainsAfter(@Duration long time) {
        Profiling.recordExit(collectionsSearchProfiler, "List.contains", time);
    }

    @OnMethod(
        clazz="+java.util.List",
        method="containsAll"
    )
    public static void listContainsAllBefore() {
        Profiling.recordEntry(collectionsSearchProfiler, "List.containsAll");
    }

    @OnMethod(
        clazz="+java.util.List",
        method="containsAll",
        location=@Location(Kind.RETURN)
    )
    public static void listContainsAllAfter(@Duration long time) {
        Profiling.recordExit(collectionsSearchProfiler, "List.containsAll", time);
    }

    @OnMethod(
        clazz="+java.util.List",
        method="indexOf"
    )
    public static void listIndexOfBefore() {
        Profiling.recordEntry(collectionsSearchProfiler, "List.indexOf");
    }

    @OnMethod(
        clazz="+java.util.List",
        method="indexOf",
        location=@Location(Kind.RETURN)
    )
    public static void listIndexOfAfter(@Duration long time) {
        Profiling.recordExit(collectionsSearchProfiler, "List.indexOf", time);
    }

    @OnMethod(
        clazz="+java.util.List",
        method="lastIndexOf"
    )
    public static void listLastIndexOfBefore() {
        Profiling.recordEntry(collectionsSearchProfiler, "List.lastIndexOf");
    }

    @OnMethod(
        clazz="+java.util.List",
        method="lastIndexOf",
        location=@Location(Kind.RETURN)
    )
    public static void listLastIndexOfAfter(@Duration long time) {
        Profiling.recordExit(collectionsSearchProfiler, "List.lastIndexOf", time);
    }

    @OnMethod(
        clazz="+java.util.Queue",
        method="contains"
    )
    public static void queueContainsBefore() {
        Profiling.recordEntry(collectionsSearchProfiler, "Queue.contains");
    }

    @OnMethod(
        clazz="+java.util.Queue",
        method="contains",
        location=@Location(Kind.RETURN)
    )
    public static void queueContainsAfter(@Duration long time) {
        Profiling.recordExit(collectionsSearchProfiler, "Queue.contains", time);
    }

    @OnMethod(
        clazz="+java.util.Queue",
        method="containsAll"
    )
    public static void queueContainsAllBefore() {
        Profiling.recordEntry(collectionsSearchProfiler, "Queue.containsAll");
    }

    @OnMethod(
        clazz="+java.util.Queue",
        method="containsAll",
        location=@Location(Kind.RETURN)
    )
    public static void queueContainsAllAfter(@Duration long time) {
        Profiling.recordExit(collectionsSearchProfiler, "Queue.containsAll", time);
    }
    // </fragment>

    // <fragment name="map_search">
    @Property(name="mapSearchProfiler") private static Profiler mapSearchProfiler = Profiling.newProfiler(3);

    @OnMethod(
        clazz="+java.util.Map",
        method="get"
    )
    public static void mapGetBefore() {
        Profiling.recordEntry(mapSearchProfiler, "Map.get");
    }

    @OnMethod(
        clazz="+java.util.Map",
        method="get",
        location=@Location(Kind.RETURN)
    )
    public static void mapGetAfter(@Duration long time) {
        Profiling.recordExit(mapSearchProfiler, "Map.get", time);
    }

    @OnMethod(
        clazz="+java.util.Map",
        method="containsKey"
    )
    public static void mapContainsKeyBefore() {
        Profiling.recordEntry(mapSearchProfiler, "Map.containsKey");
    }

    @OnMethod(
        clazz="+java.util.Map",
        method="containsKey",
        location=@Location(Kind.RETURN)
    )
    public static void mapContainsKeyAfter(@Duration long time) {
        Profiling.recordExit(mapSearchProfiler, "Map.containsKey", time);
    }

    @OnMethod(
        clazz="+java.util.Map",
        method="containsValue"
    )
    public static void mapContainsValueBefore() {
        Profiling.recordEntry(mapSearchProfiler, "Map.containsValue");
    }

    @OnMethod(
        clazz="+java.util.Map",
        method="containsValue",
        location=@Location(Kind.RETURN)
    )
    public static void mapContainsValueAfter(@Duration long time) {
        Profiling.recordExit(mapSearchProfiler, "Map.containsValue", time);
    }
    // </fragment>

    // <fragment name="map_resize">
    @Property(name="mapResizeProfiler") private static Profiler mapResizeProfiler = Profiling.newProfiler(3);
    @OnMethod(
        clazz="+java.util.Map",
        method="resize"
    )
    public static void mapResizeBefore(@ProbeClassName String pcn, int newSize) {
        Profiling.recordEntry(mapResizeProfiler, pcn);
    }
    @OnMethod(
        clazz="+java.util.Map",
        method="resize",
        location=@Location(Kind.RETURN)
    )
    public static void mapResizeBefore(int newSize, @ProbeClassName String pcn, @Duration long time) {
        Profiling.recordExit(mapResizeProfiler, pcn, time);
    }
    // </fragment>
    
    @OnTimer(1000)
    public static void updateMBeans() {
        // <fragment name="sorting">
        Profiling.snapshotAndReset(sortingProfiler);
        // </fragment>

        // <fragment name="conversions">
        Profiling.snapshotAndReset(conversionsProfiler);
        // </fragment>

        // <fragment name="general_search">
        Profiling.snapshotAndReset(generalSearchProfiler);
        // </fragment>

        // <fragment name="map_search">
        Profiling.snapshotAndReset(mapSearchProfiler);
        // </fragment>

        // <fragment name="collections_search">
        Profiling.snapshotAndReset(collectionsSearchProfiler);
        // </fragment>
        
        // <fragment name="map_resize">
        Profiling.snapshotAndReset(mapResizeProfiler);
        // </fragment>
    }
}
