/* Supports mapping of X-Midas pipes.
   @author J. Czechowski IV
 */
#include <XmPipe.h>

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

#ifdef __cplusplus
extern "C"
{
#endif

#define DEBUG nxm_sys_libxm_XmPipe_DEBUG

/* Gets the HCB pointed to by a byte buffer. */
#define GET_HCB(buffer) (((buffer) == NULL)? NULL: ((HEADER*)(*env)->GetDirectBufferAddress(env, (buffer))))

#define GET_STRING_CHARS(str)                  (*env)->GetStringUTFChars(env, (str), NULL)
#define RELEASE_STRING_CHARS(str,chars)        (*env)->ReleaseStringUTFChars(env, (str), (chars))
#define GET_BYTE_ARRAY_ELEMENTS(array)         (*env)->GetByteArrayElements(env, (array), NULL)
#define RELEASE_BYTE_ARRAY_ELEMENTS(array,ele) (*env)->ReleaseByteArrayElements(env, (array), (ele), 0)
#define TO_BOOLEAN(val)                        ((jboolean)(val)? JNI_TRUE: JNI_FALSE)

JNIEXPORT jobject JNICALL Java_nxm_sys_libxm_XmPipe_initHeaderBuffer0(JNIEnv *env, jclass clazz) {
  HEADER *hcb = (HEADER*)malloc(sizeof(HEADER));
  jobject buffer = NULL;

  if (hcb != NULL) {
    /* JC4: Copied from headers.idlp in X-Midas 4.2.1:
              Zero out the header; X-Midas needs these fields to be
              zeroed out, and they are not all fixed by M$INITIALIZE.
    */
    memset(hcb, 0, sizeof(HEADER));

    m_initialize(hcb);

    buffer = (*env)->NewDirectByteBuffer(env, hcb, sizeof(HEADER));

    if (DEBUG) {
      fprintf(stderr, "XmPipe.c: Created new HCB=0x%x and buffer=0x%x\n",hcb,buffer);
    }
  }

  return buffer;
}


JNIEXPORT void JNICALL Java_nxm_sys_libxm_XmPipe_freeHeaderBuffer(JNIEnv *env, jclass clazz, jobject buffer) {
  HEADER *hcb = GET_HCB(buffer);

  if (hcb != NULL) {
    free(hcb);
  } else {
    fprintf(stderr, "ERROR: XmPipe: hcb is null.");
  }
}


JNIEXPORT void JNICALL Java_nxm_sys_libxm_XmPipe_open(JNIEnv *env, jclass clazz, jobject buffer,
                                                      jint functions) {
  HEADER *hcb = GET_HCB(buffer);

  if (hcb != NULL) {
    m_open(hcb, functions);
  } else {
    fprintf(stderr, "ERROR: XmPipe: hcb is null.");
  }
}


JNIEXPORT void JNICALL Java_nxm_sys_libxm_XmPipe_close(JNIEnv *env, jclass clazz, jobject buffer) {
  HEADER *hcb = GET_HCB(buffer);

  if (hcb != NULL) {
    m_close(hcb);
  } else {
    fprintf(stderr, "ERROR: XmPipe: hcb is null.");
  }
}

JNIEXPORT jint JNICALL Java_nxm_sys_libxm_XmPipe_avail(JNIEnv *env, jclass clazz, jobject buffer) {
  HEADER *hcb = GET_HCB(buffer);
  jint elements = 0;

  if (hcb != NULL) {
    m_hcbfunc(hcb, HCBF_AVAIL, &elements);
  } else {
    fprintf(stderr, "ERROR: XmPipe: hcb is null.");
  }

  return elements;
}

JNIEXPORT void JNICALL Java_nxm_sys_libxm_XmPipe_updateHeader(JNIEnv *env, jclass clazz, jobject buffer) {
  HEADER *hcb = GET_HCB(buffer);

  if (hcb != NULL) {
    m_update_header(hcb);
  } else {
    fprintf(stderr, "ERROR: XmPipe: hcb is null.");
  }
}

JNIEXPORT void JNICALL Java_nxm_sys_libxm_XmPipe_seek(JNIEnv *env, jclass clazz, jobject buffer,
                                                      jdouble position) {
  HEADER *hcb = GET_HCB(buffer);

  if (hcb != NULL) {
    m_seek(hcb, position);
  } else {
    fprintf(stderr, "ERROR: XmPipe: hcb is null.");
  }
}

JNIEXPORT jint JNICALL Java_nxm_sys_libxm_XmPipe_read(JNIEnv *env, jclass clazz, jobject buffer,
                                                      jbyteArray javaArray, jint boff) {
  HEADER *hcb   = GET_HCB(buffer);
  jbyte  *array = GET_BYTE_ARRAY_ELEMENTS(javaArray);
  jbyte  *bbuf  = &(array[boff]);
  jint    ngot  = 0;  /* This MUST be initialized to zero */

  if (hcb != NULL) {
    if (DEBUG) {
      fprintf(stderr, "XmPipe.c: read: array = 0x%x, bbuf=0x%x\n",array,bbuf);
      fprintf(stderr, "XmPipe.c: read: hcb->xfer_len        = %d\n",hcb->xfer_len);
      fprintf(stderr, "XmPipe.c: read: hcb->cons_len        = %d\n",hcb->cons_len);
      fprintf(stderr, "XmPipe.c: read: hcb->buf_type        = %c\n",hcb->buf_type);
      fprintf(stderr, "XmPipe.c: read: hcb->offset          = %d\n",hcb->offset);
      fprintf(stderr, "XmPipe.c: read: Mu->okio             = %d\n",Mu->okio);
      fprintf(stderr, "XmPipe.c: read: hcb->pkt.flags       = %d\n",hcb->pkt.flags);
//    fprintf(stderr, "XmPipe.c: read: hcb->pkt.get_new_pkt = %d\n",hcb->pkt.get_new_pkt); // XM 4.11.1 removed this unused field
      fprintf(stderr, "XmPipe.c: read: calling m_grabx(0x%x, 0x%x, %d)\n",hcb,bbuf,ngot);
    }

    m_grabx(hcb, bbuf, &ngot);

    if (DEBUG) {
      fprintf(stderr, "XmPipe.c: read: finished m_grabx(..) ngot=%d\n",ngot);
    }
  } else {
    fprintf(stderr, "ERROR: XmPipe: hcb is null.");
  }

  RELEASE_BYTE_ARRAY_ELEMENTS(javaArray, array);

  return ngot;
}


JNIEXPORT void JNICALL Java_nxm_sys_libxm_XmPipe_write(JNIEnv *env, jclass clazz, jobject buffer,
                                                       jbyteArray javaArray, jint boff, jint nele) {
  HEADER *hcb   = GET_HCB(buffer);
  jbyte  *array = GET_BYTE_ARRAY_ELEMENTS(javaArray);
  jbyte  *bbuf  = &(array[boff]);

  if (hcb != NULL) {
    m_filad(hcb,bbuf,nele);
  } else {
    fprintf(stderr, "ERROR: XmPipe: hcb is null.");
  }

  RELEASE_BYTE_ARRAY_ELEMENTS(javaArray, array);
}


JNIEXPORT jstring JNICALL Java_nxm_sys_libxm_XmPipe_pipeName(JNIEnv *env, jclass clazz, jint pipeNum) {
  int i;
  char *ppname = NULL;
  char pipeName[L_pipe_name+1];

  /* JC4: The following addressing was coppied from xpipe.for with a minor change to account for
          C using zero-based arrays. I wouldn't be surprised if this ends up changing between
          versions of X-Midas.
   */
  ppname = (char*)(GET_POINTER(Mu->sec_ptr[LS_pipe_headers-1]) + (pipeNum-1)*L_pipe_name);

  for (i = 0; i < L_pipe_name; i++) {
    if ((ppname[i] == ' ') || (ppname[i] == '\0')) {
      pipeName[i] = '\0';
      break;
    } else {
      pipeName[i] = ppname[i];
    }
  }
  pipeName[L_pipe_name] = '\0';

  return (*env)->NewStringUTF(env, pipeName);
}

#ifdef __cplusplus
}
#endif
