package ex10;

import ch.usi.dag.disl.annotation.After;
import ch.usi.dag.disl.annotation.Before;
import ch.usi.dag.disl.marker.BodyMarker;
import ch.usi.dag.disl.staticcontext.ClassStaticContext;
import ch.usi.dag.disl.staticcontext.MethodStaticContext;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;

public class Instrumentation {
    @Before(marker = BodyMarker.class,
            scope = "ex10.VideoGamePlayer.*")
    static void instrumentCall(final ClassStaticContext csc, final MethodStaticContext msc) {
        Profiler.registerInvoke(csc.getName(), msc.thisMethodName());
    }

    @Before(marker = BodyMarker.class,
            scope = "ex10.Player.*")
    static void instrumentCallPlayer(final ClassStaticContext csc, final MethodStaticContext msc) {
        Profiler.registerInvoke(csc.getName(), msc.thisMethodName());
    }

    @After(marker = BodyMarker.class,
            scope = "void ex10.Main.main",
            guard = IsPublicStaticGuard.class)
    static void instrumentMain() {
        final ClassLoader cls = ClassLoader.getSystemClassLoader();
        final List<String> classes = List.of("ex10.Player", "ex10.VideoGamePlayer");
        for (final String clazzName : classes) {
            try {
                final Class<?> clazz = cls.loadClass(clazzName);
                final Set<Method> declaredMethods = Set.of(clazz.getDeclaredMethods());
                for (final Method m : clazz.getMethods()) {
                    if (declaredMethods.contains(m)) {
                        Profiler.registerDeclared(clazzName, m.toString());
                    } else {
                        Profiler.registerInherited(clazzName, m.toString());
                    }
                }
            } catch (final ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
