package com.google.devtools.build.android.desugar.testdata.java8;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;

/** Interface for testing the parameters of static and default methods. */
public interface InterfaceMethodWithParam {

  /** For testing the annotations on the parameters of interface static and default methods */
  @Documented
  @Retention(RetentionPolicy.RUNTIME)
  @Target({ElementType.PARAMETER, ElementType.METHOD})
  @interface Foo {
    String value() default "default-attr";
  }

  /** For testing the annotations on the parameters of interface static and default methods */
  @Documented
  @Retention(RetentionPolicy.RUNTIME)
  @Target({ElementType.TYPE_USE})
  @interface TyFoo {}

  static long simpleComputeStatic(int x) {
    return (long) x;
  }

  default long simpleComputeDefault(int x) {
    return (long) x;
  }

  /**
   * @param v4897b02fddeda3bb31bc15b3cad0f6febc61508 a cryptic parameter name for checking whether
   *     the name is available at run-time. The name is from $ echo "desugar-static" | sha1sum
   * @return The reflection representation of the method itself.
   */
  @Foo("custom-attr-value-1")
  static @TyFoo Method inspectCompanionMethodOfStaticMethod(
      @Foo @TyFoo String v4897b02fddeda3bb31bc15b3cad0f6febc61508) throws Exception {
    return TestHelper.getEnclosingRuntimeMethod(
        new Throwable(v4897b02fddeda3bb31bc15b3cad0f6febc61508));
  }

  /**
   * @param v12525d61e4b10b3e27bc280dd61e56728e3e8c27 A cryptic parameter name for checking whether
   *     the name is available at run-time. The name is from $ echo "desugar-default" | sha1sum
   * @return The reflection representation of the method itself.
   */
  @Foo("custom-attr-value-2")
  default @TyFoo Method inspectCompanionMethodOfDefaultMethod(
      @Foo @TyFoo String v12525d61e4b10b3e27bc280dd61e56728e3e8c27) throws Exception {
    return TestHelper.getEnclosingRuntimeMethod(
        new Throwable(v12525d61e4b10b3e27bc280dd61e56728e3e8c27));
  }

  /** A concrete class that implements an interface with static methods and default methods. */
  final class Concrete implements InterfaceMethodWithParam {}

  /** Test cases for the invocations of interface static methods and default methods. */
  final class MethodInvocations {

    public static long simpleComputeStatic(int x) {
      return InterfaceMethodWithParam.simpleComputeStatic(x);
    }

    public static long simpleComputeDefault(int x) {
      Concrete concrete = new Concrete();
      return concrete.simpleComputeDefault(x);
    }

    public static Method inspectDesugaredDefaultMethod() throws Exception {
      return InterfaceMethodWithParam.class.getDeclaredMethod(
          "inspectCompanionMethodOfDefaultMethod", String.class);
    }

    public static Method inspectCompanionOfStaticMethod() throws Exception {
      return InterfaceMethodWithParam.inspectCompanionMethodOfStaticMethod("random-input");
    }

    public static Method inspectCompanionMethodOfDefaultMethod() throws Exception {
      Concrete concrete = new Concrete();
      return concrete.inspectCompanionMethodOfDefaultMethod("random-input");
    }
  }

  /** A helper class that interface methods with parameters. */
  final class TestHelper {

    /** Returns the runtime-invoked method that encloses {@code enclosedThrowable}. */
    static Method getEnclosingRuntimeMethod(Throwable enclosedThrowable)
        throws ClassNotFoundException, NoSuchMethodException {
      StackTraceElement stackTraceElement = enclosedThrowable.getStackTrace()[0];
      String methodName = stackTraceElement.getMethodName();
      String className = stackTraceElement.getClassName();
      for (Method method : Class.forName(className).getDeclaredMethods()) {
        if (methodName.equals(method.getName())) {
          return method;
        }
      }
      throw new NoSuchMethodException();
    }
  }
}
