/* Version Jan 2022 - Introduce Makefile and revive windows build */

/*
 * DEFAULTSERVER        Defines default server when MEX is first loaded into memory
 * LOCAL_CONNECT        Allows mdsconnect('local') - requires shareable libraries
 * SHARED               Link to shared version - different connect calls
 * DYNAMICLINK          Libraries resolved at runtime - implicitly MDSLIBS
 * NOUNIQ               With shared version, never reuse connection
 * PASSWD
 * TIMEOUT              Allow changing connect/send/recv timeouts (mainly for TCP connections)
 */

/*
 * LONGLONGINT          Very old versions of MDSplus do not have quadword support for MDSip
 * DEPRECATED_DTYPES    MDSplus versions >=7.79.0 deprecated some obsolete MDSip DTYPEs
 */

/* Include MEX-file related definitions */
#include "mex.h"
/* Undefine NDEBUG as provided by the mex command        *
 * This is only used in "mex.h" and related files but    *
 * can conflict with some definitions in MDSplus headers */
#ifdef NDEBUG
#undef NDEBUG
#endif

/* Include for mds descriptor definition */
#include "ipdesc.h"
#include "mdsdescrip.h"

#ifdef DTYPE_LONGLONG
/*#ifdef _WIN32
#define LONGLONGINT _int64
#define LONGLONGINTU _int64
#else*/
#define LONGLONGINT long long
#define LONGLONGINTU unsigned long long
/*#endif*/
#endif

#ifdef DTYPE_CIT
#define DEPRECATED_DTYPES
#endif

#define BVERSION "9.2022.01.LNX64"
#ifndef int32
#define int32 int
#endif

#define MatAlloc(a,b) mxMalloc((a)*(b))
#define CONST const

#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#define MAX(a,b) (((a) > (b)) ? (a) : (b))

#include <string.h>
#ifndef INVALID_SOCKET
#define INVALID_SOCKET -1
#endif

#ifndef DEFAULTSERVER
#define DEFAULTSERVER "tcvdata.epfl.ch"
#endif
#define PROTOCOLTEST  "://"
#define SERVERTEST    "::"
#define PORTTEST      ":"
#define STRLEN 128
#ifndef NOUNIQ
#define UNIQLEN 128
#endif

#ifndef SOCKET
#define SOCKET int
#endif

#ifdef LOCAL_CONNECT
/* Set LOCAL_SOCKET=-2 to avoid collisions with standard connection indices [0 ... ] for MDSplus 7.79.0+ */
#ifndef LOCAL_SOCKET
#define LOCAL_SOCKET -2
#endif
#endif

static int BSendArg(SOCKET s, unsigned char i, char dtype, unsigned char nargs, short len, char ndims, mwSize * dims, char *ptr,
                    int alloc, int Mclass);
static int BGetAnswerInfo(SOCKET s, char *dtype, short *len, char *ndims, int *dims, int *nbytes, void **dptr, void **msgPtr);

static void Bmdsopen(int nlhs, mxArray * plhs[], int nrhs, CONST mxArray * prhs[]);
static void Bmdsclose(int nlhs, mxArray * plhs[], int nrhs, CONST mxArray * prhs[]);
static void Bmdsvalue(int oconv, char *EXP, int nlhs, mxArray * plhs[], int nrhs, CONST mxArray * prhs[]);
static void Bmdsdisconnect(int nlhs, mxArray * plhs[], int nrhs, CONST mxArray * prhs[]);
static void Bmdsput(int nlhs, mxArray * plhs[], int nrhs, CONST mxArray * prhs[]);
static int32 Bmdsconnect(char *server, int nlhs, mxArray * plhs[], int nrhs, CONST mxArray * prhs[]);
static void Bmdsdefaultserver(int nlhs, mxArray * plhs[], int nrhs, CONST mxArray * prhs[]);
static void BSetMdsTimeout(int nlhs,mxArray *plhs[],int nrhs,CONST mxArray *prhs[]);
static SOCKET BAddConnection(char *server);
static int bMdsList(int nlhs, mxArray * plhs[], int nrhs, CONST mxArray * prhs[]);

/* Variables that remain declared between calls */
SOCKET sock = INVALID_SOCKET;   /* Mark the socket as unopen */
char serv[STRLEN];              /* Current server */
char defServer[STRLEN] = "";    /* changeable default server name (possibly including port) */

#ifdef LOCAL_CONNECT
#include "mdsshr.h"
#include "mds_stdarg.h"
/* This is not in the MDSplus headers */
int TdiData();
int TdiExecute();
/* Define Some static variables (not to be lost between mex calls) */
static EMPTYXD(ansD);
/* LOCAL_CONNECT must use SHARED ! */
#ifndef SHARED
#define SHARED
#endif
#endif

#ifdef DYNAMICLINK
/* DYNAMICLINK must use SHARED ! */
#ifndef SHARED
#define SHARED
#endif
#endif

#ifdef TIMEOUT
#ifdef SHARED
/* Enable tools for dynamic loading */
#ifndef DYNAMICLINK
#define DYNAMICLINK
#define DL_TIMEOUT_ONLY
#endif
#endif
#endif

#ifdef DYNAMICLINK
#include <dlfcn.h>

#ifdef _WIN32
#define LIBNAME(name) #name".dll"
#else
#define LIBNAME(name) "lib"#name".so"
#endif

/* By defining these variables, linking on demand is turned on */
#ifndef DL_TIMEOUT_ONLY
#define DYNMdsIpShr
#ifdef LOCAL_CONNECT
#define DYNTdiShr
#define DYNMdsShr
#endif
#endif
#endif                          /* DYNAMICLINK */

#ifdef DYNMdsIpShr
static void *MdsIpShrHandle = NULL;
static int (*LReuseCheck)() = NULL;
static int (*LDisconnectFromMds)() = NULL;
static int (*LSendArg)() = NULL;
static int (*LConnectToMds)() = NULL;
static int (*LGetAnswerInfo)() = NULL;
static int (*LMdsClose)() = NULL;
static int (*LFreeMessage)() = NULL;
#else                           /* DYNMdsIpShr */
/* This is not in the MDSplus headers */
int ReuseCheck(char *hostin, char *unique, size_t buflen);
#define LReuseCheck ReuseCheck
#define LDisconnectFromMds DisconnectFromMds
#define LSendArg SendArg
#define LConnectToMds ConnectToMds
#define LGetAnswerInfo GetAnswerInfo
#define LMdsClose MdsClose
#define LFreeMessage FreeMessage
#endif                          /* DYNMdsIpShr */

#ifdef TIMEOUT
#ifdef SHARED
#ifndef DYNMdsIpShr
static void *MdsIpShrHandle = NULL;
#endif
/* Define Some static variables (not to be lost between mex calls) */
int GetAnswerTimeout = -1;    /* timeout for GetAnswerInfo */
static int (*LGetAnswerInfoTO)() = NULL;
static int (*LSetMdsConnectTimeout)() = NULL;
int BSetMdsConnectTimeout(int sec);
#else                           /* SHARED */
int SetMdsConnectTimeout(int sec);
int SetMdsSendTimeout(int sec);
int SetMdsReadTimeout(int sec);
#endif                          /* SHARED */
#endif                          /* TIMEOUT */

#ifdef DYNTdiShr
static void *TdiShrHandle = NULL;
static int (*LTdiExecute)() = NULL;
static int (*LTdiData)() = NULL;
#else                           /* DYNTdiShr */
#define LTdiExecute TdiExecute
#define LTdiData TdiData
#endif                          /* DYNTdiShr */

#ifdef DYNMdsShr
static void *MdsShrHandle = NULL;
static int (*LMdsFree1Dx)() = NULL;
#else                           /* DYNMdsShr */
#define LMdsFree1Dx MdsFree1Dx
#endif                          /* DYNMdsShr */

typedef struct _connection {
  SOCKET sock;                  /* Mark the socket as unopen */
  char *unique;                 /* Unique connection name (used in full connection) */
  char serv[STRLEN];            /* Current server */
  struct _connection *next;
} bConnection;
static bConnection *Connections = NULL;

#ifdef DYNAMICLINK
static int linkComplete = 0;
/* Lookup up entry point in shareable */
int dlsymget(void *handle, char *sym, void **addr)
{
  char *error;
  void *ret = NULL;
  dlerror();                    /* Clear any existing error */
  *addr = dlsym(handle, sym);
  if ((error = dlerror()) != NULL) {
#ifdef DEBUG
    fprintf(stderr, "%s\n", error);
#endif
    return(0);
  }
  return(1);
}

void dlsymgetall()
{

#define DLSYMGETCHECK(HANDLE,SYM) if (dlsymget(HANDLE,#SYM,(void **) &L##SYM) == 0)\
  mexErrMsgIdAndTxt("mdsipmex:dlsym","Could not find symbol [%s]",#SYM)

#if defined(DYNMdsIpShr) || defined(TIMEOUT)
  /* Load symbols from MdsIpShr */
  MdsIpShrHandle = dlopen(LIBNAME(MdsIpShr), RTLD_LAZY);
  if (MdsIpShrHandle == NULL) {
    mexErrMsgIdAndTxt("mdsipmex:dlopen","Could not find library [%s]", LIBNAME(MdsIpShr));
  } else {
#ifdef DYNMdsIpShr
    DLSYMGETCHECK(MdsIpShrHandle,ReuseCheck);
    DLSYMGETCHECK(MdsIpShrHandle,DisconnectFromMds);
    DLSYMGETCHECK(MdsIpShrHandle,SendArg);
    DLSYMGETCHECK(MdsIpShrHandle,ConnectToMds);
    DLSYMGETCHECK(MdsIpShrHandle,GetAnswerInfo);
    DLSYMGETCHECK(MdsIpShrHandle,FreeMessage);
    DLSYMGETCHECK(MdsIpShrHandle,MdsClose);
#endif                          /* DYNMdsShr */
#ifdef TIMEOUT
    if (dlsymget(MdsIpShrHandle,"SetMdsConnectTimeout", (void **) &LSetMdsConnectTimeout) == 0)
      LSetMdsConnectTimeout = (void *) &BSetMdsConnectTimeout;
    if (dlsymget(MdsIpShrHandle,"GetAnswerInfoTO", (void **) &LGetAnswerInfoTO) == 0)
      LGetAnswerInfoTO = NULL;
#endif                          /* TIMEOUT */
  }
#endif                          /* DYNMdsShr || TIMEOUT */

#ifdef DYNTdiShr
  /* Load symbols from TdiShr */
  TdiShrHandle = dlopen(LIBNAME(TdiShr), RTLD_LAZY);
  if (TdiShrHandle == NULL) {
    mexErrMsgIdAndTxt("mdsipmex:dlopen","Could not find library [%s]", LIBNAME(TdiShr));
  } else {
    DLSYMGETCHECK(TdiShrHandle,TdiExecute);
    DLSYMGETCHECK(TdiShrHandle,TdiData);
  }
#endif                          /* DYNTdiShr */

#ifdef DYNMdsShr
  /* Load symbols from MdsShr */
  MdsShrHandle = dlopen(LIBNAME(MdsShr), RTLD_LAZY);
  if (MdsShrHandle == NULL) {
    mexErrMsgIdAndTxt("mdsipmex:dlopen","Could not find library [%s]", LIBNAME(MdsShr));
  } else {
    DLSYMGETCHECK(MdsShrHandle,MdsFree1Dx);
  }
#endif                          /* DYNMdsShr */
}
#endif /* DYNAMICLINK */

/* Define ReuseCheck function for minimal MDSplus version */
#ifndef SHARED
int ReuseCheck(char *hostin, char *unique, size_t buflen)
{
  int status, ip;
  short port;
  unsigned char *uc = (unsigned char *) &ip;
  /* Check that :// is absent from hostin */
  if (strstr(hostin,PROTOCOLTEST) == NULL) {
    if ((status = HostToIp(hostin, &ip, &port)) == INVALID_SOCKET)
      return(status);
    sprintf(unique,"tcp://%03d.%03d.%03d.%03d:%d",uc[0],uc[1],uc[2],uc[3],port);
    return(status);
  } else {
    mexPrintf("The url syntax protocol://host is not supported in this version of mdsipmex\n");
    return(INVALID_SOCKET);
  }
}
#endif /* SHARED */

#ifdef TIMEOUT
#ifdef SHARED
int BSetMdsConnectTimeout(int sec)
{
  int ret = 10;
  char *strval;
  /* read current value */
  const char *timeout = getenv("MDSIP_CONNECT_TIMEOUT");
  if (timeout)
    ret = (int) strtol(timeout, NULL, 0);
  /* set new value */
  strval = malloc(STRLEN);
  if (!strval)
    return(-1);
  snprintf(strval,"MDSIP_CONNECT_TIMEOUT=%d",sec,STRLEN);
  putenv(strval);
  /* return */
  return(ret);
}
#endif
#endif

/* Matrix permutation function for use in conversion of text data between MDSplus and MATLAB */
void permute2(char *dst, char *src, int nR, int nC, int nX)
{
  char *dst_ = dst, *src_ = src;
  size_t block = 256; /* 256 bytes */
  size_t l, i, j, b;
  for (l = 0; l < nX; l++) {
    for (i = 0; i < nC; i += block) {
      for(j = 0; j < nR; ++j) {
        for(b = 0; b < block && i + b < nC; ++b) {
          dst_[j*nC + i + b] = src_[(i + b)*nR + j];
        }
      }
    }
    dst_+=nR*nC;
    src_+=nR*nC;
  }
}

/* Exit Function When the MEX-file is cleared, must also free permanent memory */
void ExitFcn(void)
{
  /* Loop through connection list */
  while (Connections != NULL) {
    sock = Connections->sock;
    printf("Mdsremote: ExitFcn closing remote server [%s]\n", Connections->serv);
    Bmdsdisconnect(0, NULL, 0, NULL);   /* Changes Connections pointer */
  }
}

/* Matlab Function ================================================================ */
void mexFunction(int nlhs, mxArray * plhs[], int nrhs, CONST mxArray * prhs[])
{

#ifdef DYNAMICLINK
  if (linkComplete == 0) {
    /* Try to load libraries and throw error if it fails */
    dlsymgetall();
    linkComplete = 1;
  }
#endif

  /* If no default server is set, use compile time definiton */
  if (!strlen(defServer))
    strcpy(defServer, DEFAULTSERVER);   /* use DEFAULTSERVER if not set */

  /* First Arg string -> mdsvalue */
  int opt = mxIsChar(prhs[0]) ? 2 : (int) *mxGetPr(prhs[0]);    /* mode */

  switch (opt) {
  case 1:                               /* mdsopen */
    Bmdsopen(nlhs, plhs, nrhs, prhs);
    break;
  case 2:                               /* mdsvalue */
    Bmdsvalue(1, NULL, nlhs, plhs, nrhs, prhs);
    break;
  case 3:                               /* orphaned */
    Bmdsclose(nlhs, plhs, nrhs, prhs);
    break;
  case 4:                               /* mdsdisconnect */
    if (nrhs > 1) {
      ExitFcn();
      /* copy status as reply */
      if (nlhs > 0)
        plhs[0] = mxCreateDoubleScalar(1);
    } else
      Bmdsdisconnect(nlhs, plhs, nrhs, prhs);
    break;
  case 5:                               /* orphaned */
    Bmdsput(nlhs, plhs, nrhs, prhs);
    break;
  case 6:                               /* mdsversion */
    if (nlhs > 0)
      plhs[0] = mxCreateString(BVERSION);       /* create string */
    break;
  case 7:                               /* mdsremotelist */
    plhs[0] = mxCreateDoubleScalar(bMdsList(nlhs, plhs, nrhs, prhs));
    break;
  case 8:                               /* mdscurrent */
    if (nlhs > 0)
      plhs[0] = mxCreateString(serv);   /* create string */
    break;
  case 9:                               /* mdsvalueraw */
    Bmdsvalue(0, NULL, nlhs, plhs, nrhs, prhs);
    break;
  case 10:                              /* mdsconnect */
    Bmdsconnect(NULL, nlhs, plhs, nrhs, prhs);
    break;
  case 11:                              /* mdsdefaultserver */
    Bmdsdefaultserver(nlhs, plhs, nrhs, prhs);
    break;
  case 12:                              /* mdstimeout */
    BSetMdsTimeout(nlhs, plhs, nrhs,  prhs);
    break;
  default:
    break;
  }
  return;
}

/*            GetMatReply in suboutine form */
void GetMatReply(int oconv, int nlhs, mxArray * plhs[])
{
  short vlen;
  int32 numbytes;
  void *vptr;
  void *msgPtr = 0;

  int len, i, j;
  struct descrip ans;
  char *cptr;
  unsigned char *ucptr;
  short *sptr;
  unsigned short *usptr;
  int *iptr;
  unsigned int *uiptr;
#ifdef LONGLONGINT
  LONGLONGINT *qptr;
  LONGLONGINTU *uqptr;
#endif
  float *fptr;
  double *dptr, *dpout;
  mxChar *mxcP;
  mxArray *mxaP;
  mwSize matdum[MAX_DIMS + 1];
  int status;                   /* check status of calls to mdsipshr.exe */
  mxClassID ocat;

#ifdef DEBUG
  printf("before BGetAnswerInfo Call");
#endif
  status = BGetAnswerInfo(sock, &ans.dtype, &vlen, &ans.ndims, ans.dims, &numbytes, &vptr, &msgPtr);
  ans.length = vlen;
  ans.ptr = numbytes ? vptr : NULL;
#ifdef DEBUG
  printf("... and return\n[%d] reply with dtype[%d] vlen[%d] ndims[%d] dims[%d,%d] numbytes[%d] vptr[%0lx]\n",
         status, ans.dtype, vlen, ans.ndims, ans.dims[0], ans.dims[1], numbytes, vptr);
#endif
  if ((status & 1) || numbytes > 0) {
    /* treat the reply */
    if (ans.ndims && ans.ptr) { /* get number of elements, assumes ans.ptr exists */
      for (i = ans.ndims, len = 1; i--;)
        len *= ans.dims[i];
    } else {
      if (ans.ptr)
        len = ans.ndims = ans.dims[0] = 1;      /* make it with one dimension with length of 1 */
      else
        len = 0;
    }
    /* Matlab ERROR: if ndims ==1 fails... FIX: pretend ndims=2 , bpd, xsl, Jan 98 */
    if (ans.ndims == 1) {
      ans.dims[1] = 1;
      ans.ndims++;
    }
#ifdef DEBUG
    printf("Len comes with [%d]\n", len);
#endif
    /* types */
    if (ans.dtype == DTYPE_CSTRING) {
#ifdef DEBUG
      mexPrintf("string ndims %d, length %d,%d -> %d,%d\n", ans.ndims, ans.length, len, ans.dims[0], ans.dims[1]);
#endif
      if (len == 0) {
        matdum[1] = matdum[0] = 0;
        plhs[0] = mxCreateCharArray(2, matdum); /* create a cell */
        /*       plhs[0] = mxCreateDoubleMatrix(0,0,mxREAL); */
      } else {
        if (len > 1) {
          for (i = ans.ndims; i--;)
            matdum[i] = ans.dims[i];
          plhs[0] = mxCreateCellArray(ans.ndims, matdum);       /* create char cell array */
        }
        cptr = ans.ptr;
        ans.ndims = 2;
        matdum[0] = 1;
        matdum[1] = ans.length;
        for (i = 0; i < len; i++) {
          mxaP = mxCreateCharArray(ans.ndims, matdum);  /* create a cell */
          mxcP = (mxChar *) mxGetPr(mxaP);      /* pointer to its data */
          for (j = 0; j < ans.length; j++)      /* copy data across */
            *mxcP++ = *cptr++;
          if (len > 1)
            mxSetCell(plhs[0], i, mxaP);        /* set cell */
          else
            plhs[0] = mxaP;     /* single string returned */
        }
      }                         /* string or cell array termination */
    } else {                    /* Not DTYPE_CSTRING */
      /* output vector */
#ifdef DEBUG
      printf("array [%d],%d [%d,%d]\n", ans.ndims, len, ans.dims[0], ans.dims[1]);
#endif
      /* BPD, Jan 2007, return raw classes */
      if (oconv) {              /* request output conversion */
        ocat = mxDOUBLE_CLASS;
      } else {
        switch (ans.dtype) {
        case DTYPE_UCHAR:
          ocat = mxUINT8_CLASS;
          break;
        case DTYPE_CHAR:
          ocat = mxINT8_CLASS;
          break;
        case DTYPE_USHORT:
          ocat = mxUINT16_CLASS;
          break;
        case DTYPE_SHORT:
          ocat = mxINT16_CLASS;
          break;
        case DTYPE_LONG:
          ocat = mxINT32_CLASS;
          break;
        case DTYPE_ULONG:
          ocat = mxUINT32_CLASS;
          break;
#ifdef LONGLONGINT
        case DTYPE_ULONGLONG:
          ocat = mxDOUBLE_CLASS;
          oconv = 1;
          break;
        case DTYPE_LONGLONG:
          ocat = mxDOUBLE_CLASS;
          oconv = 1;
          break;
#endif
        case DTYPE_FLOAT:
          ocat = mxSINGLE_CLASS;
          break;
        case DTYPE_DOUBLE:
        case 27:
          ocat = mxDOUBLE_CLASS;
          break;
        default:
          mexErrMsgIdAndTxt("mdsipmex:error","mdsipmex: Raw Return type UnTreated");
          break;
        }
      }
      for (i = ans.ndims; i--;)
        matdum[i] = ans.dims[i];
      plhs[0] = mxCreateNumericArray(ans.ndims, matdum, ocat, ans.dtype == DTYPE_COMPLEX ? mxCOMPLEX : mxREAL);
      dpout = mxGetPr(plhs[0]); /* pointer into matlab array */
      i = len;
      if (oconv)
        switch (ans.dtype) {
        case DTYPE_UCHAR:
          ucptr = (unsigned char *) ans.ptr;
          while (len--)
            *dpout++ = *ucptr++;
          break;
        case DTYPE_CHAR:
          cptr = (char *) ans.ptr;
          while (len--)
            *dpout++ = *cptr++;
          break;
        case DTYPE_USHORT:
          usptr = (unsigned short *) ans.ptr;
          while (len--)
            *dpout++ = *usptr++;
          break;
        case DTYPE_SHORT:
          sptr = (short *) ans.ptr;
          while (len--)
            *dpout++ = *sptr++;
          break;
        case DTYPE_ULONG:
          uiptr = (unsigned int *) ans.ptr;
          while (len--)
            *dpout++ = *uiptr++;
          break;
        case DTYPE_LONG:
          iptr = (int *) ans.ptr;
          while (len--)
            *dpout++ = *iptr++;
          break;
#ifdef LONGLONGINT
        case DTYPE_ULONGLONG:
          uqptr = (LONGLONGINTU *) ans.ptr;
          while (len--)
            *dpout++ = (double) *uqptr++;
          break;
        case DTYPE_LONGLONG:
          qptr = (LONGLONGINT *) ans.ptr;
          while (len--)
            *dpout++ = (double) *qptr++;
          break;
#endif
        case DTYPE_FLOAT:
          fptr = (float *) ans.ptr;
          while (len--)
            *dpout++ = *fptr++;
          break;
        case DTYPE_DOUBLE:
        case 27:
          dptr = (double *) ans.ptr;
          while (len--)
            *dpout++ = *dptr++;
          break;
        case DTYPE_COMPLEX:
          dptr = (double *) ans.ptr;
          while (len--) {
            *dpout++ = *dptr++;
            *dpout++ = *dptr++;

          }
          break;
        default:
          break;
      } else {                  /* Switch on ans.dtype */
#ifdef DEBUG
        printf("copying %d bytes from [%lu] to [%lu]\n", numbytes, ans.ptr, dpout);
#endif
        memcpy((char *) dpout, (char *) ans.ptr, numbytes);
      }
      /* make ROPRANDS into NaN, JMM circa 1993 */
    }                           /* Not a DTYPE_CSTRING */
#ifdef LOCAL_CONNECT
    if ((sock == LOCAL_SOCKET) && (status == 1))
      LMdsFree1Dx(&ansD, 0);    /* Free Answer descriptor */
#endif                          /* LOCAL_CONNECT */
#ifdef SHARED
    /* Free message if returned */
    if (msgPtr)
      LFreeMessage(msgPtr);     /* Careful... this may use a matlab free ? */
#endif                          /* SHARED */
  }
  /* return the status if requested */
  if (nlhs > 1)
    plhs[1] = mxCreateDoubleScalar(status);
  if (!(status & 1) && numbytes == 0) {
    mexErrMsgIdAndTxt("mdsipmex:connection", "mdsipmex: Error in receiving reply, check connection status");
  }
}

#ifdef NEVER
/* conversion from NaN (Matlab4) to ROPRAND, to be used in mdsput ? */
if (((quadword *) dpout)->w1 == 0xd37affeb && ((quadword *) dpout)->w2 == 0xe9bd6f4d) {
  ((quadword *) dpout)->w1 = 0x8000l;
  ((quadword *) dpout)->w2 = 0x0l;
}
#endif
/*            SendMatValue in suboutine form */
void SendMatValue(CONST mxArray * prhs[], int i, int nrhs, int opt)
{
  mxArray *outAr[1], *inAr[2];
  int Mclass;                   /* June12, record descriptor class if requried */
  int llen, len, j, k, lmax;
  char *cptr, *strptr, *s2trptr;
  unsigned char *ucptr;
  short *sptr;
  unsigned short *usptr;
  int *iptr;
  unsigned int *uiptr;
#ifdef LONGLONGINT
  LONGLONGINT *qptr;
  LONGLONGINTU *uqptr;
#endif
  float *fptr;
  double *dptr;
  mwSize matdum[MAX_DIMS + 1];
  int ndim;
  mwSize *ndimP;
  int status = 1;               /* check status of calls to mdsipshr.exe */
  int alloc;
  mxArray *Tarray;              /* Get data from class */
  int conv = 0;
#define CLEN 8
  char convS[CLEN];             /* conversion string */
  /* Before you can look at the matrix dimensions, you have to see if it is a class */
  /* use the class mdscvt to find if there is a conversion */
  if (mxIsClass(prhs[i], "mdscvt")) {
#ifdef DEBUG
    printf("Was class class mdscvt idx [%d]\n", i);
#endif
    conv = DTYPE_DOUBLE;        /* default was a double */
    Tarray = mxGetField(prhs[i], 0, "t");       /* type element */
    if (Tarray == NULL)
      mexErrMsgIdAndTxt("mdsipmex:error","mdsipmex: Conversion type in class mdscvt not given");
    else {
      mxGetString(Tarray, convS, CLEN);
#ifdef DEBUG
      printf("Got value '%s'\n", convS);
#endif
      switch (*convS) {
      case 'f':
        conv = DTYPE_FLOAT;
        break;
        /*        case 'x' : */
      case 'd':
        conv = DTYPE_DOUBLE;
        break;
      case 'l':
        conv = DTYPE_LONG;
        break;
      case 's':
        conv = DTYPE_SHORT;
        break;
      case 'c':
        conv = DTYPE_CHAR;
        break;
      case 'L':
        conv = DTYPE_ULONG;
        break;
      case 'S':
        conv = DTYPE_USHORT;
        break;
      case 'C':
        conv = DTYPE_UCHAR;
        break;
      case 't':
      case 'T':
        /* here the data must already be a string */
        conv = 999;
        break;
#ifdef LONGLONGINT
      case 'q':
        conv = DTYPE_LONGLONG;
        break;
      case 'Q':
        conv = DTYPE_ULONGLONG;
        break;
#else
      case 'q':
      case 'Q':
        mexErrMsgIdAndTxt("mdsipmex:error","mdsipmex: Quadword not yet supported");
        break;
#endif
      default:
        mexErrMsgIdAndTxt("mdsipmex:error","mdsipmex: Conversion in mdscvt class unknown");
      }
    }
    Tarray = mxGetField(prhs[i], 0, "x");
    if (Tarray == NULL)
      mexErrMsgIdAndTxt("mdsipmex:error","mdsipmex: No data in mdscvt class");
  } else
    Tarray = (mxArray *) prhs[i];       /* copy array pointer with CONST cast */
  /* Dimension information */
  ndimP = (mwSize *) mxGetDimensions(Tarray);
  ndim = (int) mxGetNumberOfDimensions(Tarray);
  /* Calculate number of elements */
  for (j = ndim, len = 1; j--;)
    len *= (int) ndimP[j];
#ifdef DEBUG
  printf("[%d] replacing type [%d], ndims [%d]", i, mxIsChar(Tarray), ndim);
  if (ndim > 1)
    printf("%d,%d", ndimP[0], ndimP[1]);
  printf("\n");
#endif                          /* of DEBUG */
  /* Check type of variable from MATLAB */
  if (mxIsNumeric(Tarray)) {
    if (mxIsComplex(Tarray))
      mexErrMsgIdAndTxt("mdsipmex:error", "mdsipmex: Imaginary numbers not wanted");
    Mclass = CLASS_A;           /* Array, dimensions in ndim and ndimP */
    /* Check if scalar */
    if (len == 1) {
#ifdef DEBUG
      printf("converting scalar %d,%d\n", mxGetM(Tarray), mxGetN(Tarray));
#endif
      ndim = 0;
      ndimP = matdum;
      Mclass = CLASS_S;         /* Fixed length, length in len */
    } else {                    /* Treat Scalar */
      /* Special case to reduce a matrix (3,1) or (1,3) to a vector */
      if (ndim == 2)
        if (MIN(ndimP[0], ndimP[1]) == 1) {
          matdum[0] = MAX(ndimP[0], ndimP[1]);
          matdum[1] = 0;
          ndimP = matdum;
          ndim--;
          /*            Mclass = CLASS_S; /* Fixed length, length in len */
        }
    }                           /* End Treat Scalar */
#ifdef DEBUG
    printf("ndim %d, point %lx", ndim, ndimP);
    if (ndim > 1)
      printf("  vals %d %d", ndimP[0], ndimP[1]);
    printf("\n");
#endif

    /* Find out what type the variable has */
    /* get class and data type from matlab */
    if (conv) {
      /* if not a normal matlab variable and conv was specified, signal alarm */
      if (!mxIsClass(Tarray, "double"))
        mexErrMsgIdAndTxt("mdsipmex:error","mdsipmex: mdscvt specified but data was not double precision");
    } else
      switch (mxGetClassID(Tarray)) {
      case mxCHAR_CLASS:
        conv = -DTYPE_CHAR;
#ifdef DEBUG
        printf("Got type 'char'\n");
#endif
        break;
      case mxINT8_CLASS:
        conv = -DTYPE_CHAR;
#ifdef DEBUG
        printf("Got type 'int8'\n");
#endif
        break;
      case mxUINT8_CLASS:
        conv = -DTYPE_UCHAR;
#ifdef DEBUG
        printf("Got type 'uint8'\n");
#endif
        break;
      case mxINT16_CLASS:
        conv = -DTYPE_SHORT;
#ifdef DEBUG
        printf("Got type 'int16'\n");
#endif
        break;
      case mxUINT16_CLASS:
        conv = -DTYPE_USHORT;
#ifdef DEBUG
        printf("Got type 'uint16'\n");
#endif
        break;
      case mxINT32_CLASS:
        conv = -DTYPE_LONG;
#ifdef DEBUG
        printf("Got type 'int32'\n");
#endif
        break;
      case mxUINT32_CLASS:
        conv = -DTYPE_ULONG;
#ifdef DEBUG
        printf("Got type 'uint32'\n");
#endif
        break;
      case mxSINGLE_CLASS:
        conv = -DTYPE_FLOAT;
#ifdef DEBUG
        printf("Got type 'float'\n");
#endif
        break;
      case mxDOUBLE_CLASS:
        conv = -DTYPE_DOUBLE;
#ifdef DEBUG
        printf("Got type 'double'\n");
#endif
        break;
      default:
        mexErrMsgIdAndTxt("mdsipmex:error","mdsipmex: Can not handle this matlab data type, YET!");
      }
    dptr = mxGetPr(Tarray);     /* pointer to original data */
    strptr = (char *) dptr;     /* default output value */
    alloc = 0;
    llen = len;
    switch (abs(conv)) {
    case DTYPE_UCHAR:
      alloc = sizeof(unsigned char);
      if (conv < 0)
        break;
      ucptr = (unsigned char *) MatAlloc(len, alloc);
      strptr = (char *) ucptr;
      while (len--)
        *ucptr++ = (unsigned char) *dptr++;
      break;
    case DTYPE_CHAR:
      alloc = sizeof(char);
      if (conv < 0)
        break;
      cptr = (char *) MatAlloc(len, alloc);
      strptr = (char *) cptr;
      while (len--)
        *cptr++ = (char) *dptr++;
      break;
    case DTYPE_USHORT:
      alloc = sizeof(unsigned short);
      if (conv < 0)
        break;
      usptr = (unsigned short *) MatAlloc(len, alloc);
      strptr = (char *) usptr;
      while (len--)
        *usptr++ = (unsigned short) *dptr++;
      break;
    case DTYPE_SHORT:
      alloc = sizeof(short);
      if (conv < 0)
        break;
      sptr = (short *) MatAlloc(len, alloc);
      strptr = (char *) sptr;
      while (len--)
        *sptr++ = (short) *dptr++;
      break;
#ifdef DTYPE_LONGLONG
    case DTYPE_ULONGLONG:
      alloc = sizeof(LONGLONGINTU);
      if (conv < 0)
        break;
      uqptr = (LONGLONGINTU *) MatAlloc(len, alloc);
      strptr = (char *) uqptr;
      while (len--)
        *uqptr++ = (LONGLONGINTU) * dptr++;
      break;
    case DTYPE_LONGLONG:
      alloc = sizeof(LONGLONGINT);
      if (conv < 0)
        break;
      qptr = (LONGLONGINT *) MatAlloc(len, alloc);
      strptr = (char *) qptr;
      while (len--)
        *qptr++ = (LONGLONGINT) * dptr++;
      break;
#endif
    case DTYPE_ULONG:
      alloc = sizeof(unsigned int);
      if (conv < 0)
        break;
      uiptr = (unsigned int *) MatAlloc(len, alloc);
      strptr = (char *) uiptr;
      while (len--)
        *uiptr++ = (unsigned int) *dptr++;
      break;
    case DTYPE_LONG:
      alloc = sizeof(int);
      if (conv < 0)
        break;
      iptr = (int *) MatAlloc(len, alloc);
      strptr = (char *) iptr;
      while (len--)
        *iptr++ = (int) *dptr++;
      break;
    case DTYPE_FLOAT:
      alloc = sizeof(float);
      if (conv < 0)
        break;
      fptr = (float *) MatAlloc(len, alloc);
      strptr = (char *) fptr;
      while (len--)
        *fptr++ = (float) *dptr++;
      break;
    case DTYPE_DOUBLE:
      alloc = sizeof(double);
      if (conv < 0)
        break;
      strptr = (char *) dptr;
      /* assume that this does not need conversion */
      break;
    case 999:
      mexErrMsgIdAndTxt("mdsipmex:error","mdsipmex: Text conversion requires data be ischar");
    default:
      alloc = sizeof(double);
      conv = DTYPE_DOUBLE;
      break;
    }
#ifdef DEBUG
    {
      int ii;
      for (ii = 0; ii < (llen * alloc); ii++)
        printf("Byte(%d) = 0x%x\n", ii, strptr[ii]);
    }
#endif
    status = BSendArg(sock, (unsigned char) (i - opt), (char) abs(conv), (char) (nrhs - opt)
                      , (short) alloc, (char) ndim, ndimP, strptr, dptr != mxGetPr(Tarray), Mclass);

    /*      status = SendArg(sock, (unsigned char)(i-opt), DTYPE_DOUBLE, (unsigned char)(nrhs-opt)
       ,(short)sizeof(double), (char)ndim, ndimP, (char *)mxGetPr(Tarray)); */
  }                             /* End Treat numbers */
  /* text data (only accept text data from CellArray) */
  else if (mxIsChar(Tarray) || mxIsCell(Tarray)) {
    Mclass = CLASS_A;           /* Array, dimensions in ndim and ndimP */
    if (mxIsChar(Tarray)) {     /* ONLY characters */
      ndim--;                   /* remove second array */
      lmax = (int) ndimP[1];    /* length of each element */
      *matdum = *ndimP;         /* first element gives size */
      for (j = 1; j < ndim; j++)
        matdum[j] = ndimP[j + 1];       /* copy the other dimensions */
      strptr = (char *) MatAlloc((len + 1), sizeof(char));
      /* Use custom function for matrix permutation */
      size_t nX = 1;
      for (j = 2; j < ndim+1; j++)
        nX *= ndimP[j];
      s2trptr = mxArrayToString(Tarray);
#ifdef DEBUG
      printf("Pre permute2 call, nR=%d, nC=%d, nX=%d\n",ndimP[0],ndimP[1],nX);
#endif
      permute2(strptr, s2trptr, ndimP[0], ndimP[1], nX);
      mxFree(s2trptr);          /* Free the data */
      /* if there is only one dimension, this is a simple string */
      if ((ndim == 1) && (matdum[0] == 1)) {
        ndim = 0;
        Mclass = CLASS_S;       /* Fixed length, length in len */
      }
    } else if (mxIsCell(Tarray)) {
      lmax = 0;                 /* find max length of a cell */
      for (j = len; j--;) {
        if (!mxIsChar(mxGetCell(Tarray, j)) || mxGetM(mxGetCell(Tarray, j)) > 1)
          mexErrMsgIdAndTxt("mdsipmex:error", "mdsipmex: Cell array can only contain 1D character Arrays");
        k = (int) mxGetN(mxGetCell(Tarray, j));
        if (k > lmax)
          lmax = k;
      }
      /* Allow room for all data and extra space for 0 at the end */
      cptr = strptr = (char *) MatAlloc(lmax * len + 1, sizeof(char));
      memset(strptr, ' ', lmax * len);  /* fill with spaces */
      for (j = 0; j < len; j++) {
        mxGetString(mxGetCell(Tarray, j), cptr, lmax + 1);
        /* put back the spaces */
        cptr[strlen(cptr)] = ' ';
        cptr += lmax;           /* point to next string */
      }
      for (j = 0; j < ndim; j++)
        matdum[j] = ndimP[j];   /* copy dimensions of matrix */
      matdum[MAX_DIMS] = lmax;  /* SPECIAL.... copy w_length into last element */
    }                           /* End of CELL data */
    /* Remove a second dimension if 1 to reduce rank in remote server */
    if (ndim == 2 && matdum[ndim - 1] == 1) {
      ndim--;
    }
    /* Dec 2002; if string is empty, mdsplus gets confused if there are dimensions */
    if (lmax == 0) {
      ndim = 0;
      Mclass = CLASS_S;         /* Fixed length, length in len */
    }
#ifdef DEBUG
    printf("Send String %d,%d,%d,%d,%d,%d,%d,[%s]\n", i - opt, nrhs - opt, DTYPE_CSTRING, lmax, ndim, matdum[0], matdum[1], strptr);
#endif
    status = BSendArg(sock, (unsigned char) (i - opt), DTYPE_CSTRING, (unsigned char) (nrhs - opt)
                      , (short) lmax, (char) ndim, matdum, strptr, 1, Mclass);
  }                             /* end of text data */
  else {
    mexErrMsgIdAndTxt("mdsipmex:error", "mdsipmex: Untreated data type encountered: %s", mxGetClassName(Tarray));
  }
  if (!(status & 1)) {
    mexErrMsgIdAndTxt("mdsipmex:connection", "mdsipmex: Error in sending argument, check connection status");
  }
}

/* Note: This command is shielded by mdsput.m that calls mdsvalue directly */
/* mdsput ======================================================================*/
static void Bmdsput(int nlhs, mxArray * plhs[], int nrhs, CONST mxArray * prhs[])
{
  char *Pstr = "TreePut($,$,$,$,$,$,$,$,$,$,$,$,$,$,$,$,$,$,$,$)";
  int opt = mxIsChar(prhs[0]) ? 0 : 1;  /* Offset */

  /* Check there is an active connection */
  if (sock == INVALID_SOCKET)
    mexErrMsgIdAndTxt("mdsipmex:error", "mdsipmex: No active connection to MDSip server");

  if ((nrhs - opt) < 2)
    mexErrMsgIdAndTxt("mdsipmex:error", "mdsipmex: mdsput requires at least 3 parameters");

  /* Check is string */
  if (!mxIsChar(prhs[opt]))
    mexErrMsgIdAndTxt("mdsipmex:error", "mdsput: Put argument not string");

  /* make Expression */
  Pstr[7 + (nrhs - opt) * 2] = ')';
  Pstr[8 + (nrhs - opt) * 2] = '\0';
#ifdef DEBUG
  printf("PUT [%s],opt[%d],nrhs[%d]\n");
#endif
  Bmdsvalue(0, Pstr, nlhs, plhs, nrhs, prhs);
}

/* mdsvalue ======================================================================*/
static void Bmdsvalue(int oconv, char *EXP, int nlhs, mxArray * plhs[], int nrhs, CONST mxArray * prhs[])
{
  int i;
  int status = 1;               /* check status of calls to mdsipshr.exe */
  int opt = mxIsChar(prhs[0]) ? 0 : 1;  /* Offset */
  int Eflag = (EXP != NULL);    /* was expression provided ? */
  if (Eflag)
    opt = 0;                    /* If Expression given, no offset possible */

  /* Check there is an active connection */
  if (sock == INVALID_SOCKET)
    mexErrMsgIdAndTxt("mdsipmex:error", "mdsipmex: No active connection to MDSip server");

  /* Check is string */
  if (!mxIsChar(prhs[opt]))
    mexErrMsgIdAndTxt("mdsipmex:error", "mdsipmex:mdsvalue, First argument not string");

  /* JUNE12, add preliminary expression */
  if (Eflag) {
    mxArray *Brhs[1];
    Brhs[0] = mxCreateString(EXP);      /* Expression String */
    SendMatValue((CONST mxArray **) Brhs, 0, nrhs + Eflag, 0);
    mxDestroyArray(Brhs[0]);    /* free memory */
  }
  /* Cycle through the commands one at a time */
  for (i = opt; i < nrhs; i++) {
    SendMatValue(prhs, i, nrhs, opt - Eflag);
  }                             /* end of input data */
  GetMatReply(oconv, nlhs, plhs);
}

/* Get a list of the open connections */
static int bMdsList(int nlhs, mxArray * plhs[], int nrhs, CONST mxArray * prhs[])
{
  bConnection *cptr;
  int i = 0;
  int matFlg = 1;

  for (cptr = Connections; (cptr != NULL); cptr = cptr->next) {
    i++;
    /* This has to be a valid connection */
    printf("Name[%20.20s] Socket[%3d] unique[%s]", cptr->serv, cptr->sock, cptr->unique);
    if (cptr->sock == sock) {
      printf("  <-- active");
      if (nlhs > 1) {
        char stmp[STRLEN];
        sprintf(stmp, "Name[%20.20s] Socket[%3d] unique[%s]", cptr->serv, cptr->sock, cptr->unique);
        plhs[1] = mxCreateString(stmp);       /* Return actual server */
        matFlg = 0;
      }
    }
    printf("\n");
  }
  if (matFlg)
    plhs[1] = mxCreateString("none");
  return (i);
}

/* routine returns socket if valid */
static SOCKET BAddConnection(char *server)
{
  int status;
  bConnection *cptr;
  SOCKET nsock;

#ifdef NOUNIQ
#ifdef LOCAL_CONNECT
  /* Even if NOUNIQ is defined, we can have only one local connection */
  if (strcmp(server, "local") == 0)
    for (cptr = Connections; (cptr != NULL); cptr = cptr->next)
      if (cptr->sock == LOCAL_SOCKET)
        return (cptr->sock);
#endif
#else
  char *unique = (char *) MatAlloc(UNIQLEN, 1);
#ifdef LOCAL_CONNECT
  if (strcmp(server, "local") == 0)
    strcpy(unique, server);     /* copy "local" into the unique */
  else
#endif                          /* LOCAL_CONNECT */
    if ((status = LReuseCheck(server, unique, UNIQLEN)) == INVALID_SOCKET) {
      mxFree(unique);
      mexPrintf("hostname [%s] invalid, No Connection\n", server);
      return (INVALID_SOCKET);
    }

  /* Scan through current list looking for a match to unique */
  for (cptr = Connections; (cptr != NULL); cptr = cptr->next) {
    if (strcmp(unique, cptr->unique) == 0) {
      mxFree(unique);
#ifdef DEBUG
      printf("mdsconnect: Keep socket! name changed from  [%s] to [%s]\n", cptr->serv, server);
#endif
      /* found a match, replace string and return socket */
      strcpy(cptr->serv, server);
      return(cptr->sock);
    }
  }
#endif                          /* NOUNIQ */

#ifdef LOCAL_CONNECT
  if (strcmp(server, "local") == 0)
    nsock = LOCAL_SOCKET;       /* Mark socket as local */
  else
#endif                          /* LOCAL_CONNECT */
  /* See if the connection works */
  if ((nsock = LConnectToMds(server)) == INVALID_SOCKET) {
    printf("mdsconnect: Could not open connection to MDS server\n");
    return (INVALID_SOCKET);
  }

  /* Create a new connection and insert at the top */
#ifdef DEBUG
  printf("Making new Connection\n");
#endif
  cptr = (bConnection *) MatAlloc(sizeof(bConnection), 1);
  mexMakeMemoryPersistent(cptr);      /* make non discardable */
  cptr->next = Connections;
  Connections = cptr;

  /* Copy in the connection details */
#ifdef NOUNIQ
  cptr->unique = NULL;
#else
  cptr->unique = unique;
  mexMakeMemoryPersistent(unique);	/* make non discardable */
#endif
  cptr->sock = nsock;
  strcpy(cptr->serv, server);   /* Copy in the name */
  return (nsock);
}

/* BPD, Nov 2010, Change the default server and/or return the current default */
static void Bmdsdefaultserver(int nlhs, mxArray * plhs[], int nrhs, CONST mxArray * prhs[])
{
  if (nrhs > 1) {               /* Something provided */
    if (mxIsChar(prhs[1])) {
      mxGetString(prhs[1], defServer, STRLEN);
    } else
      mexErrMsgIdAndTxt("mdsipmex:error", "mdsdefaultserver requires a string ie: mdsdefaultserver('machine:port');");
  }
  if (nlhs > 0)
    plhs[0] = mxCreateString(defServer);        /* Return default */
}

/* mdsconnect, Nov 2010, separated from mdsopen */
static int32 Bmdsconnect(char *serverIN, int nlhs, mxArray * plhs[], int nrhs, CONST mxArray * prhs[])
{
  char server[STRLEN];
  SOCKET nsock;
  int32 status = 1;
  if (serverIN == NULL) {       /* was a string provided directly */
    if (nrhs != 2)
      mexErrMsgIdAndTxt("mdsipmex:error", "mdsconnect Requires one parameter ie: mdsconnect('mdsServer:port')");
    /* check character string is sent */
    if (mxIsChar(prhs[1]) && !mxIsEmpty(prhs[1])) {
      mxGetString(prhs[1], server, STRLEN);
    } else
      mexErrMsgIdAndTxt("mdsipmex:error", "mdsconnect Requires a non-empty STRING parameter");
  } else
    strcpy(server, serverIN);
  /* Add connection or return already present connection */
  if ((sock == INVALID_SOCKET) || strcmp(server, serv)) {
    if ((nsock = BAddConnection(server)) == INVALID_SOCKET) {
      *serv = '\0';
      sock = INVALID_SOCKET;
      status = 0;
    } else {
      mexAtExit(&ExitFcn);      /* install exit function */
      sock = nsock;
      strcpy(serv, server);     /* copy new name to memory, keep socket open */
    }
  }
  if (serverIN == NULL) {       /* was a string provided directly */
    /* output to calling code */
    if (nlhs > 0)
      plhs[0] = mxCreateDoubleScalar(status);
    return (0);
  } else {
#ifdef DEBUG
    printf("Bmdsconnect return with %d\n", status);
#endif
    return (status);
  }
}

/* mdsopen and server connect */
static void Bmdsopen(int nlhs, mxArray * plhs[], int nrhs, CONST mxArray * prhs[])
{
  char *server = defServer;
  char *cptr;
  char trees[STRLEN], temp[STRLEN];
  char *tree = trees;
  int32 shot, status;

  status = 1;                   /* May 2004, default value */

  /* Open server connection if not available */
  if (prhs) {
    /* check character string is sent */
    if (nrhs < 3)
      mexErrMsgIdAndTxt("mdsipmex:error", "mdsopen: Requires two parameters ie. mdsopen('tcv_shot',123)");

    if (mxIsChar(prhs[1])) {
      mxGetString(prhs[1], trees, STRLEN);
    }
    /* No, this is not a string array. */
    else
      mexErrMsgIdAndTxt("mdsipmex:error", "mdsopen: must enter character string for tree");
    /* Look for server in tree */
    if ((cptr = strstr(trees, SERVERTEST)) != NULL) {
      *cptr = '\0';             /* terminate the new server string */
      strcpy(temp, trees);      /* copy string into temp */
      server = temp;            /* point server to new string */
      tree = cptr + strlen(SERVERTEST); /* skip over initial part of string */
      /* check to see if a port was requested */
      if ((cptr = strstr(tree, PORTTEST)) != NULL) {
        strcat(server, cptr);   /* add port to end */
        *cptr = '\0';
      }
    } else {
      /* restore server name if connection open */
      if (sock != INVALID_SOCKET) {
        server = serv;          /* point server name to old string */
      }
    }
    status = Bmdsconnect(server, 0, NULL, 0, NULL);
  }

  /* Special to open the connection from mdsopen */
  if (plhs == NULL && prhs == NULL)
    status = 0;

  /* If string empty, or no shot given (ie string) do not perform mdsopen */
  if (status && strlen(tree) && !mxIsChar(prhs[2])) {

    /* Get Shot number */
    if ((mxGetM(prhs[2]) * mxGetN(prhs[2])) != 1)
      mexErrMsgIdAndTxt("mdsipmex:error", "mdsopen: shot number is a single number");
    else
      shot = (int) *mxGetPr(prhs[2]);   /* single number */
    /* Open the shot and get status */
#ifdef DEBUG
    printf("On server [%s], Opening tree:[%s] shot:[%d]\n", server, tree, shot);
#endif
/* June12: recycle MdsOpen call back to mex version to preserve local connection */
    {
      mxArray *Brhs[2];
      /*      Brhs[0] = mxCreateString("TreeOpen($,nint($))"); */
      Brhs[0] = mxCreateString(tree);   /* String containig Tree */
      Brhs[1] = mxCreateDoubleScalar(shot);     /* Shot number the same */
#ifdef DEBUG
      printf("Calling Bmdsvalue\n");
#endif
      Bmdsvalue(0, "TreeOpen($,nint($))", nlhs, plhs, 2, (CONST mxArray **) Brhs);
      mxDestroyArray(Brhs[0]);  /* free memory */
      mxDestroyArray(Brhs[1]);  /* free memory */
    }
  } else {
    status = (int) sock;
#ifdef DEBUG
    printf("mdsopen: status [%d]\n", status);
#endif
    /* output to calling code */
    if (nlhs > 0)
      plhs[0] = mxCreateDoubleScalar(status);
  }
}

/* mdsclose and server connect */
static void Bmdsclose(int nlhs, mxArray * plhs[], int nrhs, CONST mxArray * prhs[])
{
  int status;

  /* Check there is an active connection */
  if (sock == INVALID_SOCKET)
    mexErrMsgIdAndTxt("mdsipmex:error", "mdsipmex: No active connection to MDSip server");

  status = LMdsClose(sock);

  if (nlhs > 0)
    plhs[0] = mxCreateDoubleScalar(status);
}

/* mdsdisconnect ==================================================================== */
static void Bmdsdisconnect(int nlhs, mxArray * plhs[], int nrhs, CONST mxArray * prhs[])
{
  int status;
  bConnection *cptr, *ocptr;

  if (sock != INVALID_SOCKET) {
#ifdef LOCAL_CONNECT
    if (sock == LOCAL_SOCKET)
      status = 1;
    else
#endif
      status = LDisconnectFromMds(sock);
    for (cptr = Connections; (cptr != NULL); cptr = cptr->next) {
      if (cptr->sock == sock) {
        /* Remove connection and free connection memory */
        if (cptr == Connections) {      /* First in the list */
          Connections = cptr->next;     /* Could be NULL */
        } else {                        /* In list (but NOT first) */
          ocptr->next = cptr->next;     /* Could be NULL */
        }
#ifdef SHARED
#ifdef NOUNIQ
#else
        if (cptr->unique != NULL)
          mxFree(cptr->unique);
        cptr->unique = NULL;    /* mark correctly */
#endif
#endif
        mxFree(cptr);           /* free the locked memory */
        break;
      }
      ocptr = cptr;             /* Save pointer to previous in list */
    }
  } else {
    printf("mdsdisconnect: warning, communication socket aleady closed\n");
    status = 0;
  }
  sock = INVALID_SOCKET;
  *serv = '\0';                 /* Clear current server string */
  /* copy status as reply */
  if (nlhs > 0)
    plhs[0] = mxCreateDoubleScalar(status);
}

#ifdef PASSWD
int Lgihpwd()
{
  return (1);
}
#endif

/*============== June 2012, direct call routines */
#ifdef LOCAL_CONNECT
/* definitions from ipdesc.h for MdsIp descriptor */
static int MdsToIp(struct descriptor *tdiarg)
{
  int dtype;
  short llen;
  short *len;
  static EMPTYXD(xd);

  len = &llen;                  /* ensure writing is not invalid */

  switch (tdiarg->dtype) {
  case DTYPE_BU:               /*      2               byte (unsigned);  8-bit unsigned quantity */
    dtype = DTYPE_UCHAR;
    *len = sizeof(char);
    break;
  case DTYPE_WU:               /*      3               word (unsigned);  16-bit unsigned quantity */
    dtype = DTYPE_USHORT;
    *len = sizeof(short);
    break;
  case DTYPE_LU:               /*      4               longword (unsigned);  32-bit unsigned quantity */
    dtype = DTYPE_ULONG;
    *len = sizeof(int);
    break;
  case DTYPE_QU:               /*      5               quadword (unsigned); 64-bit unsigned quantity */
    dtype = DTYPE_ULONGLONG;
    *len = 8;
    break;
  case DTYPE_B:                /*      6               byte integer (signed);  8-bit signed 2's-complement integer */
    dtype = DTYPE_CHAR;
    *len = sizeof(char);
    break;
  case DTYPE_W:                /*      7               word integer (signed);  16-bit signed 2's-complement integer */
    dtype = DTYPE_SHORT;
    *len = sizeof(short);
    break;
  case DTYPE_L:                /*      8               longword integer (signed);  32-bit signed 2's-complement integer */
    dtype = DTYPE_LONG;
    *len = sizeof(int);
    break;
  case DTYPE_Q:                /*      9               quadword integer (signed); 64-bit signed 2's-complement integer */
    dtype = DTYPE_LONGLONG;
    *len = 8;
    break;
  case DTYPE_T:                /*      14              character string;  a single 8-bit character or a sequence of characters */
    dtype = DTYPE_CSTRING;
    *len = (short) (tdiarg->length ? tdiarg->length : (tdiarg->pointer ? strlen(tdiarg->pointer) : 0));
    break;
  case DTYPE_F:                /*      10              F_floating;  32-bit single-precision floating point */
  case DTYPE_FS:               /*      52              IEEE float basic single S */
    if (tdiarg->dtype != DTYPE_NATIVE_FLOAT)
      printf("Descriptor type [%d]\n not DTYPE_NATIVE_FLOAT handled\n", tdiarg->dtype);
    dtype = DTYPE_FLOAT;
    *len = sizeof(float);
    break;
  case DTYPE_D:
  case DTYPE_G:
  case DTYPE_FT:               /*      53              IEEE float basic double T */
    if (tdiarg->dtype != DTYPE_NATIVE_DOUBLE)
      printf("Descriptor type [%d]\n not DTYPE_NATIVE_DOUBLE handled\n", tdiarg->dtype);
    dtype = DTYPE_DOUBLE;
    *len = sizeof(double);
    break;
  case DTYPE_FC:
  case DTYPE_FSC:              /*      54              IEEE float basic single S complex */
    if (tdiarg->dtype != DTYPE_FLOAT_COMPLEX)
      printf("Descriptor type [%d]\n not DTYPE_FLOAT_COMPLEX handled\n", tdiarg->dtype);
    dtype = DTYPE_COMPLEX;
    *len = sizeof(float) * 2;
    break;
  case DTYPE_DC:
  case DTYPE_GC:
  case DTYPE_FTC:              /*      54              IEEE float basic single S complex */
    if (tdiarg->dtype != DTYPE_DOUBLE_COMPLEX)
      printf("Descriptor type [%d]\n not DTYPE_DOUBLE_COMPLEX handled\n", tdiarg->dtype);
    dtype = DTYPE_COMPLEX_DOUBLE;
    *len = sizeof(float) * 2;
    break;


  case DTYPE_Z:                /*      0               unspecified */
    dtype = 0;
    *len = 0;
    break;
  case DTYPE_H:                /*      28              H_floating;  128-bit quadruple-precision floating point */
  case DTYPE_OU:               /*      25              octaword (unsigned);  128-bit unsigned quantity */
  case DTYPE_O:                /*      26              octaword integer (signed);  128-bit signed 2's-complement integer */
  case DTYPE_HC:               /*      30              H_floating complex */
#ifdef DEPRECATED_DTYPES       /* For compatibility with MDSplus version 7.79.0 and later */
  case DTYPE_CIT:              /*      31              COBOL Intermediate Temporary */
  case DTYPE_VT:               /*      37              varying character string;  16-bit count, followed by a string */
  case DTYPE_NU:               /*      15              numeric string, unsigned */
  case DTYPE_NL:               /*      16              numeric string, left separate sign */
  case DTYPE_NLO:              /*      17              numeric string, left overpunched sign */
  case DTYPE_NR:               /*      18              numeric string, right separate sign */
  case DTYPE_NRO:              /*      19              numeric string, right overpunched sign */
  case DTYPE_NZ:               /*      20              numeric string, zoned sign */
  case DTYPE_P:                /*      21              packed decimal string */
  case DTYPE_V:                /*      1               aligned bit string */
  case DTYPE_VU:               /*      34              unaligned bit string */
#endif
  default:
    printf("Descriptor type [%d]\n not handled\n", tdiarg->dtype);
    *len = dtype = 0;
  }
  return (dtype);
}

static int IpToMds(int dtypein)
{
  int dtype;
  switch (dtypein) {
  case DTYPE_UCHAR:
    dtype = DTYPE_BU;
    break;                      /*      2                byte (unsigned);  8-bit unsigned quantity */
  case DTYPE_USHORT:
    dtype = DTYPE_WU;
    break;                      /*      3                word (unsigned);  16-bit unsigned quantity */
  case DTYPE_ULONG:
    dtype = DTYPE_LU;
    break;                      /*      4                longword (unsigned);  32-bit unsigned quantity */
  case DTYPE_ULONGLONG:
    dtype = DTYPE_QU;
    break;                      /*      5    quadword (unsigned); 64-bit unsigned quantity */
  case DTYPE_CHAR:
    dtype = DTYPE_B;
    break;                      /*      6                byte integer (signed);  8-bit signed 2's-complement integer */
  case DTYPE_SHORT:
    dtype = DTYPE_W;
    break;                      /*      7                word integer (signed);  16-bit signed 2's-complement integer */
  case DTYPE_LONG:
    dtype = DTYPE_L;
    break;                      /*      8                longword integer (signed);  32-bit signed 2's-complement integer */
  case DTYPE_LONGLONG:
    dtype = DTYPE_Q;
    break;                      /*      9    quadword integer (signed);  64-bit signed 2's-complement integer */
  case DTYPE_FLOAT:
    dtype = DTYPE_NATIVE_FLOAT;
    break;                      /*  10    float 32-bit */
  case DTYPE_DOUBLE:
    dtype = DTYPE_NATIVE_DOUBLE;
    break;                      /* 11    double 64-bit */
  case DTYPE_COMPLEX:
    dtype = DTYPE_FLOAT_COMPLEX;
    break;                      /* 12    complex 32-bit real and imaginary */
  case DTYPE_COMPLEX_DOUBLE:
    dtype = DTYPE_DOUBLE_COMPLEX;
    break;                      /* 13    complex 64-bit real and imaginary */
  case DTYPE_CSTRING:
    dtype = DTYPE_T;
    break;                      /*     14                character string;  a single 8-bit character or a sequence of characters */
  }
  return (dtype);
}

static int Palloc[20];
static array_coeff *Pdes[20];
/*static struct descriptor_a *Pdes[20];*/

static int MakeDescriptor(array_coeff * Des, char dtype, short len, char ndims, int *dims, char *ptr, int Mclass)
{
  /* This makes a descriptor out of the information send to SendArg
   * dtype is the data type
   * class:       CLASS_S:fixed-length descriptor
   *              CLASS_D:dynamic string descriptor
   *              CLASS_A:array descriptor
   */
  int i, j;

  Des->length = len;            /* length of an array element in bytes */
  Des->dtype = dtype;           /* data type code */
  Des->class = Mclass;          /* descriptor class code = K_CLASS_A */
  Des->pointer = ptr;           /* address of first actual byte of data storage */
  Des->digits = 0;              /* if nonzero, number of decimal digits in internal representation */
  /* Des->aflags  = {0,1,1,0,0};  /* see include/mdsdescrip.h (note aflags[3] coeff) */
  Des->aflags.binscale = 0;
  Des->aflags.redim = 1;
  Des->aflags.column = 1;
  Des->aflags.coeff = 0;
  Des->aflags.bounds = 0;
  Des->dimct = ndims;           /* number of dimensions */
  if (Mclass == CLASS_A) {      /* Treat array variables */
    Des->aflags.coeff = 1;      /* Signal coeff present */
    Des->a0 = ptr;              /* Address of the element whose subscripts are all zero */
    for (i = 0, j = len; i < ndims; i++) {
      if (dims[i]) {
        Des->m[i] = dims[i];
        j *= dims[i];
      } else
        break;
    }
    Des->arsize = j;            /* total size of array in bytes */
  } else
    Des->arsize = *dims * len;  /* only first dimension */
}
#endif                          /* LOCAL_CONNECT */

/* BSendArg- SendArg bifurcation for local (descriptor) or remote (descrip) parameters */
static int BSendArg(SOCKET s, unsigned char i, char dtype, unsigned char nargs, short len, char ndims, mwSize * mdims, char *ptr,
                    int alloc, int Mclass)
{
  int ans;
  int dims[MAX_DIMS + 1];
  int j;
  for (j = ndims; j--;)
    dims[j] = (int) mdims[j];   /* convert dims array to integers for MdsPlus */
#ifdef LOCAL_CONNECT
  /* if s==LOCAL_SOCKET, call to be made to TdiExecute */
  if (s == LOCAL_SOCKET) {
    int ttype;
    static EMPTYXD(sig);
    /* make and copy a descriptor */
    Palloc[i] = alloc;
    Pdes[i] = (array_coeff *) mxMalloc(sizeof(array_coeff));
    ttype = IpToMds(dtype);     /* Translate from MdsIp to Tdi DTYPE */
    MakeDescriptor(Pdes[i], ttype, len, ndims, dims, ptr, Mclass);
#ifdef DEBUG
    {
      array_coeff *D = Pdes[i];
      printf("DES-args[%d] length[%d] dtype[%d] class[%d] dimct[%d] arsize[%d]\n", i, D->length, D->dtype, D->class, D->dimct,
             D->arsize);
    }
#endif
    if (i >= (nargs - 1)) {     /* Last one */
      Pdes[nargs] = (array_coeff *) & sig;      /* place for answer */
      Pdes[nargs + 1] = (array_coeff *) MdsEND_ARG;     /* Signal list end */
      if ((LTdiExecute
           (Pdes[0], Pdes[1], Pdes[2], Pdes[3], Pdes[4], Pdes[5], Pdes[6], Pdes[7], Pdes[8], Pdes[9], Pdes[10], Pdes[11], Pdes[12],
            Pdes[13], Pdes[14], Pdes[15], Pdes[16], Pdes[17], Pdes[18], Pdes[19]) & 1)) {
        /*      static DESCRIPTOR(clear_errors,"WRITE(*,DECOMPILE($)),DEBUG(4)"); */
        static DESCRIPTOR(clear_errors, "DEBUG(4)");
        LTdiData(sig.pointer, &ansD MDS_END_ARG);
        LTdiExecute(&clear_errors, &sig, &sig MDS_END_ARG);
      } else {
        /*      static DESCRIPTOR(error_out,"_MSG=DEBUG(0),DEBUG(4),WRITE(*,_MSG)"); */
        static DESCRIPTOR(error_out, "_MSG=DEBUG(0),DEBUG(4);_MSG");
        LTdiExecute(&error_out, &ansD MDS_END_ARG);
      }
      LMdsFree1Dx(&sig, 0);     /* Free the intermediate descriptor */
#ifdef DEBUG
      {
        /* write out what came back */
        struct descriptor *D = ansD.pointer;
        if (ansD.pointer != NULL)
          printf("OUT-length[%d] dtype[%d] class[%d]\n", D->length, D->dtype, D->class);
        else
          printf("OUT- got a NULL\n");
      }
#endif
      /* remove the pointers and deallocate memory if appropriate */
      for (i = 0; i < nargs; i++) {
        if (Palloc[i])
          mxFree(Pdes[i]->pointer);     /* Allocated by Matlab */
        mxFree(Pdes[i]);        /* Allocated above */
      }
    }
    ans = 1;
  } else {
#endif                          /* LOCAL_CONNECT */
    ans = LSendArg(s, i, dtype, nargs, len, ndims, dims, ptr);
    if (alloc)
      mxFree(ptr);              /* free allocated Matlab Variable */
#ifdef LOCAL_CONNECT
  }
#endif                          /* LOCAL_CONNECT */
  return (ans);
}

static int BGetAnswerInfo(SOCKET s, char *dtype, short *len, char *ndims, int *dims, int *nbytes, void **dptr, void **msgPtr)
{
  int status;

#ifdef LOCAL_CONNECT
  int num, j;
  struct descriptor_a *desca;
  if (s == LOCAL_SOCKET) {
    if (ansD.pointer != NULL) {
#ifdef DEBUG
      {
        /* write out what came back */
        array_coeff *D = (array_coeff *) ansD.pointer;
        printf("ANS2-length[%d] dtype[%d] class[%d] dimct[%d] arsize[%d]\n", D->length, D->dtype, D->class, D->dimct, D->arsize);
      }
#endif
      *dtype = MdsToIp(ansD.pointer);   /* covert and copy header */
#ifdef DEBUG
      printf("Remap [%d]->[%d]\n", ansD.pointer->dtype, *dtype);
#endif
      *len = ansD.pointer->length;
      *dptr = ansD.pointer->pointer;
      *nbytes = *len;
      switch (ansD.pointer->class) {    /* switch between fixed and array returns in xd */
      case CLASS_S:            /* fixed-length descriptor */
        *ndims = 1;             /* one dimension */
        *dims = 1;              /* only one element */
        break;
      case CLASS_A:            /* Array descriptor */
        desca = (struct descriptor_a *) ansD.pointer;
        num = desca->arsize / desca->length;
        *ndims = desca->dimct;  /* dimensions count */
        if (desca->aflags.coeff) {
          array_bounds *bptr = (array_bounds *) desca;  /* access the array bounds of descriptor_a */
          for (j = 0; j < desca->dimct; j++) {
            dims[j] = bptr->m[j];       /* copy in the dimensions */
            *nbytes *= dims[j];
          }
        } else
          dims[0] = num;        /* simple vector assignment */
        break;
      }
      status = 1;
    } else {                    /* return pointer empty */
#ifdef DEBUG
      printf("Returned Pointer NULL\n");
#endif
      status = *ndims = *dims = *nbytes = 0;
      *dptr = NULL;
    }
  } else
#endif                          /* LOCAL_CONNECT */
#ifdef TIMEOUT
#ifdef SHARED
  if (LGetAnswerInfoTO != NULL)
    status = LGetAnswerInfoTO(sock, dtype, len, ndims, dims, nbytes, dptr, msgPtr, GetAnswerTimeout);
  else
#endif
#endif
  status = LGetAnswerInfo(sock, dtype, len, ndims, dims, nbytes, dptr);
  return (status);
}

/*============== July 2020, Set timeouts */
static void BSetMdsTimeout(int nlhs,mxArray *plhs[],int nrhs,CONST mxArray *prhs[]){
#ifdef TIMEOUT
  double *inP, *outP;
  inP     = mxGetPr(prhs[1]);       /* Pointer to timeouts, first argument to mdsipmex is always mode */
  if (!(mxIsNumeric(prhs[1]) && (mxGetNumberOfElements(prhs[1]) == 3)))
    mexErrMsgIdAndTxt("mdsipmex:mdstimeout","mdstimeout requires a 3 element vector: [connection, send, receive] in seconds");
  plhs[0] = mxCreateDoubleMatrix(3,1,mxREAL);
  outP    = mxGetPr(plhs[0]);
#ifdef SHARED       /* Here we set connect and GetAnswer timeouts */
  outP[0] = (double)(LSetMdsConnectTimeout((int)(inP[0])));
  outP[1] = -1; /* send timeout: disabled */
  outP[2] = (double) GetAnswerTimeout;
  GetAnswerTimeout = (int)(inP[2]);
#else       /* Here we call new routines in the modified mdsplus legacy routines */
#ifndef _WIN32  /* Not enabled for windows */
  outP[0] = (double)(SetMdsConnectTimeout((int)(inP[0])));
  outP[1] = (double)(SetMdsSendTimeout((int)(inP[1])));
  outP[2] = (double)(SetMdsReadTimeout((int)(inP[2])));
#else
  outP[0] = 0;
  outP[1] = 0;
  outP[2] = 0;
#endif
#endif
#else
  mexErrMsgIdAndTxt("mdsipmex:mdstimeout","mdstimeout has been disabled from this build");
#endif
}
