package nxm.sys.lib;

import nxm.sys.inc.DataTypes;
import nxm.sys.inc.InternalUseOnly;

/** A class to interface to Native function libraries.
    Allows for the turning OFF native library use.  Turning ON use of native
    libraries will have no affect if we can not load them anyway!

  @author Jeff Schoen/ Jay Hallam
*/
@InternalUseOnly("Access to native code can crash the JVM if used incorrectly and may not always be available and can have platform specific behavior")
public class Native {

  static final int    ROUND_DOWN        = Convert.ROUND_DOWN;        // Used from native code
  static final int    ROUND_CEILING     = Convert.ROUND_CEILING;     // Used from native code
  static final int    ROUND_FLOOR       = Convert.ROUND_FLOOR;       // Used from native code
  static final int    ROUND_HALF_UP     = Convert.ROUND_HALF_UP;     // Used from native code
  static final int    ROUND_HALF_EVEN   = Convert.ROUND_HALF_EVEN;   // Used from native code
  static final int    ROUND_UNNECESSARY = Convert.ROUND_UNNECESSARY; // Used from native code
  static final int    ROUND_ROUND       = Convert.ROUND_ROUND;       // Used from native code
  static final double DMAX              = Double.MAX_VALUE;          // Used from native code
  static final float  FMAX              = Float.MAX_VALUE;           // Used from native code
  static final long   XMAX              = Long.MAX_VALUE;            // Used from native code
  static final int    LMAX              = Integer.MAX_VALUE;         // Used from native code
  static final short  IMAX              = Short.MAX_VALUE;           // Used from native code
  static final byte   BMAX              = Byte.MAX_VALUE;            // Used from native code
  static final byte   NMAX              = DataTypes.NMAX;            // Used from native code

  static final byte   IEEE              = DataTypes.IEEE;            // Used from native code
  static final byte   EEEI              = DataTypes.EEEI;            // Used from native code
  static final byte   VAX               = DataTypes.VAX;             // Used from native code

  /** comma-separate list of valid native code that can be used (i.e. for {@link #setUse(String)}. */
  public static final String useList = "IO,Math,Cast,CastX,Convert,Cuda,Other";
  /** constant options for which native code that can be used in {@link #setUse(int)} */
  public static final int IO=0x1, MATH=0x2, CAST=0x4, CASTX=0x8, CONV=0x10, CUDA=0x20, OTHER=0x40;
  /** boolean to turn on and off native code usage */
  public static boolean canUse;
  /** use IO */
  public static boolean useIO;
  /** use Math */
  public static boolean useMath;
  /** use Cast */
  public static boolean useCast;
  /** use CastX */
  public static boolean useCastX;
  /** use Convert */
  public static boolean useConvert;
  /** use Cuda */
  public static boolean useCuda;
  /** use Other */
  public static boolean useOther;

  private static int use, useable;
  private static boolean isInitUseComplete = false; // Since 3.9.0

  /** This constructor was automatically provided by Java,
   *  but it is unneeded as all the methods are static methods.
   *  @deprecated now that Javadocs are required, it is temporarily
   *              being explicitely provided but deprecated
   */
  @Deprecated
  public Native () {}

  /** enable or disable usable native code
   *  @param state boolean false to disable all native code
   */
  public static void setCanUse (boolean state) {
    canUse = state;
    setUse(use);
  }
  /** use a string mask to enable or disable usable native code use
   *  @param usestr String mask indicating use
   */
  public static void setUse (String usestr) {
    setUse( Parser.mask(useList,usestr,use) );
  }
  /** use a int mask to enable or disable usable native code use
   *  @param mask int mask indicating use
   */
  public static void setUse (int mask) {
    use        = (mask & useable);
    useIO      = canUse && (use&IO)!=0;
    useMath    = canUse && (use&MATH)!=0;
    useCast    = canUse && (use&CAST)!=0;
    useCastX   = canUse && (use&CASTX)!=0;
    useConvert = canUse && (use&CONV)!=0;
    useCuda    = canUse && (use&CUDA)!=0;
    useOther   = canUse && (use&OTHER)!=0;
  }
  /** get String representation of Native code that is actually in use
   *  @return String representation of Native code that is actually in use
   */
  public static String getUse() {
    return Parser.mask2s (useList,use);
  }
  /** get String representation of useable native code
   *  @return useable native code - none if noNative
   */
  public static String getUseable() {
    return Parser.mask2s (useList,useable);
  }

  /** initialize the usage of native method groups */
  public static void initUse() {
    if (isInitUseComplete) return; // Since 3.9.0 to make sure this is only executed one time
    canUse = Shell.getPriv(Privileges.PRIV_NATIVE);
    if (canUse) {
      String noNative = Shell.getProperty("NONATIVE");
      canUse = (noNative == null) || StringUtil.isFalse(noNative);
    }
    canUse = canUse && Shell.loadLibrary ("sys","lib","Native");
    isInitUseComplete=canUse; // If canUse is true, we don't want to come back in here
    useable = 0;
    if (canUse) {
      useable |= (IO|MATH|CONV|OTHER);
      if (castInit(new float[10],new double[11],new byte[12],new short[13],new int[14],
	           new float[20],new double[21],new byte[22],new short[23],new int[24])==1) useable |= CAST;
      if (cudaInit()==1) useable |= CUDA;
    }
    setUse("ALL");  // initially try for everything
    setUse("-CUDA");  // except CUDA
  }

  /** Cast a byte array to 'D' (Java double)
   *  @param array byte array
   *  @return Java double array
   */
  public static native double[] castD (byte[] array);
  /** Cast a byte array to 'F' (Java float)
   *  @param array byte array
   *  @return Java float array
   */
  public static native float[] castF (byte[] array);
  /** Cast a byte array to 'L' (Java int)
   *  @param array byte array
   *  @return Java int array
   */
  public static native int[] castL (byte[] array);
  /** Cast a byte array to 'I' (Java short)
   *  @param array byte array
   *  @return Java short array
   */
  public static native short[] castI (byte[] array);

  // Revert previously cast array to a byte array
  /** Revert array previously cast to 'D' back to byte array
   *  @param array array to revert
   *  @return byte array
   */
  public static native byte[] uncastD (Object array);
  /** Revert array previously cast to 'F' back to byte array
   *  @param array array to revert
   *  @return byte array
   */
  public static native byte[] uncastF (Object array);
  /** Revert array previously cast to 'L' back to byte array
   *  @param array array to revert
   *  @return byte array
   */
  public static native byte[] uncastL (Object array);
  /** Revert array previously cast to 'I' back to byte array
   *  @param array array to revert
   *  @return byte array
   */
  public static native byte[] uncastI (Object array);

  private static native int castInit (Object F, Object D, Object B, Object I, Object L,
  				      Object f, Object d, Object b, Object i, Object l);
  private static native int cudaInit ();

  /** package-private, only used by Convert, parameters match those in Convert.type. */
  static native int type (Object buf1, int off1, byte type1,
                          Object buf2, int off2, byte type2, int size,int round,boolean saturating);

  /** package-private, only used by Convert, parameters match those in
      {@link Convert#rep(long, int, byte, int, byte, byte)}.
      @since NeXtMidas 2.7.0
   */
  static native int rep (long lbuf, int off, byte type, int n, byte repa, byte repb);

  /** package-private, only used by Convert on sub-record of interest (soi), parameters
      match those in {@link Convert#rep(long, int, byte, int, byte, byte, int, int)}.
      @since NeXtMidas 2.7.0
   */
  static native int repsoi (long lbuf, int off, byte type, int n, byte repa, byte repb, int nn, int inc);

  /** @deprecated Since NeXtMidas 2.5.2: Use the methods in convert rather than calling this method
      directly. This method will cause an error if a native conversion is not available.
      @param buf1  Object array to convert
      @param off1  Offset (in scalar elements of type1) into Input buffer, 0..n
      @param type1 Type of buf1
      @param buf2  Output byte array
      @param off2  Offset (in scalar elements of type1) into Output buffer, 0..n
      @param type2 Type of buf2
      @param size  Number of elements to convert
      @return status
   */
  @Deprecated public static int cast (Object buf1, int off1, byte type1,
                          Object buf2, int off2, byte type2, int size) {
    return type(buf1,off1,type1,buf2,off2,type2,size,Convert.ROUND_ROUND,true);
  }

  /** dumps the contents of a class in hex
   *  @param obj   class to be dumped (via printf)
   *  @param words size
   */
  public static native void dumpClass (Object obj, int words);

  /** get the memory address of different areas of an object
   *  @param obj  object on which to get memory address
   *  @param item area
   *  @return memory addres
   */
  public static native long getAddress (Object obj, int item);

  /** Converts a float array to 10 bit dB using fast exponent algorithm
   *  @param array source float array
   *  @param size  size of array
   *  @param flags unused
   */
  public static native void dbf (Object array, int size, int flags);

  /** Perform a single precision square root function
      @param value takes square root of this value
      @return square root of value */
  public static native float sqrtf (float value);

  /** Perform a single precision natural logarithm function
      @param value natural log of this value
      @return natural log of value */
  public static native float logf (float value);

  /** Copy contents of one array to another
   *  @param array1 source array
   *  @param array2 destination array
   *  @param bytes  number of bytes
   */
  public static native void copy (Object array1, Object array2, int bytes);

  /** Alloc native memory buffer.
      @param bytes number of bytes to allocate
      @return pointer
      @since 3.5.0 */
  public static native long alloc (long bytes);

  /** Free native memory buffer.
   *  @param handle handle to native memory buffer
   *  @return 0 (zero) on success, -1 on error.
   *  @since 3.5.0 */
  public static native int free (long handle);

  /** Copy data from a native pointer to a Java array
   *  @param pointer1 source pointer
   *  @param off1     source starting offset
   *  @param array2   destination array
   *  @param off2     destination starting offset
   *  @param bytes    number of bytes
   */
  public static native void p2ja (long pointer1, int off1, Object array2, int off2, int bytes);

  /** Copy data from a Java array to a native pointer
   *  @param array1   source array
   *  @param off1     source starting offset
   *  @param pointer2 destination pointer
   *  @param off2     destination starting offset
   *  @param bytes    number of bytes
   */
  public static native void ja2p (Object array1, int off1, long pointer2, int off2, int bytes);

  /** Copy data from a native pointer to a native pointer
   *  @param pointer1 source pointer
   *  @param off1     source starting offset
   *  @param pointer2 destination pointer
   *  @param off2     destination starting offset
   *  @param bytes    number of bytes
   */
  public static native void p2p (long pointer1, int off1, long pointer2, int off2, int bytes);

  /** Copy data from native pointer to native pointer and advance with different strides.
      @param pointer1 source pointer
      @param off1 source starting offset
      @param stride1 source stride
      @param pointer2 destination pointer
      @param off2 destination starting offset
      @param stride2 destination stride
      @param bytesc bytes to copy per stride
      @param bytest total number of bytes to copy
      @since NeXtMidas 3.5.0
  */
  public static native void p2ps (long pointer1, int off1, int stride1, long pointer2, int off2, int stride2, int bytesc, int bytest);

  /** Open a file for read|write
   *  @param fname file name
   *  @param dir   access mode
   *  @return native file descriptor
   */
  public static native int fopen (String fname, int dir);

  /** Move current read/write pointer in a file
   *  @param fd     Native file descriptor (from return value of {@link #fopen(String, int)}.
   *  @param offset new pointer offset
   *  @return current file position
   */
  public static native long fseek (int fd, long offset);

  /** Read from a file into a pointer.
      NOTE: The return value intentionally matches the native OS's file read(..) API,
      i.e. it DOES NOT match the return value of the IOResource's read(..).
      @param fd    Native file descriptor (from return value of {@link #fopen(String, int)}.
      @param lbuf  Native pointer to buffer.
      @param boff  The offset into <code>buf</code> to start at.
      @param bytes The number of bytes to try reading.
      @return number of bytes read, 0 for End-Of-File (EOF), -1 for error.
    */
  public static native int fread (int fd, long lbuf, int boff, int bytes);

  /** Write to a file from a pointer
      @param fd    Native file descriptor (from return value of {@link #fopen(String, int)}.
      @param lbuf  Native pointer to buffer.
      @param boff  The offset into <code>buf</code> to start at.
      @param bytes The number of bytes to write
      @return number of bytes written
    */

  public static native int fwrite (int fd, long lbuf, int boff, int bytes);

  /** Read from a file into a java array.
      NOTE: The return value intentionally matches the native OS's file read(..) API,
      i.e. it DOES NOT match the return value of the IOResource's read(..).
      @param fd   file descriptor (from return value of {@link #fopen(String, int)}.
      @param buf  byte array buffer to read data into.
      @param boff byte offset into the byte array (buf) to start writing at.
      @param bytes The number of bytes to try reading.
      @return number of bytes read, 0 for End-Of-File (EOF), -1 for error.
   */
  public static native int freadb (int fd, Object buf, int boff, int bytes);

  /** Write to a file from a java array
      @param fd   file descriptor (from return value of {@link #fopen(String, int)}.
      @param buf  byte array buffer to read data from.
      @param boff byte offset into the byte array (buf) to start reading from.
      @param bytes The number of bytes to write
      @return number of bytes written
   */
  public static native int fwriteb (int fd, Object buf, int boff, int bytes);

  /** Close a native file
      @param fd file descriptor (from return value of {@link #fopen(String, int)}. */
  public static native void fclose (int fd);

  /** Change working directory
      @param path for new working directory
      @return 0 (zero) on success, -1 on error. */
  public static native int chdir (String path);

  /** Get system file system stats.
      @param path file system path to status
      @param buf  output buf
      @return 0 (zero) on success, -1 on error.
    */
  public static native int statfs (String path, Object buf);

  /** Get system CPU, memory, and process stats
   *  @param flags unused
   *  @param buf   output buffer
   *  @param boff  buffer offset
   *  @param bytes unused
   *  @return status
   */
  public static native int top (int flags, Object buf, int boff, int bytes);

  /** This will copy a Java String on to an existing Fortran string. This works
      by converting the Java String to a UTF-8 C string (<code>char*</code>) and
      then copying each of the letters in the C string to the location of the
      Fortran string; effectively overwriting the existing Fortran string with
      the letters in the C string.<br>
      <br>
      When copying the C string to the location of the Fortran string no more
      than <code>lfstr</code> letters of the C string will be copied over (this
      is to prevent writing outside the allocated memory of the Fortran). Thus
      it is vital that <code>lfstr</code> be less than or equal to the actual
      number of bytes that have been allocated to the Fortran string.<br>
      <br>
      Note that if the actual length of the C string (as determined by the C
      function <code>strlen(char*)</code>) is less than <code>lfstr</code> then
      the number of characters copied from the C string to the Fortran string
      will be equal to the length of the C string.

      @param jstr  The Java String to copy onto the Fortran string.

      @param fstr  The native pointer to the location of the Fortran string.

      @param lfstr The maximum string length to copy to the Fortran
                   string. <b>(This must be less than or equal to the number
                   of bytes allocated to the Fortran string.)</b>

      @return The number of characters actually copied to the Fortran string.
              This will be equal to the lesser of <code>lfstr</code> or the
              number of characters in the String after being converted to a
              UTF-8 C string (as determined by <code>strlen(char*)</code>).
  */
  public static native int jstr2fstr (String jstr, long fstr, int lfstr);

}
