package org.stagemonitor.requestmonitor.profiler;

import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;

import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
import org.stagemonitor.core.instrument.StagemonitorByteBuddyTransformer;
import org.stagemonitor.core.instrument.StagemonitorClassNameMatcher;

public class ProfilingTransformer extends StagemonitorByteBuddyTransformer {

	@Override
	public ElementMatcher.Junction<TypeDescription> getExtraExcludeTypeMatcher() {
		return nameStartsWith(Profiler.class.getPackage().getName())
				.or(makeSureClassesAreNotProfiledTwice());
	}

	/*
	 * If this is a subclass of ProfilingTransformer, make sure to not instrument classes
	 * which are matched by ProfilingTransformer
	 */
	private ElementMatcher.Junction<TypeDescription> makeSureClassesAreNotProfiledTwice() {
		return isSubclass() ? StagemonitorClassNameMatcher.INSTANCE : ElementMatchers.<TypeDescription>none();
	}

	private boolean isSubclass() {
		return getClass() != ProfilingTransformer.class;
	}

	@Override
	protected Class<? extends StagemonitorByteBuddyTransformer> getAdviceClass() {
		return ProfilingTransformer.class;
	}

	@Advice.OnMethodEnter
	public static void enter(@Advice.Origin("#r #t.#m#s") String signature) {
		Profiler.start(signature);
	}

	@Advice.OnMethodExit(onThrowable = Throwable.class)
	public static void exit() {
		Profiler.stop();
	}

	@Override
	protected int getOrder() {
		return Integer.MAX_VALUE;
	}
}

@@ -124,32 +124,435 @@
 ort 
-net.bytebuddy.asm.Advice
+java.lang.annotation.ElementType;%0Aimport java.lang.annotation.Retention;%0Aimport java.lang.annotation.RetentionPolicy;%0Aimport java.lang.annotation.Target;%0Aimport java.util.Collections;%0Aimport java.util.List;%0A%0Aimport net.bytebuddy.asm.Advice;%0Aimport net.bytebuddy.description.annotation.AnnotationDescription;%0Aimport net.bytebuddy.description.method.MethodDescription;%0Aimport net.bytebuddy.description.method.ParameterDescription
 ;%0Aim
@@ -1736,35 +1736,25 @@
 er(@
-Advice.Origin(%22#r #t.#m#s%22)
+ProfilerSignature
  Str
@@ -1943,24 +1943,24 @@
 etOrder() %7B%0A
-
 %09%09return Int
@@ -1977,11 +1977,1489 @@
 ALUE;%0A%09%7D
+%0A%0A%09@Override%0A%09protected List%3CStagemonitorDynamicValue%3C?%3E%3E getDynamicValues() %7B%0A%09%09return Collections.%3CStagemonitorDynamicValue%3C?%3E%3EsingletonList(new ProfilerDynamicValue());%0A%09%7D%0A%0A%09@Retention(RetentionPolicy.RUNTIME)%0A%09@Target(ElementType.PARAMETER)%0A%09public @interface ProfilerSignature %7B%0A%09%7D%0A%0A%09public static class ProfilerDynamicValue extends StagemonitorByteBuddyTransformer.StagemonitorDynamicValue%3CProfilerSignature%3E %7B%0A%0A%09%09@Override%0A%09%09public Class%3CProfilerSignature%3E getAnnotationClass() %7B%0A%09%09%09return ProfilerSignature.class;%0A%09%09%7D%0A%0A%09%09@Override%0A%09%09public Object resolve(MethodDescription.InDefinedShape method,%0A%09%09%09%09%09%09%09  ParameterDescription.InDefinedShape target,%0A%09%09%09%09%09%09%09  AnnotationDescription.Loadable%3CProfilerSignature%3E annotation,%0A%09%09%09%09%09%09%09  boolean initialized) %7B%0A%09%09%09final String returnType = method.getReturnType().asErasure().getSimpleName();%0A%09%09%09final String className = method.getDeclaringType().getName();%0A%09%09%09return String.format(%22%25s %25s.%25s(%25s)%22, returnType, className, method.getName(), getSignature(method));%0A%09%09%7D%0A%0A%09%09public String getSignature(MethodDescription.InDefinedShape instrumentedMethod) %7B%0A%09%09%09StringBuilder stringBuilder = new StringBuilder();%0A%09%09%09boolean comma = false;%0A%09%09%09for (TypeDescription typeDescription : instrumentedMethod.getParameters().asTypeList().asErasures()) %7B%0A%09%09%09%09if (comma) %7B%0A%09%09%09%09%09stringBuilder.append(',');%0A%09%09%09%09%7D else %7B%0A%09%09%09%09%09comma = true;%0A%09%09%09%09%7D%0A%09%09%09%09stringBuilder.append(typeDescription.getSimpleName());%0A%09%09%09%7D%0A%09%09%09return stringBuilder.toString();%0A%09%09%7D%0A%09%7D
 %0A%7D%0A

Use simple names for profiler return and argument types