/*
 * Copyright The OpenTelemetry Authors
 * SPDX-License-Identifier: Apache-2.0
 */

package io.opentelemetry.javaagent.tooling.muzzle;

import java.util.Iterator;
import java.util.ServiceLoader;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.utility.JavaModule;

/**
 * This class contains class references for objects shared by the agent installer as well as muzzle
 * (both compile and runtime). Extracted out from AgentInstaller to begin separating some of the
 * logic out.
 */
public final class AgentTooling {

  private static final AgentLocationStrategy LOCATION_STRATEGY =
      new AgentLocationStrategy(getBootstrapProxy());

  private static final AgentBuilder.PoolStrategy POOL_STRATEGY =
      new AgentCachingPoolStrategy(LOCATION_STRATEGY);

  private static final ThreadLocal<CurrentTransform> CURRENT_TRANSFORM = new ThreadLocal<>();

  public static AgentLocationStrategy locationStrategy() {
    return LOCATION_STRATEGY;
  }

  public static AgentBuilder.PoolStrategy poolStrategy() {
    return POOL_STRATEGY;
  }

  public static AgentBuilder.Listener transformListener() {
    return new ClassTransformListener();
  }

  private static ClassLoader getBootstrapProxy() {
    Iterator<BootstrapProxyProvider> iterator =
        ServiceLoader.load(BootstrapProxyProvider.class, AgentTooling.class.getClassLoader())
            .iterator();
    if (iterator.hasNext()) {
      BootstrapProxyProvider bootstrapProxyProvider = iterator.next();
      return bootstrapProxyProvider.getBootstrapProxy();
    }

    return null;
  }

  public static boolean isTransforming(ClassLoader classLoader, String className) {
    CurrentTransform currentTransform = CURRENT_TRANSFORM.get();
    if (currentTransform == null) {
      return false;
    }
    return currentTransform.className.equals(className)
        && currentTransform.classLoader == classLoader;
  }

  private static class ClassTransformListener extends AgentBuilder.Listener.Adapter {
    @Override
    public void onDiscovery(
        String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {
      CURRENT_TRANSFORM.set(new CurrentTransform(classLoader, typeName));
    }

    @Override
    public void onComplete(
        String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {
      CURRENT_TRANSFORM.remove();
    }
  }

  private static class CurrentTransform {
    private final ClassLoader classLoader;
    private final String className;

    CurrentTransform(ClassLoader classLoader, String className) {
      this.classLoader = classLoader;
      this.className = className;
    }
  }

  private AgentTooling() {}
}
