import org.checkerframework.checker.nullness.qual.*;

/*
 * The test checks annotations in multidimention arrays.
 * Each array dimention is beeing annotated with eather @Nullable or @NonNull
 * to check error is thrown if assignment type is incompatible on eather
 * array level.
 * Tests uses 3 dimentional arrays. Each annotaion combination is used once starting
 * with @Nullable [] @Nullable [] @Nullable [] and
 * ends with @NonNull [] @NonNull [] @NonNull [].
 *
 * Test has 8 methods that returns 3-dimentional arrays where each dimention is annotated
 * with eather @Nullable or @NonNull.
 *
 * Test containg 8 methods where all variables are beeing assign with one of arrays from
 * method that returns annotated arrays.
 *
 * Errors are expected if one or more array levels in a declaration are annotated with @NonNull, but
 * in assignment are annotated with @Nullable.
 */
public class MultidimentionalArrayAnnotationTest {

  int numb = 1;

  // Declared 8 3-dimentional variables.
  Object @Nullable [] @Nullable [] @Nullable [] obj1 = new Object[numb][numb][numb];
  Object @NonNull [] @Nullable [] @Nullable [] obj2 = new Object[numb][numb][numb];
  Object @Nullable [] @NonNull [] @Nullable [] obj3 = new Object[numb][numb][numb];
  Object @Nullable [] @Nullable [] @NonNull [] obj4 = new Object[numb][numb][numb];
  Object @NonNull [] @NonNull [] @Nullable [] obj5 = new Object[numb][numb][numb];
  Object @NonNull [] @Nullable [] @NonNull [] obj6 = new Object[numb][numb][numb];
  Object @Nullable [] @NonNull [] @NonNull [] obj7 = new Object[numb][numb][numb];
  Object @NonNull [] @NonNull [] @NonNull [] obj8 = new Object[numb][numb][numb];

  /*
   * Call to method 1 that returns Object @NonNull [] @NonNull [] @NonNull [].
   * Errors are not expected.
   */
  void callTomethod1() {
    obj1 = method1();
    obj2 = method1();
    obj3 = method1();
    obj4 = method1();
    obj5 = method1();
    obj6 = method1();
    obj7 = method1();
    obj8 = method1();
  }

  /*
   * Call to method 2 that returns Object @Nullable [] @NonNull [] @NonNull [].
   */
  void callTomethod2() {
    obj1 = method2();
    // :: error: (assignment)
    obj2 = method2();
    obj3 = method2();
    obj4 = method2();
    // :: error: (assignment)
    obj5 = method2();
    // :: error: (assignment)
    obj6 = method2();
    obj7 = method2();
    // :: error: (assignment)
    obj8 = method2();
  }

  /*
   * Call to method 3 that returns Object @NonNull [] @Nullable [] @NonNull [].
   */
  void callTomethod3() {
    obj1 = method3();
    obj2 = method3();
    // :: error: (assignment)
    obj3 = method3();
    obj4 = method3();
    // :: error: (assignment)
    obj5 = method3();
    obj6 = method3();
    // :: error: (assignment)
    obj7 = method3();
    // :: error: (assignment)
    obj8 = method3();
  }

  /*
   * Call to method 4 that returns Object @NonNull [] @NonNull [] @Nullable [].
   */
  void callTomethod4() {
    obj1 = method4();
    obj2 = method4();
    obj3 = method4();
    // :: error: (assignment)
    obj4 = method4();
    obj5 = method4();
    // :: error: (assignment)
    obj6 = method4();
    // :: error: (assignment)
    obj7 = method4();
    // :: error: (assignment)
    obj8 = method4();
  }

  /*
   * Call to method 5 that returns Object @Nullable [] @Nullable [] @NonNull [].
   */
  void callTomethod5() {
    obj1 = method5();
    // :: error: (assignment)
    obj2 = method5();
    // :: error: (assignment)
    obj3 = method5();
    obj4 = method5();
    // :: error: (assignment)
    obj5 = method5();
    // :: error: (assignment)
    obj6 = method5();
    // :: error: (assignment)
    obj7 = method5();
    // :: error: (assignment)
    obj8 = method5();
  }

  /*
   * Call to method 6 that returns Object @Nullable [] @NonNull [] @Nullable [].
   */
  void callTomethod6() {
    obj1 = method6();
    // :: error: (assignment)
    obj2 = method6();
    obj3 = method6();
    // :: error: (assignment)
    obj4 = method6();
    // :: error: (assignment)
    obj5 = method6();
    // :: error: (assignment)
    obj6 = method6();
    // :: error: (assignment)
    obj7 = method6();
    // :: error: (assignment)
    obj8 = method6();
  }

  /*
   * Call to method 7 that returns Object @NonNull [] @Nullable [] @Nullable [].
   */
  void callTomethod7() {
    obj1 = method7();
    obj2 = method7();
    // :: error: (assignment)
    obj3 = method7();
    // :: error: (assignment)
    obj4 = method7();
    // :: error: (assignment)
    obj5 = method7();
    // :: error: (assignment)
    obj6 = method7();
    // :: error: (assignment)
    obj7 = method7();
    // :: error: (assignment)
    obj8 = method7();
  }

  /*
   * Call to method 8 that returns Object @Nullable [] @Nullable [] @Nullable [].
   */
  void callTomethod8() {
    obj1 = method8();
    // :: error: (assignment)
    obj2 = method8();
    // :: error: (assignment)
    obj3 = method8();
    // :: error: (assignment)
    obj4 = method8();
    // :: error: (assignment)
    obj5 = method8();
    // :: error: (assignment)
    obj6 = method8();
    // :: error: (assignment)
    obj7 = method8();
    // :: error: (assignment)
    obj8 = method8();
  }

  Object[][][] method1() {
    return new Object[numb][numb][numb];
  }

  Object @Nullable [][][] method2() {
    return new Object[numb][numb][numb];
  }

  Object[] @Nullable [][] method3() {
    return new Object[numb][numb][numb];
  }

  Object[][] @Nullable [] method4() {
    return new Object[numb][numb][numb];
  }

  Object @Nullable [] @Nullable [][] method5() {
    return new Object[numb][numb][numb];
  }

  Object @Nullable [][] @Nullable [] method6() {
    return new Object[numb][numb][numb];
  }

  Object[] @Nullable [] @Nullable [] method7() {
    return new Object[numb][numb][numb];
  }

  Object @Nullable [] @Nullable [] @Nullable [] method8() {
    return new Object[numb][numb][numb];
  }
}
