/* Native methods for calling X-Midas APIs while running under NeXtOpt.
   See XMidas.java for more information.
*/
#include <midas.h>
#include <mjackets.h>
#include <nxm_sys_libxm_XMidas.h>
#include <qmessdef.h>
#include <tables.h>
#include <string.h>
#include "jni.h"
#include "jawt.h"
#include "jawt_md.h"

/* Include XM compatibility mappings, used so we can support various
   (incompatible) versions of X-Midas.
 */
#include "xmcompat.h"

#define USE_IPC 1

extern MCSTRUCT **mc;            /* mc is loaded in shared.c */
#if USE_IPC
extern int      jsm_active;
extern int_u4   jni$sync(int);
extern int_u4   jni$return(int);
#endif

int_4 m_rfilter   (const char *filter, char *name, int_4 nlen, char type[1],
             int_u1 *attr, void *value, int_4 vlen, int_u4 *offset);
int_4 m$valid_aux_ (const int_4*);
void  m$dispatch_ (const char[], const int_4*, const int_4*, fstring_len_t);
int_4 mx$window_id_ (const int_4*, const int_4*);

#if _UNIX
#ifndef m$form_path
#define m$form_path m$form_path_
#endif
int_4 m$form_path(int_4 *, const char *, char *, fstring_len_t,fstring_len_t);
int_4 m_form_path(int_4 iaux, const char *id, char *string, int_4 ls)
{
  int_4 length;
  length = m$form_path(&iaux,id,string,strlen(id),ls);
  strf2c(string, ls, string, ls, F2C_TRIM);
  return length;
}
#else
int_4 m$form_path(int_4 *,VAXstr *, VAXstr *);
int_4 m_form_path(int_4 iaux, const char *id, char *string, int_4 ls)
{
  int_4 length;
  VAXstr V1,V2;
  C2VAXSTR(id, strlen(id), V1);
  C2VAXSTR(string, ls, V2);
  length = m$form_path(&iaux,&V1,&V2);
  strf2c(string, ls, string, ls, F2C_TRIM);
  return length;
}
#endif


#if _UNIX
#ifndef m$define_aux
#define m$define_aux m$define_aux_
#endif
int_4 m$define_aux(const int_4*, char *, fstring_len_t);
int_4 m_define_aux(const int_4 aux, char *auxpath)
{
  int_4 stat;
  stat = m$define_aux(&aux, auxpath, strlen(auxpath));
  return stat;
}
#endif

#ifdef __cplusplus
extern "C" {
#endif


#ifdef XM371
  extern MUSTRUCT *mu;
  extern MTSTRUCT **mt;
#else
  extern MUNIQUE *mu; /* mu is loaded in shared.c, will set to point to the unique block on initialization */
  extern MTABLES **mt;
#endif

JNIEXPORT void JNICALL Java_nxm_sys_libxm_XMidas_sync(JNIEnv *env, jclass obj, jint responseId) {
#if USE_IPC
  if (jsm_active == 0 || responseId == 0) { responseId = 0; }

  jni$sync(responseId);
#else
  fprintf(stderr, ">>>>>>>>>>>>>>> XMidas.c: need to call M$SYNC\n");
#endif
}

JNIEXPORT void JNICALL Java_nxm_sys_libxm_XMidas_return_1(JNIEnv *env, jclass obj, jint responseId) {
#if USE_IPC
  if (jsm_active == 0 || responseId == 0) { responseId = 0; }

  jni$return(responseId);
#else
  fprintf(stderr, ">>>>>>>>>>>>>>> XMidas.c: need to call M$RETURN\n");
#endif
}

JNIEXPORT jint JNICALL Java_nxm_sys_libxm_XMidas_sendMsg0(JNIEnv *env, jclass cls,
  jbyteArray mqh, jbyteArray mqd, jint reader, jint mask) {

  jbyte *nmqh  = (*env)->GetByteArrayElements(env, mqh, NULL);
  jbyte *nmqd  = (*env)->GetByteArrayElements(env, mqd, NULL);
  jint  lmqd   = (*env)->GetArrayLength(env, mqd);
  jint  status = 1;

  m_send_msg(nmqh, nmqd, reader, (int_u4)mask);

  (*env)->ReleaseByteArrayElements(env, mqh, nmqh, 0);
  (*env)->ReleaseByteArrayElements(env, mqd, nmqd, 0);

  return status;
}

JNIEXPORT jint JNICALL Java_nxm_sys_libxm_XMidas_getMsg0(JNIEnv *env, jclass cls,
  jbyteArray mqh, jbyteArray mqd, float wait, jint mask) {

  jbyte *nmqh  = (*env)->GetByteArrayElements(env, mqh, NULL);
  jbyte *nmqd  = (*env)->GetByteArrayElements(env, mqd, NULL);
  jint  lmqd   = (*env)->GetArrayLength(env, mqd);
  jint  status = -1;

  status = m_get_msg(nmqh, nmqd, lmqd, wait, (int_u4)mask);

  (*env)->ReleaseByteArrayElements(env, mqh, nmqh, 0);
  (*env)->ReleaseByteArrayElements(env, mqd, nmqd, 0);

  return status;
}

JNIEXPORT jint JNICALL Java_nxm_sys_libxm_XMidas_getMaxResultLength
  (JNIEnv *env, jclass obj) {
  jint retVal;
  retVal = L_max_ascii;
  return (retVal);
}

JNIEXPORT jint JNICALL Java_nxm_sys_libxm_XMidas_put_1result
  (JNIEnv *env, jclass obj, jstring label, jbyteArray bbuf, jint buflen,
   jint modemask, jchar type, jint attrmask) {
  char  *str   = NULL;
  jbyte *buf   = NULL;
  char   btype = type;
  jint   retVal;

  str = (char*)(*env)->GetStringUTFChars(env, label, 0);
  buf = (*env)->GetByteArrayElements(env, bbuf, 0);

  retVal = m_put_result(str, buf, buflen, modemask, &btype, attrmask);

  (*env)->ReleaseStringUTFChars(env,label,str);
  (*env)->ReleaseByteArrayElements(env, bbuf, buf, 0);
  return retVal;
}

JNIEXPORT jint JNICALL Java_nxm_sys_libxm_XMidas_del_1result
  (JNIEnv *env, jclass obj, jstring label, jint attrmask) {
  char  *str   = NULL;
  jint   retVal;
  int_u1 mask  = (int_u1)(0xFF & attrmask);

  str = (char*)(*env)->GetStringUTFChars(env, label, 0);

  retVal = m_del_result(str, mask);

  (*env)->ReleaseStringUTFChars(env,label,str);
  return retVal;
}

JNIEXPORT jint JNICALL Java_nxm_sys_libxm_XMidas_str2result
  (JNIEnv *env, jclass obj, jstring label, jstring resVal, jint attrmask) {
   char *strLabel;
   char *strValue;
   jint retVal;

   strValue = (char*)(*env)->GetStringUTFChars(env, resVal, 0);
   strLabel = (char*)(*env)->GetStringUTFChars(env, label, 0);

   retVal = m_str2result(strLabel, strValue, attrmask);

   (*env)->ReleaseStringUTFChars(env,label,strLabel);
   (*env)->ReleaseStringUTFChars(env,resVal,strValue);
   return retVal;
}

JNIEXPORT jstring JNICALL Java_nxm_sys_libxm_XMidas_fileNameOpt
  (JNIEnv *env, jclass obj, jstring jarea, jstring jroot, jstring jext, jstring jopt) {
  const char *strArea, *strRoot, *strExt, *strOpt;
  char strFullName[1024];

  jint fnlen;

  /* Convert Java Strings to c strings */
  strArea = (*env)->GetStringUTFChars(env, jarea, 0);
  strRoot = (*env)->GetStringUTFChars(env, jroot, 0);
  strExt  = (*env)->GetStringUTFChars(env, jext,  0);
  strOpt  = (*env)->GetStringUTFChars(env, jopt,  0);

  /* Call the X-Midas c-code for performing this function */
  fnlen = m_filenameopt (strArea, strRoot, strExt, strFullName, strOpt, 1024);

  /* Release the memory used by the strings */
  (*env)->ReleaseStringUTFChars(env,jarea,strArea);
  (*env)->ReleaseStringUTFChars(env,jroot,strRoot);
  (*env)->ReleaseStringUTFChars(env,jext,strExt);
  (*env)->ReleaseStringUTFChars(env,jopt,strOpt);

  /* Convert the C string into a Java string */
  return ((*env)->NewStringUTF (env, strFullName));
}

JNIEXPORT jobject JNICALL Java_nxm_sys_libxm_XMidas_rfind
  (JNIEnv *env, jclass obj, jstring label) {
  static int bMapped = FALSE; /* Have we mapped to the controls section? */
  char     *strLabel;
  char      type;
  char      aval[L_max_ascii+1];
  double    dval;
  jclass    cls;
  jmethodID mid;
  jobject   val = NULL;

  /* Amit Nithian (5/31/2007) - Prevents segmentation faults by forcefully */
  /*   mapping to the control widgets shared memory buffer before getting  */
  /*   the result parameter. This prevents the segmentation fault when     */
  /*   trying to access a result parameter associated with a CTAG.         */
  if (!bMapped && (Mc->mode != 0) && (Mt->nwidget >= 0)) {
    fprintf(stderr, "XMIDAS.C: Mapping to LS_cntrl_widgets\n");
    m_map_section(LS_cntrl_widgets,2);
    bMapped = TRUE;
  }

  strLabel = (char*)(*env)->GetStringUTFChars(env, label, 0);
  type = m_rfind(strLabel, aval, &dval, strlen(strLabel), L_max_ascii);

  switch (type) {
    case 'A':
      aval[(jint)dval] = '\0';
      val = (*env)->NewStringUTF(env, aval);
      break;
    case 'D':
      cls = (*env)->FindClass(env, "java/lang/Double");
      mid = (*env)->GetMethodID(env, cls, "<init>", "(D)V");
      val = (*env)->NewObject(env, cls, mid, (double)dval);
      break;
    case 'F':
      cls = (*env)->FindClass(env, "java/lang/Float");
      mid = (*env)->GetMethodID(env, cls, "<init>", "(F)V");
      val = (*env)->NewObject(env, cls, mid, (float)dval);
      break;
    case 'X': /* Since 2.5.0 */
      cls = (*env)->FindClass(env, "java/lang/Long");
      mid = (*env)->GetMethodID(env, cls, "<init>", "(J)V");
      val = (*env)->NewObject(env, cls, mid, (jlong)dval);
      break;
    case 'L':
      cls = (*env)->FindClass(env, "java/lang/Integer");
      mid = (*env)->GetMethodID(env, cls, "<init>", "(I)V");
      val = (*env)->NewObject(env, cls, mid, (jint)dval);
      break;
    case 'I':
      cls = (*env)->FindClass(env, "java/lang/Short");
      mid = (*env)->GetMethodID(env, cls, "<init>", "(S)V");
      val = (*env)->NewObject(env, cls, mid, (jshort)dval);
      break;
    case 'B':
      cls = (*env)->FindClass(env, "java/lang/Byte");
      mid = (*env)->GetMethodID(env, cls, "<init>", "(B)V");
      val = (*env)->NewObject(env, cls, mid, (jbyte)dval);
      break;
    case ' ':
      /* Not Found */
      break;
    default:
      fprintf(stderr, "XMidas.c: WARNING: M$RFIND returned type='%c' for result '%s'.\n",type,strLabel);
      fprintf(stderr, "XMidas.c: WARNING: Can not convert type='%c' to a Java object.\n",type,strLabel);
      break;
  }
  (*env)->ReleaseStringUTFChars(env,label,strLabel);

  return val;
}

JNIEXPORT jobject JNICALL Java_nxm_sys_libxm_XMidas_getResultNames
  (JNIEnv *env, jclass cls, jstring filter) {
  jint stat=0, offset=0, attr=0, first=0;
  jclass StringBufferClass;
  jobject strBuf;
  jmethodID mid;
  char *cfilt;
  char ctype[1], cval[1], cname[L_max_resname];
  StringBufferClass = (*env)->FindClass(env, "java/lang/StringBuffer");
  mid = (*env)->GetMethodID(env, StringBufferClass, "<init>", "(I)V");
  strBuf = (*env)->NewObject(env, StringBufferClass, mid, 2048);
  mid = (*env)->GetMethodID(env, StringBufferClass, "append",
        "(Ljava/lang/String;)Ljava/lang/StringBuffer;");

  cfilt = (char*)(*env)->GetStringUTFChars(env, filter, 0);
  while ( m_rfilter (cfilt, cname, L_max_resname, ctype, &attr, cval, 0, &offset) >= 0) {
    if (first == 1) {
      strBuf = (*env)->CallObjectMethod(env, strBuf, mid, (*env)->NewStringUTF(env,","));
    } else {
      first = 1;
    }
    strBuf = (*env)->CallObjectMethod(env, strBuf, mid, (*env)->NewStringUTF(env,cname));
  }

  (*env)->ReleaseStringUTFChars(env, filter, cfilt);
  return (strBuf);
}

JNIEXPORT jstring JNICALL Java_nxm_sys_libxm_XMidas_form_1path
  (JNIEnv *env, jclass cls, jint aux, jstring id) {
  char outStr[80];
  char *strID;
  int aux_len;

  strID = (char*)(*env)->GetStringUTFChars(env, id, 0);
  aux_len = m_form_path(aux, strID, outStr, 80);
  (*env)->ReleaseStringUTFChars(env, id, strID);
  /* Convert the C string into a Java string */
  return ((*env)->NewStringUTF (env, outStr));
}

JNIEXPORT jint JNICALL Java_nxm_sys_libxm_XMidas_valid_1aux
  (JNIEnv *env, jclass cls, jint aux) {
  return (m$valid_aux_(&aux));
}

JNIEXPORT jint JNICALL Java_nxm_sys_libxm_XMidas_define_1aux
  (JNIEnv *env, jclass cls, jint aux, jstring auxpath) {
  char* auxStr;

  auxStr = (char*)(*env)->GetStringUTFChars(env, auxpath, 0);
  m_define_aux(aux, auxStr);
  (*env)->ReleaseStringUTFChars(env, auxpath, auxStr);
}

JNIEXPORT void JNICALL Java_nxm_sys_libxm_XMidas_dispatch
  (JNIEnv *env, jclass cls, jstring cmdline, jint flag, jint mask) {
  char temp[L_cmd_chars+1];
  char *str = (char*)(*env)->GetStringUTFChars(env, cmdline, 0);
  strc2f(str, -1, temp, L_cmd_chars, C2F_FLAG_FILL);
  m$dispatch_(temp, &flag, &mask, L_cmd_chars);
  (*env)->ReleaseStringUTFChars(env, cmdline, str);
}

JNIEXPORT jstring JNICALL Java_nxm_sys_libxm_XMidas_getXmVersionBuiltWith(JNIEnv *env, jclass cls) {
  return ((*env)->NewStringUTF (env, XM_SYS_VERSION_STRING));
}

/************************************************************************************************/
/* Support for displaying X11 graphics from X-Midas in Java windows.                            */
/************************************************************************************************/
typedef jboolean (JNICALL *GetAWT)(JNIEnv*, JAWT*);
#define ERROR(msg) fprintf(stderr, "ERROR: %s\n", (msg));

typedef struct {
  JAWT                        awt;     /* The AWT environment.                     */
  JAWT_DrawingSurface*        awt_ds;  /* The drawing surface.                     */
  JAWT_DrawingSurfaceInfo*    awt_dsi; /* The drawing surface info.                */
  JAWT_X11DrawingSurfaceInfo* awt_x11; /* The drawing surface info (cast to X11).  */
  int                         x11id;   /* The X11 ID for the window with /XS=<xs>. */
} XmWinInfo;

int lockXmWinInfo(XmWinInfo *info, JNIEnv *env, jobject canvas, jint xs) {
  int op = 0; /* This is always zero - used when calling mx$window_id_ */

  /* Get the AWT */
  info->awt.version = JAWT_VERSION_1_4;
  if (JAWT_GetAWT(env,&(info->awt)) == JNI_FALSE) {
    fprintf(stderr, "Unable to get AWT\n");
    return -1;
  }

  /* Get the drawing surface */
  info->awt_ds = info->awt.GetDrawingSurface(env,canvas);
  if (info->awt_ds == NULL) {
    fprintf(stderr, "Unable to get AWT surface\n");
    return -1;
  }

  /* Lock the drawing surface */
  if ((info->awt_ds->Lock(info->awt_ds) & JAWT_LOCK_ERROR) != 0) {
    fprintf(stderr, "Unable to lock AWT surface\n");
    info->awt.FreeDrawingSurface(info->awt_ds);
    return -1;
  }

  /* Get the drawing surface info */
  info->awt_dsi = info->awt_ds->GetDrawingSurfaceInfo(info->awt_ds);

  /* Get the X11/Linux drawing surface info */
  info->awt_x11 = (JAWT_X11DrawingSurfaceInfo*)(info->awt_dsi->platformInfo);

  /* Get the X-Midas X11 id. */
  info->x11id = mx$window_id_(&xs, &op);
  if (info->x11id == 0) {
    fprintf(stderr, "Could not get X11 id for /XS=%d\n", xs);
    info->awt_ds->FreeDrawingSurfaceInfo(info->awt_dsi);
    info->awt_ds->Unlock(info->awt_ds);
    info->awt.FreeDrawingSurface(info->awt_ds);
    return -1;
  }

  return 0;
}

int unlockXmWinInfo(XmWinInfo *info) {
  /* Free and unlock everything */
  info->awt_ds->FreeDrawingSurfaceInfo(info->awt_dsi);
  info->awt_ds->Unlock(info->awt_ds);
  info->awt.FreeDrawingSurface(info->awt_ds);
}

JNIEXPORT jboolean JNICALL Java_nxm_sys_libxm_XMidas_xIsSupported(JNIEnv *env, jclass cls) {
  return 1;
}

JNIEXPORT jlong JNICALL Java_nxm_sys_libxm_XMidas_xAttach(JNIEnv *env, jclass cls, jobject canvas, jint xs) {
  XmWinInfo info;

  if (lockXmWinInfo(&info, env, canvas, xs) == 0) {
    XReparentWindow(info.awt_x11->display, info.x11id, info.awt_x11->drawable, 10, 10);

    unlockXmWinInfo(&info);

    return (jlong)info.x11id;
  }

  return 0;
}

JNIEXPORT void JNICALL Java_nxm_sys_libxm_XMidas_xDetach(JNIEnv *env, jclass cls, jobject canvas, jint xs) {
  printf("xDetach: \n");

}

JNIEXPORT void JNICALL Java_nxm_sys_libxm_XMidas_xSetBounds(JNIEnv *env, jclass cls, jobject canvas, jint xs) {
  XmWinInfo info;

  if (lockXmWinInfo(&info, env, canvas, xs) == 0) {
    /* Note that x and y are relative to the origin of the canvas and thus will
       always be set to 0.
     */
    XMoveResizeWindow(info.awt_x11->display, info.x11id, 0, 0,
                      info.awt_dsi->bounds.width,
                      info.awt_dsi->bounds.height);
    unlockXmWinInfo(&info);
  }
}

#ifdef __cplusplus
}
#endif
