/****************************************************************************
 *  
 *  Main.java
 *
 *  This code is used as a driver program to demonstrate the native methods
 *  declared in Native.java.
 *
 *  The countPrimes algorithm was taken from Microsoft's documentation of
 *  their Raw Native Interface (RNI).
 *
 *
*/
public class Main {
  static int a, b;
  static final int size = 1000000;
  static final int array_size = 10000;
  static final int func_calls = 1000000;
  static final int rectangles = 500;
  static byte[] c = new byte[size];
  static byte[] d = new byte[array_size];
  public static void main(String[] args)
  {
    String str;
    long start, stop, elapsed;
    int t;
    int x;
    byte value = 123;

      // static member
    Native.z = 47;

      // Create an object from Native.java
    Native nat = new Native();

      // Print version
    nat.printVersion();

      // Test all data types and show that Delphi floats won't work correctly with JNI methods
      // that can accept a variable number of arguments.
    System.out.println("This should print out the following (but the float field will be incorrect):");
    System.out.print("boolean = true, byte = 11, char = J, int = 123456, ");
    System.out.println("long = 987654321, float = 47.56, double = 898.87678 ");
    nat.testAllTypesD(true, (byte)11, 'J', 123456, 987654321L, 47.56F, 898.87678D);

    System.out.println("\n******* Test A: Native.multiplyIntegers(3, 7)");
    System.out.println("Multiply: 3 x 7 = " + nat.multiplyIntegers(3, 7));

      // Simple test
    System.out.println("\n******* Test 1: Native.displayHelloWorld");
    nat.displayHelloWorld();

      // Prints out all members of the Java Object by calling
      // back into the nat object's 'toString' method
    System.out.println("\n******* Test 2: Native.toStringWithPrint");
    nat.toStringWithPrint();

      // Prints a Java string passed in and also returns a Java string
    System.out.println("\n******* Test 3: Native.printLine");
    str = "This is from Java";
    str = nat.printLine(str);
    System.out.println("In Java code, string is: " + str);

      // Prints out the w, x, y, and z members of the nat object
      // all from native code
    System.out.println("\n******* Test 4: Native.printWXYZ");
    nat.printWXYZ();

      // An array of Rectangles is created and passed to the native code
      // to be printed
    System.out.println("\n******* Test 5: Native.printObjectArray(Rectangle[3])");
    java.awt.Rectangle[] rects = new java.awt.Rectangle[3];
    for (int i = 0; i < rects.length; i++)
      rects[i] = new java.awt.Rectangle(0, 0, 4 * i, 3 * i);
    nat.printObjectArray(rects, true);

      // An array of Native objects are passed to Delphi for printing
      // Demonstrates how the native method works on any 'thing' derived
      // from a Java Object.
    System.out.println("\n******* Test 6: Native.printObjectArray(Native[2])");
    Native[] nats = new Native[2];
    for (int i = 0; i < nats.length; i++)
      nats[i] = new Native();
    nat.printObjectArray(nats, true);

      // The Delphi code allocates and initializes an array of Rectangles
      // that get passed back to Java and printed here.
    System.out.println("\n******* Test 7: Native.returnRectArray(3)");
    java.awt.Rectangle[] rects2 = nat.returnRectArray(3);
    for (int i = 0; i < rects2.length; i++)
      System.out.println(rects2[i].toString());

      // Some exception handling examples
      // Notice that we must catch 'Throwable' objects and not
      // Exception objects. It turns out that these tests are
      // actually returning Error objects, but both Error and
      // Exception derive from Throwable.
    System.out.println("\n******* Test 8: Native.handleException");
    try
    {
      nat.handleException();
    }
    catch (Throwable e)
    {
      System.out.println("Exception in Native code not handled: " + e);
    }

    System.out.println("\n******* Test 9: Native.causeException");
    try
    {
      nat.causeException();
    }
    catch (Throwable e)
    {
      System.out.println("Exception in Native code not handled: " + e);
    }


      // 12-18-98
    System.out.println("\n******* Test 10: Native.pass2DByteArray(array2D)");
    int dim1 = 2, dim2 = 3;
    byte count = 0;
    byte[][] array2D = new byte[dim1][dim2];
    for (int i = 0; i < dim1; i++)
      for (int j = 0; j < dim2; j++)
        array2D[i][j] = ++count;

      // Call Native code to initialize an array to a specific value many times
    nat.pass2DByteArray(array2D);

    System.out.println("\nIn Java printing array that was changed by native method.");
    for (int i = 0; i < dim1; i++)
      for (int j = 0; j < dim2; j++)
        System.out.println(i + "," + j + " = " + array2D[i][j]);


    System.out.println("\n\n********** Timings **********\n");

    System.out.print("Native countPrimes up to " + size + " ....");
    start = System.currentTimeMillis();
    nat.countPrimes(c);
    stop = System.currentTimeMillis();
    elapsed = stop - start;
    System.out.println(" " + elapsed + " milliseconds");

    System.out.print("  Java countPrimes up to " + size + " ....");
    start = System.currentTimeMillis();
    countPrimes(c);
    stop = System.currentTimeMillis();
    elapsed = stop - start;
    System.out.println(" " + elapsed + " milliseconds");


      // Call a Native 'void func(void)'
    System.out.print("Native function calls " + func_calls + " ....");
    start = System.currentTimeMillis();
    for (x = 0; x < func_calls; x++)
      nat.VoidVoid();
    stop = System.currentTimeMillis();
    elapsed = stop - start;
    System.out.println(" " + elapsed + " milliseconds");

      // Call a Java method 'void method(void)'
    System.out.print("  Java function calls " + func_calls + " ....");
    start = System.currentTimeMillis();
    for (x = 0; x < func_calls; x++)
      VoidVoid();
    stop = System.currentTimeMillis();
    elapsed = stop - start;
    System.out.println(" " + elapsed + " milliseconds");

    t = 1000;

      // Call Native code to initialize an array to a specific value many times
    System.out.print("Native initialize array[" + array_size + "] " + t + " times ....");
    start = System.currentTimeMillis();
    nat.initializeByteArray(d, t, value);
    stop = System.currentTimeMillis();
    elapsed = stop - start;
    System.out.println(" " + elapsed + " milliseconds");


      // Initialize an array to a specific value many times within Java
    System.out.print("  Java initialize array[" + array_size + "] " + t + " times ....");
    start = System.currentTimeMillis();
    for (int i = 0; i < t; i++)
      for (int j = 0; j < d.length; j++)
        d[j] = value;
    stop = System.currentTimeMillis();
    elapsed = stop - start;
    System.out.println(" " + elapsed + " milliseconds");

      // From native code, call a constructor for a Java object many times
    java.awt.Rectangle[] rects4 = new java.awt.Rectangle[rectangles];
    for (int i = 0; i < rects4.length; i++)
      rects4[i] = new java.awt.Rectangle(0, 0, 4 * i, 3 * i);
    System.out.print("Native callbacks into Java constructor " + rectangles + " rectangles ....");
    start = System.currentTimeMillis();
    nat.printObjectArray(rects4, false);
    stop = System.currentTimeMillis();
    elapsed = stop - start;
    System.out.println(" " + elapsed + " milliseconds");

      // Call a constructor from Java many times
    java.awt.Rectangle[] rects5 = new java.awt.Rectangle[rectangles];
    for (int i = 0; i < rects5.length; i++)
      rects5[i] = new java.awt.Rectangle(0, 0, 4 * i, 3 * i);
    System.out.print("Java method calls to constructor " + rectangles + " rectangles ....");
    start = System.currentTimeMillis();
    printObjectArray(rects5, false);
    stop = System.currentTimeMillis();
    elapsed = stop - start;
    System.out.println(" " + elapsed + " milliseconds");

      // Call a Java 'void method(void)' from Native code many times
    System.out.print("Native callbacks to Java void method " + func_calls + " ....");
    start = System.currentTimeMillis();
    nat.callbackVoid(func_calls);
    stop = System.currentTimeMillis();
    elapsed = stop - start;
    System.out.println(" " + elapsed + " milliseconds");

      // Call a Native 'void func(void)' from Java many times
    System.out.print("Java calls to Native void function " + func_calls + " ....");
    start = System.currentTimeMillis();
    for (x = 0; x < func_calls; x++)
      nat.VoidVoid();
    stop = System.currentTimeMillis();
    elapsed = stop - start;
    System.out.println(" " + elapsed + " milliseconds");

  }

    // Test method for function-call overhead
  public static void VoidVoid()
  {
    return;
  }

    // Sieving prime numbers
  public static long countPrimes(byte[] array)
  {
    int count = 0;
    int i;

    for (i = 0; i < array.length; i++)
      array[i] = 1;

    for (i = 2; i < array.length; i++)
    {
      if (array[i] != 0)
      {
        int k;
        for (k = i + i; k < array.length; k += i)
          array[k] = 0;

        count++;
      }
    }

    return count;
  }

  public static java.awt.Rectangle[] returnRectArray(int size)
  {
      // Allocate the array of Rectangles
    java.awt.Rectangle[] array = new java.awt.Rectangle[size];

      // Now initialize each one to a Rectangle
    for (int i = 0; i < size; i++)
    {
        // Create a new Rectangle object
      java.awt.Rectangle element = new java.awt.Rectangle(0, 0, 5 * i, 10 * i);

        // Assign the Rectangle to an element of the array
      array[i] = element;
    }
      // Return whole array to caller
    return array;
  }


  public static void printObjectArray(Object[] objArray, boolean Print)
  {
      // Loop the the array of objects and print out each one using
      // the 'toString' method of its ancestor class Object
    for (int i = 0; i < objArray.length; i++)
    {
      String s = objArray[i].toString();
      if (Print)
        System.out.println(s);

   }
  }

}