package nxm.sys.libxm;

import java.awt.Canvas;
import java.util.ArrayList;

import nxm.sys.lib.Import;
import nxm.sys.lib.Shell;

/** Provides access to the X-Midas Library functions. Do NOT use this class directly, this
    class is for use by the SYS option tree ONLY and is subject to change without notice!

    @author whk / Neon Ngo
    @version $Id: XMidas.java,v 1.12 2012/06/27 14:14:25 ntn Exp $
 */
class XMidas {
  // Central point for Java calls back into X-Midas (C/Fortran). This is class
  // is used for NeXtOpt (NeXtMidas is run as an option tree within X-Midas).

  // mutex lock object to synchronize all the calls into X-Midas APIs, since
  // they are NOT thread safe. Even though there are calls in X-Midas that
  // does "locking" they are still NOT thread safe (they date back to VMS and
  // is NOT POSIX compliant.) Bug 1790
  // @since NeXtMidas 2.6.1 & 2.7.1
  private static Object  mutex = new Object();    // mutex lock object for calling X-Midas APIs (which are thread unsafe)

  private static boolean xmJvmLibLoaded = false;  // Indicates if the jni library has been loaded.

  /* Load the JNI library that implements the native methods */
  static {
    XMidas.loadXmJvmLib();
  }

  /** Hide the default constructor since all methods are static. */
  private XMidas () { }

  /** Gets the mutex lock object to use for calling X-Midas APIs (since it is NOT thread safe).
      All X-Midas API call should synchronize on this lock object, otherwise
      bad things can happen, especially on multi-processor/core machines.
      @return lock object to synchronize before making any X-Midas API call
      @since NeXtMidas 2.6.1 & 2.7.1
   */
  static final Object getLockObj() { return mutex; } // package-private

  /** Loads the xmjvm library.
      @return <code>true</code> if successful, <code>false</code> if not.
   */
  public static boolean loadXmJvmLib () {
    if (!xmJvmLibLoaded) {
      xmJvmLibLoaded = Shell.loadLibrary("SYS", "LIBXM", "libxmjvm");

      if (!xmJvmLibLoaded) {
        System.err.println("ERROR: Unable to load xmjvm library.");
      } else {
        checkXmVersion(); // since 3.1.3
      }
    }

    return xmJvmLibLoaded;
  }

  /** Checks X-Midas runtime version against compiled-time version and print
      warning message if they don't match. Behavior is undefined in this scenario
      since X-Midas common structures may change between versions causing incompatibilities.
      @since 3.1.3
   */
  private static void checkXmVersion () {
    String xmversion = Import.getEnv("XM_VERSION_CODE");  // get X-Midas dotted runtime version for 4.9.2+
    if (xmversion == null) {                              // get X-Midas runtime version prior to 4.9.2
      xmversion = Import.getEnv("XM_VERSION", "unknown"); // e.g. xyz, 491, 445, 389,
      if (xmversion.length() == 3) {                      // convert xyz to dotted version
        xmversion = xmversion.charAt(0) + "." + xmversion.charAt(1) + "." + xmversion.charAt(2);
      }
    }
    if (!getXmVersionBuiltWith().equals(xmversion)) {     // print warning since version don't match
      System.err.println(">>>> ");
      System.err.println(">>>> WARN: NeXtOpt built with X-Midas ["+getXmVersionBuiltWith()
          +"] but is running under ["+xmversion+"].");
      System.err.println(">>>>       Mixing compile-time and runtime versions causes undefined results!");
      System.err.println(">>>> ");
    }
  }

  /** Calls M$RETURN. Only called from XmShellParent. */
  public static native void return_ (int responseId);

  /** Calls M$SYNC. Only called from XmShellParent. */
  public static native void sync (int responseId);

  /** Get the maximum result length */
  public static native int getMaxResultLength ();

  /** Sets an X-Midas result.
      @param label
      @param buf
      @param buflen
      @param modemask
      @param type
      @param attrmask
      @return Length of the result stored, or -1 if &lt;label&gt; is an invalid
              results name -2 if results table is full -3 if result
              &lt;label&gt; exists and is read-only and RA_OVERRIDE isn't set
              in &lt;attrmask&gt;
   */
  public static native int put_result (String label, byte[] buf, int buflen,
          int modemask, char type, int attrmask);

  /** Removes an X-Midas result.
      @param label
      @param attrmask
      @return Number of results deleted or -1 for all results
   */
  public static native int del_result (String label, int attrmask);

  /** Assigns the value to result. Used for assigning a result when one has the string
      representation instead of the numeric representation of the value. This deals with
      the A:FILE or D:FREQ label names which are X-Midas specific.
      @param label The result name
      @param value Value to be assigned to label
      @param attrmask The attribute mask
      @return -1=Invalid result name, -2=results table full, -3=result is
              read-only and override mask is not set
   */
  public static native int str2result (String label, String value, int attrmask);

  /** Find a result in the X-Midas results table
      @param label The result name
   */
  public static native Object rfind (String label);

  public static native StringBuffer getResultNames (String filter);

  /** Calls M$SEND_MSG. */
  public static int sendMsg (byte[] mqh, byte[] mqd, int reader, int mask) {
    return sendMsg0(mqh, mqd, reader, mask);
  }

  private static native int sendMsg0 (byte[] mqh, byte[] mqd, int reader, int mask);

  /** Calls M$GET_MSG. */
  public static int getMsg (byte[] mqh, byte[] mqd, float wait, int mask) {
    int status = 0;

    if (Mt.getNmessage() >= 0) {
      status = getMsg0(mqh, mqd, wait, mask);
    } else {
      //JC4: Disabled warning since it shows up when the primitive is exiting.
      //Shell.getMidasContext().warning("Can not call M$GET_MSG, queue not up.");
    }

    return status;
  }

  public static native int getMsg0 (byte[] mqh, byte[] mqd, float wait, int mask);

  /** Get the full file name given the option, area, root and extensions of a file.
      @param area The name of the subdirectory under the option tree
      @param root The root file name with option - MYPRIM;USER
      @param ext The extension
      @param opt The option tree name
      @return The full file name
   */
  public static native String fileNameOpt (String area, String root, String ext, String opt);

  public static native String form_path (int aux, String id);

  public static native int valid_aux (int aux);

  public static native int define_aux (int aux, String auxpath);

  public static ArrayList<String> create_aux_list () {
    ArrayList<String> alist = new ArrayList<String>();
    for (int i = 0; i < Mc.L_MAXAUXINDEX; i++) {
      String aux;
      if (valid_aux(i) > 0) {
        aux = form_path(i, Mc.getReadId());
        alist.add(aux);
      } else {
        alist.add(null);
      }
    }
    return(alist);
  }

  /** Dispatches an X-Midas command to be run.
      @param cmd  Command line to spawn in parent or subshell.
      @param flag Mode flag bitmask (see X-Midas docs for M$DISPATCH).
      @param mask Subshell shared section mask (see X-Midas docs for M$DISPATCH).
   */
  public static native void dispatch (String cmd, int flag, int mask);


  /** Gets the X-Midas version that the native code was built with.
      @return value of XM_VERSION define in xmversion.h
      @since NeXtMidas 3.1.3
   */
  public static native String getXmVersionBuiltWith ();

  //////////////////////////////////////////////////////////////////////////////
  // Support for displaying X11 graphics from X-Midas in Java windows.
  //////////////////////////////////////////////////////////////////////////////
  /** Indicates if X11 is supported. */
  public static native boolean xIsSupported ();

  /** Attaches an X11 window.
      @param canvas The Java canvas to attach to.
      @param xs     The /XS= id of the X-Midas window.
   */
  public static native long xAttach (Canvas canvas, int xs);

  /** Detaches an X11 window.
      @param canvas The Java canvas to detach from.
      @param id     The /XS= id of the X-Midas window.
   */
  public static native void xDetach (Canvas canvas, int id);

  /** Sets the bounds for an X11 window to match that of the canvas.
      @param canvas The Java canvas the window is attached to.
      @param id     The /XS= id of the X-Midas window.
   */
  public static native void xSetBounds (Canvas canvas, int id);

}
