/*--------------------------------------------------------------------------
                             A M O R S T A T U S

 The implementation file for the amor status display facilitator module. The 
 reflectometer AMOR needs some advanced feautures for its status display.
 These needs are taken care of here.

 copyright: see copyright.h

 Mark Koennecke, September 1999

 As AMOR's histogram memory becomes too big in tof mode to transfer it
 for status information the collapse and subsample functionalities have 
 been moved to the histogram memory. This code had to be modified to
 call SINQHMProject directly.

 Mark Koennecke, August 2001

 An additional projection mode: onto the y -tof plane was added

 Mark Koennecke, June 2005

 Support for new HTTP HM added
 
 Mark Koennecke, July 2006
 
 Lightly ammended for the new second generation HM module.
 
 Mark koennecke, May 2009
 --------------------------------------------------------------------------*/
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <tcl.h>
#include "fortify.h"
#include "sics.h"
#include "counter.h"
#include "stringdict.h"
#include "HistMem.h"
#include "HistMem.i"
#include "HistDriv.i"
#include "hardsup/sinqhm.h"
#include "sinqhmdriv.i"
#include "scan.h"
#include "lld.h"
#include "amorstat.i"
#include "amorstat.h"
#include "sicshipadaba.h"
/*-------------------------------------------------------------------
 Manually from SinqHM_def.h
 --------------------------------------------------------------------*/
#define PROJECT__FRAME  0x0005
#define PROJECT__AMOR   0x0006


/*-------------------------------------------------------------------------
   A static which determines if we are in TOF or scan mode. 
*/
static int iTOF = 0;
static pHistMem pHMHM = NULL;
/*-------------------------------------------------------------------------*/
static int HMCountStartCallback(int iEvent, void *pEvent, void *pUser)
{
  SConnection *pCon = (SConnection *) pUser;
  const float *fTime = NULL;
  int *iTime = NULL;
  int iLength, iRet, i;
  pHdb tbin = NULL;
  
  /* check kill condition */
  if (pCon == NULL || !SCisConnected(pCon)) {
    return -1;
  }

  if (iEvent == COUNTSTART) {
    /* send current time binning */
    iTOF = 1;
    if(pHMHM->pDes->parNode != NULL){
    	tbin = GetHipadabaNode(pHMHM->pDes->parNode,"time_binning");
    	assert(tbin != NULL);
    	iLength = tbin->value.arrayLength;
        iTime = (int *) malloc((iLength + 1) * sizeof(int));
        if ((!iTime)) {
          return 0;
        }
        iTime[0] = htonl(iLength);
        for (i = 0; i < iLength; i++) {
          iTime[i + 1] = htonl( (int) ( (tbin->value.v.floatArray[i]) / 10.) * 65536.) ;
        }
    } else {
    	fTime = GetHistTimeBin(pHMHM, &iLength);
        iTime = (int *) malloc((iLength + 1) * sizeof(int));
        if ((!fTime) || (!iTime)) {
          return 0;
        }
        iTime[0] = htonl(iLength);
        for (i = 0; i < iLength; i++) {
          iTime[i + 1] = htonl((int) ((fTime[i] / 10.) * 65536.));
        }
    }
    /* send new time binning to all clients */
    SCWrite(pCon, "TOFClear", eError);
    SCWriteUUencoded(pCon, "arrowaxis_time", iTime,
                     (iLength + 1) * sizeof(int));
    free(iTime);
  }
  return 1;
}

/*-------------------------------------------------------------------------*/
static int ScanStartCallback(int iEvent, void *pEvent, void *pUser)
{
  float *fAxis = NULL;
  int *iAxis = NULL;
  int iLength, iRet, i;
  char pBueffel[80], pName[40];
  SConnection *pCon = (SConnection *) pUser;
  pScanData pScan = (pScanData) pEvent;

  assert(pScan);

  /* check kill conditions */
  if (pCon == NULL || !SCisConnected(pCon)) {
    return -1;
  }

  if (iEvent == SCANSTART) {
    iTOF = 0;
    /* send current axis  */
    iLength = GetScanNP(pScan);
    fAxis = (float *) malloc((iLength + 1) * sizeof(float));
    iAxis = (int *) malloc((iLength + 1) * sizeof(int));
    if ((!fAxis) || (!iAxis)) {
      return 0;
    }
    iAxis[0] = htonl(iLength);
    GetSoftScanVar(pScan, 0, fAxis, iLength);
    GetScanVarName(pScan, 0, pName, 39);
    sprintf(pBueffel, "arrowaxis_%s", pName);
    for (i = 0; i < iLength; i++) {
      iAxis[i + 1] = htonl((int) (fAxis[i] * 65536.));
    }
    /* send new axis to client */
    SCWrite(pCon, "SCANClear", eError);
    SCWriteUUencoded(pCon, pBueffel, iAxis, (iLength + 1) * sizeof(int));
    free(iAxis);
    free(fAxis);
  }
  return 1;
}

/*------------------------------------------------------------------------*/
static int ScanPointCallback(int iEvent, void *pEvent, void *pUser)
{
  long *lData = NULL;
  int *iData = NULL;
  int iLength, iRet, i;
  SConnection *pCon = (SConnection *) pUser;
  pScanData pScan = (pScanData) pEvent;

  assert(pScan);

  /* check kill conditions */
  if (pCon == NULL || !SCisConnected(pCon)) {
    return -1;
  }

  if ((iEvent == SCANPOINT) || (iEvent == SCANEND)) {
    /* send current data  */
    iTOF = 0;
    iLength = GetScanNP(pScan);
    lData = (long *) malloc((iLength + 1) * sizeof(long));
    iData = (int *) malloc((iLength + 1) * sizeof(int));
    if ((!lData) || (!iData)) {
      return 0;
    }
    iData[0] = htonl(iLength);
    GetScanCounts(pScan, lData, iLength);
    for (i = 0; i < iLength; i++) {
      iData[i + 1] = htonl((int) (lData[i]));
    }
    /* send counts to client */
    SCWriteUUencoded(pCon, "arrow_spinupup", iData,
                     (iLength + 1) * sizeof(int));
    /* send counts for other detector */
    GetScanMonitor(pScan, 2, lData, iLength);
    for (i = 0; i < iLength; i++) {
      iData[i + 1] = htonl((int) (lData[i]));
    }
    SCWriteUUencoded(pCon, "arrow_spinuplo", iData,
                     (iLength + 1) * sizeof(int));
    /* to do: check for polarization and send spinlo */
    free(iData);
    free(lData);
  }
  return 1;
}

/*------------------------------------------------------------------------*/
static int SendLoadedData(pAmorStat self, SConnection * pCon)
{
  int i, iRet, *iData = NULL;
  char pBueffel[80];
  UserData ud;

  SCWrite(pCon, "loaded_CLEAR", eValue);
  iRet = LLDnodePtr2First(self->iUserList);
  while (iRet != 0) {
    LLDnodeDataTo(self->iUserList, &ud);
    iData = (int *) malloc((ud.iNP * 2 + 1) * sizeof(int));
    if (!iData) {
      return 0;
    }
    iData[0] = htonl(ud.iNP);
    for (i = 0; i < ud.iNP; i++) {
      iData[i + 1] = htonl((int) (ud.fX[i] * 65536));
      iData[i + 1 + ud.iNP] = htonl((int) (ud.fY[i] * 65536));
    }
    sprintf(pBueffel, "loaded_%s", ud.name);
    SCWriteUUencoded(pCon, pBueffel, iData,
                     (ud.iNP * 2 + 1) * sizeof(int));
    iRet = LLDnodePtr2Next(self->iUserList);
  }
  return 0;
}

/*------------------------------------------------------------------------*/
static int LoadCallback(int iEvent, void *pEvent, void *pUser)
{
  pAmorStat pAS = NULL;
  SConnection *pCon = NULL;

  pCon = (SConnection *) pUser;

  /* check kill conditions */
  if (pCon == NULL || !SCisConnected(pCon)) {
    return -1;
  }

  if (iEvent == FILELOADED) {
    pAS = (pAmorStat) pEvent;
    assert(pAS);
    assert(pCon);
    SendLoadedData(pAS, pCon);
  }
  return 1;
}

/*-------------------------------------------------------------------------*/
static void ClearUserData(pAmorStat self)
{
  int iRet;
  UserData ud;

  iRet = LLDnodePtr2First(self->iUserList);
  while (iRet != 0) {
    LLDnodeDataTo(self->iUserList, &ud);
    if (ud.fX != NULL)
      free(ud.fX);
    if (ud.fY != NULL)
      free(ud.fY);
    if (ud.name != NULL)
      free(ud.name);
    iRet = LLDnodePtr2Next(self->iUserList);
  }
  LLDdelete(self->iUserList);
  self->iUserList = LLDcreate(sizeof(UserData));
}

/*----------------------------------------------------------------------*/
void KillAmorStatus(void *pData)
{
  pAmorStat self = (pAmorStat) pData;

  if (!self)
    return;

  if (self->iUserList >= 0) {
    ClearUserData(self);
    LLDdelete(self->iUserList);
  }
  if (self->pDes)
    DeleteDescriptor(self->pDes);
  if (self->pCall)
    DeleteCallBackInterface(self->pCall);
  free(self);
}

/*------------------------------------------------------------------*/
int AmorStatusFactory(SConnection * pCon, SicsInterp * pSics,
                      void *pData, int argc, char *argv[])
{
  pAmorStat pNew = NULL;
  CommandList *pCom = NULL;
  char pBueffel[256];
  int iRet;

  /* check number of arguments */
  if (argc < 4) {
    sprintf(pBueffel, "ERROR: insufficient number of arguments to %s",
            argv[0]);
    SCWrite(pCon, pBueffel, eError);
    return 0;
  }

  /* allocate a new data structure */
  pNew = (pAmorStat) malloc(sizeof(AmorStat));
  if (!pNew) {
    sprintf(pBueffel, "ERROR: out of memory in %s", argv[0]);
    SCWrite(pCon, pBueffel, eError);
    return 0;
  }
  memset(pNew, 0, sizeof(AmorStat));
  pNew->pDes = CreateDescriptor("AmorStatus");
  pNew->iUserList = LLDcreate(sizeof(UserData));
  pNew->pCall = CreateCallBackInterface();
  if ((!pNew->pDes) || (pNew->iUserList < 0) || (!pNew->pCall)) {
    sprintf(pBueffel, "ERROR: out of memory in %s", argv[0]);
    SCWrite(pCon, pBueffel, eError);
    KillAmorStatus(pNew);
    return 0;
  }

  /*  to locate the HM and the scan object */
  pCom = FindCommand(pSics, argv[2]);
  if (pCom) {
    if (pCom->pData) {
      if (!iHasType(pCom->pData, "ScanObject")) {
        sprintf(pBueffel, "ERROR: %s is NO scan object", argv[2]);
        SCWrite(pCon, pBueffel, eError);
        KillAmorStatus(pNew);
        return 0;
      }
    } else {
      sprintf(pBueffel, "ERROR: %s is NO scan object", argv[2]);
      SCWrite(pCon, pBueffel, eError);
      KillAmorStatus(pNew);
      return 0;
    }
  } else {
    sprintf(pBueffel, "ERROR: %s NOT found", argv[2]);
    SCWrite(pCon, pBueffel, eError);
    KillAmorStatus(pNew);
    return 0;
  }
  pNew->pScan = (pScanData) pCom->pData;
  pCom = FindCommand(pSics, argv[3]);
  if (pCom) {
    if (pCom->pData) {
      if (!iHasType(pCom->pData, "HistMem") && !iHasType(pCom->pData,"HistMemSec")) {
        sprintf(pBueffel, "ERROR: %s is NO histogram memory object",
                argv[3]);
        SCWrite(pCon, pBueffel, eError);
        KillAmorStatus(pNew);
        return 0;
      }
    } else {
      sprintf(pBueffel, "ERROR: %s is NO histogram memory  object",
              argv[3]);
      SCWrite(pCon, pBueffel, eError);
      KillAmorStatus(pNew);
      return 0;
    }
  } else {
    sprintf(pBueffel, "ERROR: %s NOT found", argv[3]);
    SCWrite(pCon, pBueffel, eError);
    KillAmorStatus(pNew);
    return 0;
  }
  pNew->pHM = (pHistMem) pCom->pData;
  pHMHM = (pHistMem) pCom->pData;

  /* install command */
  iRet = AddCommand(pSics, argv[1],
                    AmorStatusAction, KillAmorStatus, pNew);
  if (!iRet) {
    sprintf(pBueffel, "ERROR: duplicate command %s NOT created", argv[1]);
    SCWrite(pCon, pBueffel, eError);
    KillAmorStatus(pNew);
    return 0;
  }


  return 1;
}

/*------------------------------------------------------------------*/
static int RegisterInterest(pAmorStat self, SConnection * pCon)
{
  long lID;
  pDummy pDum = NULL;
  pICallBack pCall = NULL;

  assert(self);
  assert(pCon);

  /* Register all the callbacks. Dependent on the state of 
     iTOF invoke the apropriate callbacks in order to force
     an initial update.
   */
  /* file load callback */
  lID = RegisterCallback(self->pCall, FILELOADED, LoadCallback,
                         SCCopyConnection(pCon), SCDeleteConnection);
  SendLoadedData(self, pCon);

  /* scan object */
  pDum = (pDummy) self->pScan;
  pCall = pDum->pDescriptor->GetInterface(pDum, CALLBACKINTERFACE);
  if (pCall) {
    lID = RegisterCallback(pCall, SCANSTART, ScanStartCallback,
                           SCCopyConnection(pCon), SCDeleteConnection);
    lID = RegisterCallback(pCall, SCANPOINT, ScanPointCallback,
                           SCCopyConnection(pCon), SCDeleteConnection);
    lID = RegisterCallback(pCall, SCANEND, ScanPointCallback,
                           SCCopyConnection(pCon), SCDeleteConnection);
    if (iTOF == 0) {
      ScanStartCallback(SCANSTART, pDum, pCon);
      ScanPointCallback(SCANPOINT, pDum, pCon);
    }
  }
  /*
   * histmem
   */
  pDum = (pDummy) self->pHM;
  pCall = pDum->pDescriptor->GetInterface(pDum, CALLBACKINTERFACE);
  if (pCall) {
    lID = RegisterCallback(pCall, COUNTSTART, HMCountStartCallback,
                           SCCopyConnection(pCon), SCDeleteConnection);
    if (iTOF == 1) {
      HMCountStartCallback(COUNTSTART, pDum, pCon);
    }
  }
  return 1;
}

/*-----------------------------------------------------------------*/
static int FileLoad(pAmorStat self, SConnection * pCon,
                    char *name, double dScale)
{
  char pBueffel[256], pDummy[50];
  FILE *fd = NULL;
  UserData ud;
  int iNP, i;
  float fDummy;

  /* open the file */
  fd = fopen(name, "r");
  if (!fd) {
    sprintf(pBueffel, "ERROR: cannot open %s for reading", name);
    SCWrite(pCon, pBueffel, eError);
    return 0;
  }

  /* skip first line */
  if (fgets(pBueffel, 255, fd) == NULL) {
    SCWrite(pCon, "ERROR: premature end of file", eError);
    fclose(fd);
    return 0;
  }

  /* read number of points in second line */
  if (fgets(pBueffel, 255, fd) == NULL) {
    SCWrite(pCon, "ERROR: premature end of file", eError);
    fclose(fd);
    return 0;
  }
  sscanf(pBueffel, "%s %d", pDummy, &iNP);
  /* allocate data */
  ud.iNP = iNP;
  ud.fX = (float *) malloc(iNP * sizeof(float));
  ud.fY = (float *) malloc(iNP * sizeof(float));
  ud.name = strdup(name);

  /* skip two lines */
  if (fgets(pBueffel, 255, fd) == NULL) {
    SCWrite(pCon, "ERROR: premature end of file", eError);
    fclose(fd);
    return 0;
  }
  if (fgets(pBueffel, 255, fd) == NULL) {
    SCWrite(pCon, "ERROR: premature end of file", eError);
    fclose(fd);
    return 0;
  }

  /* loop reading data */
  for (i = 0; i < iNP; i++) {
    if (fgets(pBueffel, 255, fd) == NULL) {
      SCWrite(pCon, "WARNING: premature end of file", eError);
      break;
    }
    sscanf(pBueffel, " %f %f %f", &ud.fX[i], &fDummy, &ud.fY[i]);
    ud.fY[i] *= dScale;
  }
  fclose(fd);

  /* enter ud into list */
  LLDnodeInsertFrom(self->iUserList, &ud);

  return 1;
}

/*-----------------------------------------------------------------
   Collapse creates a 2D image from the detector by summing all time
   channels together in any given detector.
*/

static int Collapse(pAmorStat self, SConnection * pCon)
{
  HistInt *lData = NULL;
  int i, i2, i3, iDim[MAXDIM], iIdx, iSum, status, length;
  int *iImage = NULL, *iPtr;
  pSINQHM pHist;
  SinqHMDriv *pTata;
  int iMax = -999999;
  char hmCommand[256];
  HistInt *data = NULL;

  /* get size of our problem */
  GetHistDim(self->pHM, iDim, &i3);
  /* assert(i3 == 3); */

  /* allocate some data */
  length = 2 + iDim[0] * iDim[1];
  iImage = (int *) malloc(length * sizeof(int));
  if (iImage == NULL) {
    SCWrite(pCon, "ERROR: failed to allocate memory in Collapse", eError);
    return 0;
  }
  memset(iImage, 0, (2 + iDim[0] * iDim[1]) * sizeof(int));

  /* first two numbers are the dimension of the image */
  iImage[0] = htonl(iDim[0]);
  iImage[1] = htonl(iDim[1]);


  if (isSINQHMDriv(self->pHM->pDriv)) {
    /*
       send a Project request to the histogram memory
     */
    pTata = (SinqHMDriv *) self->pHM->pDriv->pPriv;
    pHist = (pSINQHM) pTata->pMaster;
    /*
       The 3 in the following call has to be identical to
       PROJECT__COLL in sinqhm_def.h
     */
    status = SINQHMProject(pHist, 3, 0, iDim[0],
                           0, iDim[1], iImage + 2,
                           (length - 2) * sizeof(int));
    /*
       Byte swapping
     */
    for (i = 2; i < length; i++) {
      /*
         if(iImage[i] > iMax){
         iMax = iImage[i];
         }
       */
      iImage[i] = htonl(iImage[i]);
    }
    /*
       printf("Collapsed maximum: %d\n",iMax);
     */
    if (status != 1) {
      SCWrite(pCon, "ERROR: histogram memory refused to Collapse", eError);
      return 0;
    }
  } else if (self->iHTTP == 1) {
    if (i3 > 2) {
      snprintf(hmCommand, 255, "sum:2:0:%d", iDim[2]);
      if (self->pHM->pDriv->SubSample != NULL) {
        data =
            self->pHM->pDriv->SubSample(self->pHM->pDriv, pCon, 0,
                                        hmCommand);
      } else {
        data = NULL;
      }
      if (data == NULL) {
        SCWrite(pCon, "ERROR: failed to retrieve collapsed data from HM",
                eError);
        return 0;
      }
      for (i = 2; i < length; i++) {
        iImage[i] = htonl(data[i - 1]);
      }
      free(data);
    } else {
      GetHistogramDirect(self->pHM, pCon, 0, 0, length - 2,
                         &iImage[2], length * sizeof(HistInt));
      for (i = 2; i < length; i++) {
        iImage[i] = htonl(iImage[i]);
      }
    }
  } else {
    /*
       we are in simulation and just create some random numbers
     */
    for (i = 0; i < iDim[0]; i++) {
      for (i2 = 0; i2 < iDim[1]; i2++) {
        iIdx = i * iDim[1] + i2;
        iImage[iIdx + 2] = htonl(random());
        /* iImage[iIdx+2] = htonl(77); */
      }
    }
  }

  /* send image */
  SCWriteUUencoded(pCon, "arrow_image", iImage,
                   ((iDim[0] * iDim[1]) + 2) * sizeof(int));
  free(iImage);
  return 1;
}

/*-----------------------------------------------------------------
   projectYTOF creates a 2D image from the detector by summing all x channels 
   together onto the y - tof plane
*/

static int projectYTOF(pAmorStat self, SConnection * pCon)
{
  HistInt *lData = NULL;
  int i, i2, i3, iDim[MAXDIM], iIdx, iSum, status, length;
  int *iImage = NULL, *iPtr;
  pSINQHM pHist;
  SinqHMDriv *pTata;
  int iMax = -999999;
  char hmCommand[256];
  HistInt *data = NULL;

  /* get size of our problem */
  GetHistDim(self->pHM, iDim, &i3);
  if (i3 < 3 || iDim[2] < 2) {
    SCWrite(pCon, "ERROR: cannot project on Y - TOF, not in TOF mode",
            eError);
    return 0;
  }

  /* allocate some data */
  length = 2 + iDim[1] * iDim[2];
  iImage = (int *) malloc(length * sizeof(int));
  if (iImage == NULL) {
    SCWrite(pCon, "ERROR: failed to allocate memory in projectYTOF",
            eError);
    return 0;
  }
  memset(iImage, 0, (2 + iDim[1] * iDim[2]) * sizeof(int));

  /* first two numbers are the dimensions of the image */
  iImage[0] = htonl(iDim[1]);
  iImage[1] = htonl(iDim[2]);


  if (isSINQHMDriv(self->pHM->pDriv)) {
    /*
       send a Project request to the histogram memory
     */
    pTata = (SinqHMDriv *) self->pHM->pDriv->pPriv;
    pHist = (pSINQHM) pTata->pMaster;
    status = SINQHMProject(pHist, PROJECT__AMOR, 0, 0,
                           0, 0, iImage + 2, (length - 2) * sizeof(int));

    for (i = 2; i < length; i++) {
      iImage[i] = htonl(iImage[i]);
    }
    if (status != 1) {
      SCWrite(pCon, "ERROR: histogram memory refused to project", eError);
      return 0;
    }
  } else if (self->iHTTP == 1) {
    snprintf(hmCommand, 255, "sum:0:0:%d", iDim[0]);
    data =
        self->pHM->pDriv->SubSample(self->pHM->pDriv, pCon, 0, hmCommand);
    if (data == NULL) {
      SCWrite(pCon, "ERROR: failed to retrieve Y-projection from HM",
              eError);
      return 0;
    }
    for (i = 2; i < length; i++) {
      iImage[i] = htonl(data[i - 1]);
    }
    free(data);
  } else {
    /*
       we are in simulation and just create some random numbers
     */
    for (i = 0; i < iDim[1]; i++) {
      for (i2 = 0; i2 < iDim[2]; i2++) {
        iIdx = i * iDim[2] + i2;
        iImage[iIdx + 2] = htonl(random());
        iImage[iIdx + 2] = htonl(77);
      }
    }
  }

  /* send image */
  SCWriteUUencoded(pCon, "y_tof_projection", iImage,
                   ((iDim[1] * iDim[2]) + 2) * sizeof(int));
  free(iImage);
  return 1;
}

/*-----------------------------------------------------------------
  SendSingleTOF sends single detector data for TOF mode
--------------------------------------------------------------------*/

static int SendSingleTOF(pAmorStat self, SConnection * pCon)
{
  HistInt *lData = NULL;
  int i, i2, i3, iDim[MAXDIM], iIdx, iSum, status, length, nTime;
  pSINQHM pHist;
  SinqHMDriv *pTata;
  int iMax = -999999;
  const float *timebin;
  HistInt *iData = NULL;
  int iStart;

  /* get size of our problem */
  GetHistDim(self->pHM, iDim, &i3);

  /* allocate some data */
  timebin = GetHistTimeBin(self->pHM, &nTime);
  if (nTime < 2) {
    return 1;
  }

  length = 1 + 2 * nTime;
  iData = (HistInt *) malloc(length * sizeof(HistInt));
  if (iData == NULL) {
    SCWrite(pCon, "ERROR: failed to allocate memory in SendSingleTOF",
            eError);
    return 0;
  }
  memset(iData, 0, length * sizeof(int));

  /* first number is the length of each single histogram */
  iData[0] = htonl(nTime);


  if (isSINQHMDriv(self->pHM->pDriv)) {
    iStart = iDim[0] * iDim[1] * nTime;
    GetHistogramDirect(self->pHM, pCon, 0, iStart,
                       iStart + 2 * nTime, &iData[1],
                       2 * nTime * sizeof(HistInt));
    for (i = 1; i < length; i++) {
      iData[i] = htonl(iData[i]);
    }
  } else if (self->iHTTP == 1) {
    GetHistogramDirect(self->pHM, pCon, 1, 0, 2 * nTime, &iData[1],
                       2 * nTime * sizeof(HistInt));
    for (i = 1; i < length; i++) {
      iData[i] = htonl(iData[i]);
    }
  } else {
    /*
       we are in simulation and just create some random numbers
     */
    for (i = 1; i < length; i++) {
      iData[i] = htonl(random());
    }
  }

  /* 
     send, with a little trick to do two histograms.  
   */
  SCWriteUUencoded(pCon, "SING1", iData, (nTime + 1) * sizeof(int));
  iData[nTime] = htonl(nTime);
  SCWriteUUencoded(pCon, "SING2", &iData[nTime],
                   (nTime + 1) * sizeof(int));
  free(iData);
  return 1;
}

/*-------------------------------------------------------------------
    SubSample sums histogram data in the area defined by the rectangle
    x1,y1 x2, y2. Summing is along the time axis.
----------------------------------------------------------------------*/
static int SubSample(pAmorStat self, SConnection * pCon,
                     char *name, int x1, int x2, int y1, int y2)
{
  int iDim[MAXDIM], i, i2, i3, *iSum = NULL, iLang, *iPtr;
  HistInt *lData = NULL;
  int iLimit, status, nTime;
  char pBueffel[132];
  pSINQHM pHist;
  SinqHMDriv *pTata;
  const float *fTime;
  char hmCommand[256];
  HistInt *data = NULL;

  /* get histogram dimensions */
  GetHistDim(self->pHM, iDim, &i3);
  fTime = GetHistTimeBin(self->pHM, &nTime);
  iDim[i3] = nTime;
  i3++;

  /* check limits */
  if (x2 < x1) {
    i = x1;
    x1 = x2;
    x2 = i + 1;
  }
  if (y2 < y1) {
    i = y1;
    y1 = y2;
    y2 = i + 1;
  }

  iLimit = 0;
  if (x1 > iDim[0]) {
    iLimit = 1;
    x1 = iDim[0] - 1;
  }
  if (x1 < 0) {
    iLimit = 1;
    x1 = 0;
  }
  if (x2 > iDim[0]) {
    iLimit = 2;
    x2 = iDim[0] - 1;
  }
  if (x2 < 0) {
    iLimit = 2;
    x2 = 0;
  }
  if (y1 > iDim[1]) {
    iLimit = 3;
    y1 = iDim[1] - 1;
  }
  if (y1 < 0) {
    iLimit = 3;
    y1 = 0;
  }
  if (y2 > iDim[1]) {
    iLimit = 4;
    y2 = iDim[1] - 1;
  }
  if (y2 < 0) {
    iLimit = 4;
    y2 = 0;
  }
  if (iLimit != 0) {
    switch (iLimit) {
    case 1:
      strcpy(pBueffel, "WARNING: limit violation on x1");
      break;
    case 2:
      strcpy(pBueffel, "WARNING: limit violation on x2");
      break;
    case 3:
      strcpy(pBueffel, "WARNING: limit violation on y1");
      break;
    case 4:
      strcpy(pBueffel, "WARNING: limit violation on y2");
      break;
    }
    SCWrite(pCon, pBueffel, eWarning);
  }

  /* allocate space for result */
  iSum = (int *) malloc((iDim[2] + 1) * sizeof(int));
  if (!iSum) {
    SCWrite(pCon, "ERROR: out of memory in SubSample", eError);
    return 0;
  }
  memset(iSum, 0, (iDim[2] + 1) * sizeof(int));

  iSum[0] = htonl(iDim[2]);
  if (isSINQHMDriv(self->pHM->pDriv)) {
    /*
       send project message to histogram memory
     */
    pTata = (SinqHMDriv *) self->pHM->pDriv->pPriv;
    pHist = (pSINQHM) pTata->pMaster;
    status = SINQHMProject(pHist, 4, x1, x2 - x1,
                           y1, y2 - y1, iSum + 1, iDim[2] * sizeof(int));
    /*
       convert to network byte order 
     */
    for (i = 1; i < iDim[2] + 1; i++) {
      iSum[i] = htonl(iSum[i]);
    }
    if (status != 1) {
      SCWrite(pCon, "ERROR: histogram memory refused to SubSample",
              eError);
      return 0;
    }
  } else if (self->iHTTP == 1) {
    snprintf(hmCommand, 255,
             "sample:%d:%d:%d:%d:0:%d;sum:0:0:%d;sum:0:0:%d", x1, x2, y1,
             y2, iDim[2] - 1, x2 - x1, y2 - y1);
    data =
        self->pHM->pDriv->SubSample(self->pHM->pDriv, pCon, 0, hmCommand);
    if (data == NULL) {
      SCWrite(pCon, "ERROR: failed to retrieve sub sampled data fromHM",
              eError);
      return 0;
    }
    for (i = 1; i < iDim[2] + 1; i++) {
      iSum[i] = htonl(data[i - 1]);
    }
    free(data);
  } else {
    /* do  acouple of random numbers! */
    for (i = 1; i < iDim[2] + 1; i++) {
      iSum[i] = htonl(random());
    }
  }

  /* send */
  sprintf(pBueffel, "arrowsum_%s", name);
  SCWriteUUencoded(pCon, pBueffel, iSum, (iDim[2] + 1) * sizeof(int));

  free(iSum);
  return 1;
}

/*------------------------------------------------------------------*/
int AmorStatusAction(SConnection * pCon, SicsInterp * pSics,
                     void *pData, int argc, char *argv[])
{
  pAmorStat self = (pAmorStat) pData;
  char pBueffel[512];
  double dScale;
  int iRet;
  int x1, x2, y1, y2;

  assert(self);

  if (argc < 2) {
    sprintf(pBueffel, "ERROR: need argument to %s", argv[0]);
    SCWrite(pCon, pBueffel, eError);
    return 0;
  }

  strtolower(argv[1]);
  if (strcmp(argv[1], "interest") == 0) {
    RegisterInterest(self, pCon);
    SCSendOK(pCon);
    return 1;
  } else if (strcmp(argv[1], "load") == 0) {
    if (argc < 4) {
      sprintf(pBueffel,
              "ERROR: need filename and scale argument to %s load",
              argv[0]);
      SCWrite(pCon, pBueffel, eError);
      return 0;
    }
    iRet = Tcl_GetDouble(pSics->pTcl, argv[3], &dScale);
    if (iRet != TCL_OK) {
      sprintf(pBueffel, "ERROR: cannot convert %s to scale factor",
              argv[3]);
      SCWrite(pCon, pBueffel, eError);
      return 0;
    }
    FileLoad(self, pCon, argv[2], dScale);
    InvokeCallBack(self->pCall, FILELOADED, self);
    SCSendOK(pCon);
  } else if (strcmp(argv[1], "collapse") == 0) {
    iRet = Collapse(self, pCon);
    if (iRet) {
      SCSendOK(pCon);
    }
    return iRet;
  } else if (strcmp(argv[1], "projectytof") == 0) {
    iRet = projectYTOF(self, pCon);
    if (iRet) {
      SCSendOK(pCon);
    }
    return iRet;
  } else if (strcmp(argv[1], "http") == 0) {
    if (argc > 2) {
      if (!SCMatchRights(pCon, usMugger)) {
        return 0;
      }
      self->iHTTP = atoi(argv[2]);
      SCSendOK(pCon);
      return 1;
    } else {
      snprintf(pBueffel, 511, "amorstat.http = %d", self->iHTTP);
      SCWrite(pCon, pBueffel, eValue);
      return 1;
    }
  } else if (strcmp(argv[1], "sample") == 0) {
    if (argc < 7) {
      SCWrite(pCon, "ERROR: insufficent number of arguments to sample",
              eError);
      return 0;
    }
    iRet = Tcl_GetInt(pSics->pTcl, argv[3], &x1);
    if (iRet != TCL_OK) {
      sprintf(pBueffel, "ERROR: cannot convert %s to int", argv[3]);
      SCWrite(pCon, pBueffel, eError);
      return 0;
    }
    iRet = Tcl_GetInt(pSics->pTcl, argv[6], &y2);
    if (iRet != TCL_OK) {
      sprintf(pBueffel, "ERROR: cannot convert %s to int", argv[6]);
      SCWrite(pCon, pBueffel, eError);
      return 0;
    }
    iRet = Tcl_GetInt(pSics->pTcl, argv[4], &x2);
    if (iRet != TCL_OK) {
      sprintf(pBueffel, "ERROR: cannot convert %s to int", argv[4]);
      SCWrite(pCon, pBueffel, eError);
      return 0;
    }
    iRet = Tcl_GetInt(pSics->pTcl, argv[5], &y1);
    if (iRet != TCL_OK) {
      sprintf(pBueffel, "ERROR: cannot convert %s to int", argv[5]);
      SCWrite(pCon, pBueffel, eError);
      return 0;
    }
    iRet = SubSample(self, pCon, argv[2], x1, x2, y1, y2);
    if (iRet)
      SCSendOK(pCon);
    return iRet;
  } else if (strcmp(argv[1], "singletof") == 0) {
    return SendSingleTOF(self, pCon);
  } else if (strcmp(argv[1], "sendloaded") == 0) {
    SendLoadedData(self, pCon);
    return 1;
  } else if (strcmp(argv[1], "clear") == 0) {
    ClearUserData(self);
    InvokeCallBack(self->pCall, FILELOADED, self);
    SCSendOK(pCon);
  } else if (strcmp(argv[1], "tofmode") == 0) {
    HMCountStartCallback(COUNTSTART, NULL, pCon);
    return 1;
  } else {
    sprintf(pBueffel, "ERROR: %s nor recognized as subcommand to %s",
            argv[1], argv[2]);
    SCWrite(pCon, pBueffel, eError);
    return 0;
  }
  return 1;
}
