/*---------------------------------------------------------------------------

			S I N Q H M D R I V
			
   This is the SICS driver to the SINQ histogram memory.
   
   Mark Koennecke, April 1997
   
       Copyright:

       Labor fuer Neutronenstreuung
       Paul Scherrer Institut
       CH-5423 Villigen-PSI


      The authors hereby grant permission to use, copy, modify, distribute,
      and license this software and its documentation for any purpose, provided
      that existing copyright notices are retained in all copies and that this
      notice is included verbatim in any distributions. No written agreement,
      license, or royalty fee is required for any of the authorized uses.
      Modifications to this software may be copyrighted by their authors
      and need not follow the licensing terms described here, provided that
      the new terms are clearly indicated on the first page of each file where
      they apply.

      IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
      FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
      ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
      DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
      POSSIBILITY OF SUCH DAMAGE.

      THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
      INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
      FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
      IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
      NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
      MODIFICATIONS.

      Modified to work without counter in slave mode.
      Mark Koennecke, January 2001

----------------------------------------------------------------------------*/
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>
#include <fortify.h>
#include <sics.h>
#include <countdriv.h>
#include <counter.h>
#include <HistMem.h>
#include "hardsup/sinqhm.h"
#include "hardsup/sinqhm.i"     /* for byte switching stuff */
#include <stringdict.h>
#include <HistDriv.i>
#include "sinqhmdriv.i"
/*-----------------------------------------------------------------------*/
static void PrintHMError(char *text, SConnection * pCon)
{
  char pBueffel[1064];

  strcpy(pBueffel, "ERROR: Histogram Memory --> ");
  strlcat(pBueffel, text,1024);
  SCWrite(pCon, pBueffel, eError);
}

/*-------------------------------------------------------------------------*/
static void ErrInit(SinqHMDriv * self)
{
  self->iLastHMError = 0;
  self->iLastCTError = 0;
}

/*------------------------------------------------------------------------*/
static char *pHistMode[] = {
  "transparent",
  "normal",
  "tof",
  "strobo",
  "hrpt",
  "psd",
  "sanstof",
  NULL
};

static HistMode Text2Mode(char *text)
{
  int i = 0;

  while (pHistMode[i] != NULL) {
    if (strcmp(pHistMode[i], text) == 0) {
      return i;
    }
    i++;
  }
  /* not found */
  return -1;
}

/*------------------------------------------------------------------------*/
static char *pFlowMode[] = {
  "ignore",
  "ceil",
  "count",
  "reflect",
  NULL
};

static OverFlowMode Text2Flow(char *text)
{
  int i = 0;

  while (pFlowMode[i] != NULL) {
    if (strcmp(pFlowMode[i], text) == 0) {
      return i;
    }
    i++;
  }
  /* not found */
  return -1;
}

/*--------------------------------------------------------------------------
  Configure deconfigures first, in order to have a clean state. Than the HM
  is configured and a client connection will be built. Configure requires, 
  that the pSINQHM data structure has been initialised by CreateSINQHMDriver.
  */

static int SQConfigure(pHistDriver self, SConnection * pCon,
                       pStringDict pOpt, SicsInterp * pSics)
{
  SinqHMDriv *pInternal;
  int status, iMode;
  char pError[132];
  char pHMComputer[256], pValue[80], pBueffel[256];
  float fVal;
  char pcCounter[256];
  CommandList *pCom = NULL;
  int iStart = 0;
  int iInit = 0, i, iRet;
  int xOff, xFac, yOff, yFac;
  int iPort, iLength;
  int extraDetectors, noDet;

  assert(self);
  assert(self->pPriv);
  assert(pCon);
  assert(pOpt);

  pInternal = (SinqHMDriv *) self->pPriv;
  ErrInit(pInternal);


  /* enter histmode */
  iRet = StringDictGet(pOpt, "histmode", pValue, 79);
  if (!iRet) {
    SCWrite(pCon, "ERROR: internal Value not found!", eError);
    return 0;
  }
  strtolower(pValue);
  iRet = Text2Mode(pValue);
  if (iRet < 0) {
    snprintf(pBueffel,255, "ERROR: Invalid parameter %s to HistMode", pValue);
    SCWrite(pCon, pBueffel, eError);
    return 0;
  }
  pInternal->eHistMode = iRet;

  /* handle overflowmode */
  iRet = StringDictGet(pOpt, "overflowmode", pValue, 79);
  if (!iRet) {
    SCWrite(pCon, "ERROR: internal Value not found!", eError);
    return 0;
  }
  strtolower(pValue);
  iRet = Text2Flow(pValue);
  if (iRet < 0) {
    snprintf(pBueffel,255, "ERROR: Invalid parameter %s to OverflowMode",
            pValue);
    SCWrite(pCon, pBueffel, eError);
    return 0;
  }
  pInternal->eFlow = iRet;

  /* BinWidth */
  iRet = StringDictGetAsNumber(pOpt, "binwidth", &fVal);
  if (!iRet) {
    iRet = StringDictGet(pOpt, "binwidth", pValue, 79);
    if (iRet) {
      snprintf(pBueffel,255, "ERROR: %s not valid for BinWidth ", pValue);
      SCWrite(pCon, pBueffel, eError);
      return 0;
    } else {
      SCWrite(pCon, "ERROR: internal Value not found!", eError);
      return 0;
    }
  }
  if (fVal < 1) {
    snprintf(pBueffel,255, "ERROR: %f is invalid for BinWidth", fVal);
    SCWrite(pCon, pBueffel, eError);
    return 0;
  }
  pInternal->iBinWidth = (int) fVal;

  /*
     extra detectors
   */
  iRet = StringDictGet(pOpt, "extradetector", pValue, 79);
  if (iRet) {
    extraDetectors = atoi(pValue);
  } else {
    extraDetectors = 0;
  }
  pInternal->extraDetector = extraDetectors;



  /* check BinWidth */
  if ((pInternal->iBinWidth != 1) && (pInternal->iBinWidth != 2)
      && (pInternal->iBinWidth != 4)) {
    PrintHMError("Unsuported BinWidth specified, 1,2,4 are permissable",
                 pCon);
    return 0;
  }

  /* if not initialised, analyze options database for the stuff we need */
  if (pInternal->pMaster == NULL) {
    iStart = 1;
    /* the histogram memory */
    status = StringDictGet(pOpt, "hmcomputer", pHMComputer, 254);
    if (!status) {
      PrintHMError("No network name for histogram memory computer found",
                   pCon);
      return 0;
    }
    status = StringDictGetAsNumber(pOpt, "hmport", &fVal);
    if (!status) {
      PrintHMError("No network name for histogram memory computer found",
                   pCon);
      return 0;
    }
    iPort = (int) fVal;

    /* the counter */
    status = StringDictGet(pOpt, "counter", pcCounter, 254);
    if (!status) {
      SCWrite(pCon, "WARNING: no counter control found for this HM",
              eWarning);
      pInternal->pCounter = NULL;
    } else {
      pCom = FindCommand(pSics, pcCounter);
      if (!pCom) {
        PrintHMError("WARNING: no counter for HM found! ", pCon);
        pInternal->pCounter = NULL;
      } else {
        pInternal->pCounter = (pCounter) pCom->pData;
        if (!pInternal->pCounter->pDes->GetInterface(pInternal->pCounter,
                                                     COUNTID)) {
          PrintHMError("Counter for histogram memory is invalid", pCon);
          return 0;
        }
      }
    }
    /* ok! put in HM */
    pInternal->pMaster = CreateSINQHM(pHMComputer, iPort);
    if (!pInternal->pMaster) {
      PrintHMError("No memory to allocate SINQHM", pCon);
      return 0;
    }
  } else {
    /* close the connection if there is such a thing */
    SINQHMCloseDAQ(pInternal->pMaster);
  }

  /* in any case let us propagate the state of affairs to 
     SINQHM
   */

  SINQHMSetPar(pInternal->pMaster, self->data->rank,
               getHMDataLength(self->data), pInternal->iBinWidth);


  /* actual configuration. On first call, check for flag INIt in
     options. We do need to configure, if the HM has configured
     itself already. What is does, these days.
   */
  if (iStart) {
    status = StringDictGetAsNumber(pOpt, "init", &fVal);
    if (!status) {
      iInit = 0;
    } else {
      if (fVal > 0.9) {
        iInit = 1;
      }
    }
  }

  if (iInit == 0) {
    /* do a deconfig first */
    status = SINQHMDeconfigure(pInternal->pMaster, 1);
    if (status < 0) {
      SINQHMError2Text(status, pError, 131);
      PrintHMError(pError, pCon);
      return 0;
    }

    if ((pInternal->iBinWidth != 1) && (pInternal->iBinWidth != 2)
        && (pInternal->iBinWidth != 4)) {
      PrintHMError("Unsuported BinWidth specified, 1,2,4 are permissable",
                   pCon);
      return 0;
    }


    /* configure */
    switch (pInternal->eHistMode) {
    case eHTransparent:
      iMode = SQHM__TRANS;
      break;
    case eHNormal:
      iMode = SQHM__HM_DIG;
      break;
    case eHTOF:
    case eSANSTOF:
      iMode = SQHM__TOF;
      /* recalculate some parameters */
      noDet = 1;
      for (i = 0; i < self->data->rank; i++) {
        noDet *= self->data->iDim[i];
      }
      noDet += extraDetectors;
      iLength = noDet * self->data->nTimeChan;
      SINQHMDefineBank(pInternal->pMaster, 0, 0, noDet,
                       self->data->timeBinning, self->data->nTimeChan);
      break;
    case eHStrobo:
      iMode = SQHM__HM_PSD | SQHM__STROBO;
      break;
    case eHRPT:
      iMode = SQHM__HRPT;
      break;
    case ePSD:
      iMode = SQHM__HM_PSD;
      break;
    default:
      PrintHMError("Unsupported mode requested", pCon);
      return 0;
    }
    switch (pInternal->eFlow) {
    case eOIgnore:
      iMode = iMode | SQHM__BO_IGN;
      break;
    case eOCeil:
      iMode = iMode | SQHM__BO_SMAX;
      break;
    case eOCount:
      iMode = iMode | SQHM__BO_CNT;
      break;
    case eReflect:
      iMode = iMode | SQHM__REFLECT;
      break;
    default:
      PrintHMError("Unsupported overflowmode requested", pCon);
      return 0;
    }
    if (pInternal->eHistMode != ePSD) {
      status = SINQHMConfigure(pInternal->pMaster,
                               iMode,
                               self->data->rank,
                               getHMDataLength(self->data) +
                               extraDetectors * self->data->nTimeChan,
                               pInternal->iBinWidth, 0, 0);
    } else {
      status = StringDictGetAsNumber(pOpt, "xoff", &fVal);
      if (!status) {
        PrintHMError("No xOff value for PSD mode found", pCon);
        return 0;
      }
      xOff = (int) fVal;
      status = StringDictGetAsNumber(pOpt, "xfac", &fVal);
      if (!status) {
        PrintHMError("No xFac value for PSD mode found", pCon);
        return 0;
      }
      xFac = (int) fVal;
      status = StringDictGetAsNumber(pOpt, "yoff", &fVal);
      if (!status) {
        PrintHMError("No yOff value for PSD mode found", pCon);
        return 0;
      }
      yOff = (int) fVal;
      status = StringDictGetAsNumber(pOpt, "yfac", &fVal);
      if (!status) {
        PrintHMError("No yFac value for PSD mode found", pCon);
        return 0;
      }
      yFac = (int) fVal;
      /* xSize and ySize are supposed to be in dim */
      status = SINQHMConfigurePSD(pInternal->pMaster,
                                  iMode,
                                  self->data->iDim[0],
                                  xOff,
                                  xFac,
                                  self->data->iDim[1] +
                                  pInternal->extraDetector, yOff, yFac,
                                  pInternal->iBinWidth,
                                  self->data->timeBinning,
                                  self->data->nTimeChan);
    }

    if (status < 0) {
      SINQHMError2Text(status, pError, 131);
      PrintHMError(pError, pCon);
      return 0;
    }
  }

  /* initiate a client connection for DAQ */
  status = SINQHMOpenDAQ(pInternal->pMaster);
  if (status < 0) {
    SINQHMError2Text(status, pError, 131);
    PrintHMError(pError, pCon);
    return 0;
  }

  /* tell the counter box our current status */
  if (pInternal->pCounter != NULL) {
    SetCounterMode(pInternal->pCounter, self->eCount);
    SetCounterPreset(pInternal->pCounter, self->fCountPreset);
  }

  self->iReconfig = 0;
  return 1;
}

/*--------------------------------------------------------------------------*/
static int SQStart(pHistDriver self, SConnection * pCon)
{
  SinqHMDriv *pInternal;
  pICountable pCountInt = NULL;
  int status, iMode, nHist, length;
  char pError[132];

  assert(self);
  assert(pCon);
  assert(self->pPriv);

  pInternal = self->pPriv;

  /* tell the counter box our current status */
  if (pInternal->pCounter != NULL) {
    SetCounterMode(pInternal->pCounter, self->eCount);
    pInternal->pCounter->pDriv->fPreset = self->fCountPreset;
  }

  /* first zero the HM */
  if (pInternal->eHistMode == ePSD && self->data->nTimeChan > 2) {
    /*
       this is special for AMOR and should be replaced by -1, -1, -1
       logic ASAP
     */
    nHist = (self->data->iDim[0] * self->data->iDim[1] + 2) *
        self->data->nTimeChan;
    status = SINQHMZero(pInternal->pMaster, -1, -1, -1);
  } else {
    length = getHMDataLength(self->data);
    if (pInternal->extraDetector > 0 && isInTOFMode(self->data)) {
      length += pInternal->extraDetector * getNoOfTimebins(self->data);
    }
    status = SINQHMZero(pInternal->pMaster, -1, 0, length);
  }
  /*
     status = SINQHMZero(pInternal->pMaster,-1,-1,-1);
   */
  if (status < 0) {
    pInternal->iLastHMError = status;
    return HWFault;
  }

  /* start the HM */
  status = SINQHMStartDAQ(pInternal->pMaster);
  if ((status < 0) && (status != DAQ_INHIBIT)) {
    pInternal->iLastHMError = status;
    return HWFault;
  }

  /*
     When SICS was interrupted while the measurement was paused
     the inhibit flag may still be set. We clear this here in order to 
     fix this bug.
   */
  status = SINQHMContinueDAQ(pInternal->pMaster);
  if ((status < 0) && (status != DAQ_INHIBIT)) {
    pInternal->iLastHMError = status;
    return HWFault;
  }


  /* start the El737 counter */
  if (pInternal->pCounter != NULL) {
    pCountInt =
        pInternal->pCounter->pDes->GetInterface(pInternal->pCounter,
                                                COUNTID);
    if (pCountInt) {
      ReleaseCountLock(pCountInt);
      return pCountInt->StartCount(pInternal->pCounter, pCon);
    }
  }
  return 1;
}

/*--------------------------------------------------------------------------*/
static int SQPause(pHistDriver self, SConnection * pCon)
{
  SinqHMDriv *pInternal;
  pICountable pCountInt = NULL;
  int status, iMode;
  char pError[132];

  assert(self);
  assert(pCon);
  assert(self->pPriv);

  pInternal = self->pPriv;

  /* pause the HM */
  status = SINQHMInhibitDAQ(pInternal->pMaster);
  if ((status < 0) && (status != DAQ_INHIBIT)) {
    pInternal->iLastHMError = status;
    return HWFault;
  }

  /* pause the El737 counter */
  if (pInternal->pCounter != NULL) {
    pCountInt =
        pInternal->pCounter->pDes->GetInterface(pInternal->pCounter,
                                                COUNTID);
    if (pCountInt) {
      return pCountInt->Pause(pInternal->pCounter, pCon);
    }
  }
  return 1;
}

/*--------------------------------------------------------------------------*/
static int SQContinue(pHistDriver self, SConnection * pCon)
{
  SinqHMDriv *pInternal;
  pICountable pCountInt = NULL;
  int status, iMode;
  char pError[132];

  assert(self);
  assert(pCon);
  assert(self->pPriv);

  pInternal = self->pPriv;

  /* continue the HM */
  status = SINQHMContinueDAQ(pInternal->pMaster);
  if ((status < 0) && (status != DAQ_INHIBIT)) {
    pInternal->iLastHMError = status;
    return HWFault;
  }

  /* continue the El737 counter */
  if (pInternal->pCounter != NULL) {
    pCountInt =
        pInternal->pCounter->pDes->GetInterface(pInternal->pCounter,
                                                COUNTID);
    if (pCountInt) {
      return pCountInt->Continue(pInternal->pCounter, pCon);
    }
  }
  return 1;
}

/*--------------------------------------------------------------------------*/
static int SQHalt(pHistDriver self)
{
  SinqHMDriv *pInternal;
  pICountable pCountInt = NULL;
  int status, iRet;
  char pError[132];

  assert(self);
  assert(self->pPriv);

  pInternal = self->pPriv;
  iRet = 1;

  /* Halt HM */
  status = SINQHMStopDAQ(pInternal->pMaster);
  if (status < 0) {
    iRet = 0;
  }

  /* Halt counter */
  if (pInternal->pCounter != NULL) {
    pCountInt =
        pInternal->pCounter->pDes->GetInterface(pInternal->pCounter,
                                                COUNTID);
    if (pCountInt) {
      status = pCountInt->Halt(pInternal->pCounter);
      if (!status) {
        iRet = 0;
      } else {
        iRet = 1;
      }
    }
    return iRet;
  }
  return 1;
}

/*---------------------------------------------------------------------------*/
static int SQStatus(pHistDriver self, SConnection * pCon)
{
  SinqHMDriv *pInternal;
  pICountable pCountInt = NULL;
  int status, iRet;
  char pError[132];
  int iMode, iRank, iLength, iDaq, iBin, iClient;
  float fControl;

  assert(self);
  assert(self->pPriv);

  pInternal = self->pPriv;

  /* first check at counter */
  if (pInternal->pCounter != NULL) {
    pCountInt =
        pInternal->pCounter->pDes->GetInterface(pInternal->pCounter,
                                                COUNTID);
    if (pCountInt) {
      status = pCountInt->CheckCountStatus(pInternal->pCounter, pCon);
      if (status == HWFault) {
        ReleaseCountLock(pCountInt);
        return status;
      }
    }
  }

  /* now check at HM */
  iRet = SINQHMGetStatus(pInternal->pMaster, &iMode, &iDaq,
                         &iRank, &iBin, &iLength, &iClient);
  if (iRet < 0) {
    SINQHMError2Text(iRet, pError, 131);
    PrintHMError(pError, pCon);
    return HWFault;
  }

  if (iDaq == 2) {
    return HWPause;
  }
  /*
     else if(iDaq == 1)
     {
     status = HWBusy;
     } 
   */
  else if (iDaq == 0) {
    status = HWIdle;
  }

  return status;
}

/*--------------------------------------------------------------------------*/
static int SQError(pHistDriver self, int *iCode, char *pError, int iErrLen)
{
  SinqHMDriv *pInternal;

  assert(self);
  assert(self->pPriv);

  pInternal = self->pPriv;

  /* first check for HM errors */
  if (pInternal->iLastHMError < 0) {
    SINQHMError2Text(pInternal->iLastHMError, pError, iErrLen);
    *iCode = pInternal->iLastHMError;
    return 1;
  }

  /* now counter errors */
  if (pInternal->pCounter != NULL) {
    return pInternal->pCounter->pDriv->GetError(pInternal->pCounter->pDriv,
                                                iCode, pError, iErrLen);
  } else {
    /* this can't be */
    return 0;
  }
}

/*--------------------------------------------------------------------------*/
static int SQFixIt(pHistDriver self, int iCode)
{
  SinqHMDriv *pInternal;

  assert(self);
  assert(self->pPriv);

  pInternal = self->pPriv;

  /* check for HM Errors */
  if (pInternal->iLastHMError < 0) {
    switch (iCode) {
    case RECEIVE_ERROR:
    case INSUFFICIENT_DATA:
      /* may happen after a bad command. Currently the HM-client
         dies in this stage. This should fix this.
       */
      SINQHMCloseDAQ(pInternal->pMaster);
      SINQHMOpenDAQ(pInternal->pMaster);
      return COREDO;
      break;

    case BYTE_ORDER_CHAOS:
    case HIST_BAD_RECV:
    case SEND_ERROR:
      return COREDO;
      break;

    default:
      return COTERM;
    }
  }

  /* do counter errors */
  if (pInternal->pCounter != NULL) {
    return pInternal->pCounter->pDriv->TryAndFixIt(pInternal->pCounter->
                                                   pDriv, iCode);
  }
  return COTERM;
}

/*---------------------------------------------------------------------------*/
static int SQGetData(pHistDriver self, SConnection * pCon)
{
  SinqHMDriv *pInternal;
  pICountable pCountInt = NULL;
  int iMode, iDaq, iRank, iBin, iLength, iClient, iRet;
  char pError[132];

  assert(self);
  assert(self->pPriv);

  pInternal = self->pPriv;

  /* now check at HM */
  iRet = SINQHMGetStatus(pInternal->pMaster, &iMode, &iDaq,
                         &iRank, &iBin, &iLength, &iClient);
  if (iRet < 0) {
    SINQHMError2Text(iRet, pError, 131);
    PrintHMError(pError, pCon);
    return HWFault;
  }

  /* stop DAQ, if still active */
  /*
     if(iDaq == 1)
     {
     SINQHMStopDAQ(pInternal->pMaster);
     }
   */

  /* do only counter, histograms are read on demand */
  if (pInternal->pCounter != NULL) {
    pCountInt =
        pInternal->pCounter->pDes->GetInterface(pInternal->pCounter,
                                                COUNTID);
    if (pCountInt) {
      return pCountInt->TransferData(pInternal->pCounter, pCon);
    }
  }
  return 1;
}

/*--------------------------------------------------------------------------*/
static int SQGetHistogram(pHistDriver self, SConnection * pCon,
                          int iNum, int iStart, int iEnd, HistInt * plData)
{
  SinqHMDriv *pInternal;
  void *pData = NULL;
  char *pPtr;
  SQint16 *pPtr16;
  SQint32 *pPtr32;
  int i, iLen, iByte, status;
  char pError[132];

  assert(self);
  assert(self->pPriv);

  pInternal = self->pPriv;

  /* we do not need to do a lot of copying when datasizes match! */
  iByte = (iEnd - iStart) * pInternal->iBinWidth;
  if (pInternal->iBinWidth == sizeof(HistInt)) {
    /* read HM */
    status = SINQHMRead(pInternal->pMaster,
                        iNum, iStart, iEnd, plData, iByte);
    if (status < 0) {
      SINQHMError2Text(status, pError, 131);
      PrintHMError(pError, pCon);
      return 0;
    }
    return 1;
  }

  /* we need to convert datasizes: allocate storage */
  pData = (void *) malloc(iByte * sizeof(char));
  if (!pData) {
    PrintHMError("Out of memory in SinqHMDriv", pCon);
    return 0;
  }
  memset(pData, 0, iByte);

  /* read HM */
  status =
      SINQHMRead(pInternal->pMaster, iNum, iStart, iEnd, pData, iByte);
  if (status < 0) {
    SINQHMError2Text(status, pError, 131);
    PrintHMError(pError, pCon);
    return 0;
  }

  /* convert to correct datasize */
  switch (pInternal->iBinWidth) {
  case 1:
    pPtr = (char *) pData;
    for (i = 0; i < iEnd; i++) {
      plData[i] = (HistInt) pPtr[i];
    }
    break;
  case 2:
    pPtr16 = (SQint16 *) pData;
    for (i = 0; i < iEnd; i++) {
      plData[i] = (HistInt) pPtr16[i];
    }
    break;
  case 4:
    /* 32 bit is native here now. May need change on other machine, 
       no conversion necessary
     */
    pPtr32 = (SQint32 *) pData;
    /*     for(i = 0; i < iEnd; i++)
       {
       plData[i] = (HistInt)pPtr32[i];
       }
     */
    memcpy(plData, pPtr32, iEnd * sizeof(HistInt));
    break;
  }

  free(pData);
  return 1;
}

/*--------------------------------------------------------------------------*/
static int SQSetHistogram(pHistDriver self, SConnection * pCon,
                          int iNum, int iStart, int iEnd, HistInt * plData)
{
  SinqHMDriv *pInternal;
  void *pData = NULL;
  char *pPtr;
  SQint16 *pPtr16;
  SQint32 *pPtr32;
  int i, iLen, iByte, status;
  char pError[132];

  assert(self);
  assert(self->pPriv);

  pInternal = self->pPriv;

  /* allocate storage */
  iByte = iEnd * pInternal->iBinWidth;
  pData = (void *) malloc(iByte * sizeof(char));
  if (!pData) {
    PrintHMError("Out of memory in SinqHMDriv", pCon);
    return 0;
  }
  memset(pData, 0, iByte);

  /* convert from long to supported binwidth */
  switch (pInternal->iBinWidth) {
  case 1:
    pPtr = (char *) pData;
    for (i = 0; i < iEnd; i++) {
      pPtr[i] = plData[i];
    }
    break;
  case 2:
    pPtr16 = (SQint16 *) pData;
    for (i = 0; i < iEnd; i++) {
      pPtr16[i] = plData[i];
    }
    break;
  case 4:
    pPtr32 = (SQint32 *) pData;
    for (i = 0; i < iEnd; i++) {
      pPtr32[i] = plData[i];
    }
    break;
  }

  /* send data */
  status = SINQHMWrite(pInternal->pMaster, iNum, iStart, iEnd, pData);
  if (status < 0) {
    SINQHMError2Text(status, pError, 131);
    PrintHMError(pError, pCon);
    free(pData);
    return 0;
  }

  free(pData);
  return 1;

}

/*---------------------------------------------------------------------------*/
static long SQGetMonitor(pHistDriver self, int i, SConnection * pCon)
{
  SinqHMDriv *pInternal;
  char pError[132];

  assert(self);
  assert(self->pPriv);

  pInternal = self->pPriv;
  if (pInternal->pCounter != NULL) {
    return GetMonitor(pInternal->pCounter, i, pCon);
  }
  return 0;
}

/*---------------------------------------------------------------------------*/
static float SQGetTime(pHistDriver self, SConnection * pCon)
{
  SinqHMDriv *pInternal;
  char pError[132];

  assert(self);
  assert(self->pPriv);

  pInternal = self->pPriv;
  if (pInternal->pCounter != NULL) {
    return GetCountTime(pInternal->pCounter, pCon);
  }
  return -999.99;
}

/*---------------------------------------------------------------------------*/
static int SQPreset(pHistDriver self, SConnection * pCon, HistInt iVal)
{
  SinqHMDriv *pInternal;
  char pError[132];
  HistInt *plData = NULL;
  int i, status, nHist;

  assert(self);
  assert(self->pPriv);

  pInternal = self->pPriv;

  /* get memory */
  if (pInternal->eHistMode == ePSD && self->data->nTimeChan > 2) {
    nHist =
        self->data->iDim[0] * self->data->iDim[1] * self->data->nTimeChan;
  } else {
    nHist = getHMDataLength(self->data);
  }
  plData = (HistInt *) malloc(nHist * sizeof(HistInt));
  if (!plData) {
    PrintHMError("Out of memory in SinqHMDriv", pCon);
    return 0;
  }

  /* initialise */
  for (i = 0; i < nHist; i++) {
    plData[i] = iVal;
  }

  status = SQSetHistogram(self, pCon, -1, 0, nHist, plData);
  free(plData);
  return status;
}

/*--------------------------------------------------------------------------*/
static int SQFreePrivate(pHistDriver self)
{
  SinqHMDriv *pInternal;

  assert(self);
  assert(self->pPriv);

  pInternal = self->pPriv;

  if (pInternal->pMaster) {
    DeleteSINQHM(pInternal->pMaster);
  }
  free(pInternal);
  return 1;
}

/*--------------------------------------------------------------------------*/
pHistDriver CreateSINQDriver(pStringDict pOption)
{
  pHistDriver pNew = NULL;
  SinqHMDriv *pInternal = NULL;

  /* create the general driver */
  pNew = CreateHistDriver(pOption);
  if (!pNew) {
    return NULL;
  }

  /* add our options */
  StringDictAddPair(pOption, "hmcomputer", "psds02.psi.ch");
  StringDictAddPair(pOption, "hmport", "2400");
  StringDictAddPair(pOption, "counter", "Rudolf");
  StringDictAddPair(pOption, "xsize", "256");
  StringDictAddPair(pOption, "xoff", "100");
  StringDictAddPair(pOption, "xfac", "10");
  StringDictAddPair(pOption, "ysize", "256");
  StringDictAddPair(pOption, "yoff", "100");
  StringDictAddPair(pOption, "yfac", "10");
  StringDictAddPair(pOption, "init", "1");
  StringDictAddPair(pOption, "histmode", "normal");
  StringDictAddPair(pOption, "overflowmode", "ceil");
  StringDictAddPair(pOption, "binwidth", "4");

  /* initialise our private data structure */
  pInternal = (SinqHMDriv *) malloc(sizeof(SinqHMDriv));
  if (!pInternal) {
    free(pNew);
    return NULL;
  }
  memset(pInternal, 0, sizeof(SinqHMDriv));
  pNew->pPriv = pInternal;
  pInternal->eHistMode = eHNormal;
  pInternal->eFlow = eOCeil;
  pInternal->iBinWidth = 4;


  /* configure all those functions */
  pNew->Configure = SQConfigure;
  pNew->Start = SQStart;
  pNew->Halt = SQHalt;
  pNew->GetCountStatus = SQStatus;
  pNew->GetError = SQError;
  pNew->TryAndFixIt = SQFixIt;
  pNew->GetData = SQGetData;
  pNew->GetHistogram = SQGetHistogram;
  pNew->SetHistogram = SQSetHistogram;
  pNew->GetMonitor = SQGetMonitor;
  pNew->GetTime = SQGetTime;
  pNew->Preset = SQPreset;
  pNew->FreePrivate = SQFreePrivate;
  pNew->Pause = SQPause;
  pNew->SubSample = DefaultSubSample;
  pNew->Continue = SQContinue;

  return pNew;
}

/*--------------------------------------------------------------------------*/
int isSINQHMDriv(pHistDriver test)
{
  if (test->Start == SQStart)
    return 1;
  else
    return 0;
}
