/*---------------------------------------------------------------------------
                     F o c u s A v e r a g e r
   
  A little averager for FOCUS data. Used by the FOCUS status display.
  
  copyright: see copyright.h
  
  Mark Koennecke, October 1998

  Updated for additional detector banks

  Mark Koennecke, March 2000

  Added focusraw command for retrieving single detector banks in support
  of the colour mapping part of the FOCUS status display.

  Mark Koennecke, July 2001  
---------------------------------------------------------------------------*/
#include <stdlib.h>
#include <assert.h>
#include <tcl.h>
#include <time.h>
#include "fortify.h"
#include "sics.h"
#include "sicsvar.h"
#include "counter.h"
#include "HistMem.h"
#include "fomerge.h"
#include "faverage.h"

/*
#define DEB 1
*/
/*-------------------------------------------------------------------------*/
typedef struct __FocusAverager {
  pObjectDescriptor pDes;
  pHistMem pHistogram1;
  pHistMem pHistogram2;
  pHistMem pHistogram3;
} FocusAverager, *pFocusAverager;

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

HistInt *CheckBank(pFocusAverager self, SConnection * pCon,
                   int iLength, int iBank);

static void KillFA(void *pData)
{
  pFocusAverager self = NULL;

  self = (pFocusAverager) pData;
  if (!self)
    return;

  if (self->pDes)
    DeleteDescriptor(self->pDes);
  free(self);
}

/*-------------------------------------------------------------------------*/
int FocusAverageDo(SConnection * pCon, SicsInterp * pSics, void *pData,
                   int argc, char *argv[])
{
  pFocusAverager self = NULL;
  int *iData = NULL;
  const float *fTimeBin = NULL;
  float fVal;
  int iLength, iStart, iEnd, iNum, i, ii, iTest, iBufLen, iRet, iVal;
  char pBueffel[256];
  HistInt *hiData = NULL, *hiPtr;
  time_t tStart, tEnd;
  int iBank = MIDDLE;
  pSicsVariable var1 = NULL;
  pSicsVariable var2 = NULL;
  pSicsVariable var3 = NULL;
  int lbank=0, mbank=0, ubank=0;

  self = (pFocusAverager) pData;
  assert(self);
  assert(pCon);
  assert(pSics);

  /* we need two parameters: start and end of averaging */
  if (argc < 3) {
    SCWrite(pCon,
            "ERROR: insufficient number of parameters for FocusAverage",
            eError);
    return 0;
  }
  iRet = Tcl_GetInt(pSics->pTcl, argv[1], &iStart);
  if (iRet != TCL_OK) {
    snprintf(pBueffel,255, "ERROR: cannot convert %s to integer", argv[1]);
    SCWrite(pCon, pBueffel, eError);
    return 0;
  }
  iRet = Tcl_GetInt(pSics->pTcl, argv[2], &iEnd);
  if (iRet != TCL_OK) {
    snprintf(pBueffel,255, "ERROR: cannot convert %s to integer", argv[2]);
    SCWrite(pCon, pBueffel, eError);
    return 0;
  }

  /* another parameter, if available describes the detector bank
   */
  if (argc > 3) {
    iRet = Tcl_GetInt(pSics->pTcl, argv[3], &iBank);
    if (iRet != TCL_OK) {
      snprintf(pBueffel,255, "ERROR: cannot convert %s to integer", argv[3]);
      SCWrite(pCon, pBueffel, eError);
      return 0;
    }
  }

  /* how much to do: correct parameters? */
  iNum = iEnd - iStart;
  if (iNum < 0) {
    SCWrite(pCon, "ERROR: invalid parameters given to FocusAverage",
            eError);
    return 0;
  }
  /* may be only one histogram requested */
  if (iNum == 0)
    iNum = 1;
  if (iStart < 0) {
    SCWrite(pCon, "ERROR: invalid parameters given to FocusAverage",
            eError);
    return 0;
  }
#ifdef DEB
  printf("Starting averaging ...\n");
  fflush(stdout);
  tStart = time(NULL);
#endif

  /* do work! first retrieve time binning data */

  var2 = FindVariable(pServ->pSics, "mbank");
  if (var2) {
    VarGetInt(var2, &mbank);
  } else {
    SCWrite(pCon, "ERROR: mbank value not found!", eError);
  }
  if (mbank == 1) {
    fTimeBin = GetHistTimeBin(self->pHistogram2, &iLength);
  } else {
    var1 = FindVariable(pServ->pSics, "lbank");
    if (var1) {
      VarGetInt(var1, &lbank);
    } else {
      SCWrite(pCon, "ERROR: lbank value not found!", eError);
    }
    if (lbank == 1) {
      fTimeBin = GetHistTimeBin(self->pHistogram1, &iLength);
    } else {
      fTimeBin = GetHistTimeBin(self->pHistogram3, &iLength);
    }
  }
  assert(fTimeBin);
  if (iLength <= 0) {
    SCWrite(pCon, "ERROR: histogram memory inproperly configured", eError);
    return 0;
  }
  /* allocate result data */
  iBufLen = (iLength * 2 + 1) * sizeof(int);
  iData = (int *) malloc(iBufLen);
  memset(iData, 0, iBufLen);

  /* get histogram length */
  i = getFMdim(iBank);
  /* correct iEnd to maximum allowed */
  iTest = i;
  if (iEnd > iTest - 1) {
    iEnd = iTest - 1;
    iNum = iEnd - iStart;
    if (iNum <= 0)
      iNum = 1;
  }
#ifdef DEB
  printf("Getting histogram....\n");
  fflush(stdout);
#endif

  hiData = CheckBank(self, pCon, iLength, iBank);

#ifdef DEB
  tEnd = time(NULL);
  printf("Histogram received in %d seconds\n", tStart - tEnd);
  fflush(stdout);
#endif

  if (hiData == NULL) {
    SCWrite(pCon, "ERROR: BAD Configuration", eError);
    free(iData);
    return 0;
  }

  /* first int: length of things to come */
  iData[0] = htonl(iLength);
  /* sum up */
  for (i = iStart; i < iEnd; i++) {
    hiPtr = hiData + i * iLength;
    for (ii = 0; ii < iLength; ii++) {
      iData[ii + 1] += hiPtr[ii];
    }
  }
  /* average */
  for (i = 1; i < iLength + 1; i++) {
    fVal = (float) iData[i] / (float) iNum;
    fVal *= 65536.;
    iData[i] = htonl((int) fVal);
  }
  /* make time binning fixed point */
  for (i = 0; i < iLength; i++) {
    fVal = fTimeBin[i] / 10.;
    fVal *= 65536.;
    iData[iLength + 1 + i] = htonl((int) fVal);
  }
#ifdef DEB
  printf("Sending averaged data....\n");
  fflush(stdout);
#endif
  /* finally send out uuencoded */
  SCWriteUUencoded(pCon, "FocusAverage", iData, iBufLen);
  if (iData)
    free(iData);
#ifdef DEB
  printf("Averaging finished\n");
  fflush(stdout);
#endif
  return 1;
}

/*-------------------------------------------------------------------------*/
static int FocusRaw(SConnection * pCon, SicsInterp * pSics, void *pData,
                    int argc, char *argv[])
{
  pFocusAverager self = NULL;
  int *iData = NULL;
  int iLength, noTimebin, iRet, i;
  char pBueffel[256];
  const float *timeBin;
  HistInt *hiData = NULL, *hiPtr;
  int iBank = MIDDLE;
  pSicsVariable var1 = NULL;
  pSicsVariable var2 = NULL;
  pSicsVariable var3 = NULL;
  int lbank, mbank, ubank;

  self = (pFocusAverager) pData;
  assert(self);
  assert(pCon);
  assert(pSics);


  /* we need one parameter, the bank to read */
  if (argc < 2) {
    SCWrite(pCon,
            "ERROR: insufficient number of parameters for FocusRaw",
            eError);
    return 0;
  }
  iRet = Tcl_GetInt(pSics->pTcl, argv[1], &iBank);
  if (iRet != TCL_OK) {
    sprintf(pBueffel, "ERROR: cannot convert %s to integer", argv[1]);
    SCWrite(pCon, pBueffel, eError);
    return 0;
  }
  var2 = FindVariable(pServ->pSics, "mbank");
  if (var2) {
    VarGetInt(var2, &mbank);
  } else {
    SCWrite(pCon, "ERROR: mbank value not found!", eError);
  }
  if (var2) {
    timeBin = GetHistTimeBin(self->pHistogram2, &iLength);
  } else {
    var1 = FindVariable(pServ->pSics, "lbank");
    if (var1) {
      VarGetInt(var1, &lbank);
    } else {
      SCWrite(pCon, "ERROR: lbank value not found!", eError);
    }
    if (var1) {
      timeBin = GetHistTimeBin(self->pHistogram1, &iLength);
    } else {
      timeBin = GetHistTimeBin(self->pHistogram3, &iLength);
    }
  }
  assert(timeBin);
  hiData = CheckBank(self, pCon, iLength, iBank);

  /* get histogram length */
  iLength = getFMdim(iBank);
  noTimebin = getFMdim(TIMEBIN);
  /* write dimension info */
  sprintf(pBueffel, "focusrawdim = %d = %d", iLength, noTimebin);
  SCWrite(pCon, pBueffel, eValue);

  /* allocate space */
  iData = (int *) malloc((iLength * noTimebin + 1) * sizeof(int));
  if (iData == NULL) {
    SCWrite(pCon, "ERROR: out of memory in FocusRaw", eError);
    return 0;
  }
  memset(iData, 0, noTimebin * iLength * sizeof(int));

  /* first int: length of things to come */
  iData[0] = htonl(iLength * noTimebin);
  /* network byte order for everything */
  for (i = 0; i < noTimebin * iLength; i++) {
    iData[i + 1] = htonl(hiData[i]);
  }
  /* send away, zipped */
  SCWriteZipped(pCon, "focusraw", iData,
                (iLength * noTimebin + 1) * sizeof(int));

  free(iData);
  return 1;
}

/*-------------------------------------------------------------------------*/
int MakeFA(SConnection * pCon, SicsInterp * pSics, void *pData,
           int argc, char *argv[])
{
  pFocusAverager pNew = NULL;
  CommandList *pCom = NULL;
  pDummy pDum = NULL;
  char pBueffel[256];
  int iRet;
  pSicsVariable var1 = NULL;
  pSicsVariable var2 = NULL;
  pSicsVariable var3 = NULL;
  int lbank=0, mbank=0, ubank=0;

  assert(pCon);
  assert(pSics);

  /* we need two parameters: the name for the averager and the histogram 
     memory
   */
  if (argc < 3) {
    SCWrite(pCon, "ERROR: Insufficient number of parameters to MakeFA",
            eError);
    return 0;
  }

  /* find histogram memory */
  pCom = FindCommand(pSics, argv[2]);
  if (!pCom) {
    snprintf(pBueffel,255, "ERROR: histogram memory %s NOT found!", argv[2]);
    SCWrite(pCon, pBueffel, eError);
    return 0;
  }
  pDum = (pDummy) pCom->pData;
  if (!pDum) {
    snprintf(pBueffel,255, "ERROR: histogram memory %s INVALID!", argv[2]);
    SCWrite(pCon, pBueffel, eError);
    return 0;
  }
  if (strcmp(pDum->pDescriptor->name, "HMcontrol") != 0) {
    snprintf(pBueffel,255, "ERROR: %s is NO histogram control object!",
            argv[2]);
    SCWrite(pCon, pBueffel, eError);
    return 0;
  }

  /* we got what we need: set things up */
  pNew = (pFocusAverager) malloc(sizeof(FocusAverager));
  if (!pNew) {
    SCWrite(pCon, "ERROR: out of memory in MakeFA", eError);
    return 0;
  }
  memset(pNew, 0, sizeof(FocusAverager));

  pNew->pDes = CreateDescriptor("FocusAverager");
  if (!pNew->pDes) {
    SCWrite(pCon, "ERROR: out of memory in MakeFA", eError);
    return 0;
  }
  var2 = FindVariable(pServ->pSics, "mbank");
  if (var2) {
    VarGetInt(var2, &mbank);
  } else {
    SCWrite(pCon, "ERROR: mbank value not found!", eError);
  }
  if (mbank == 1) {
    pNew->pHistogram2 = FindHM(pSics,"hm2");
  }
  var1 = FindVariable(pServ->pSics, "lbank");
  if (var1) {
    VarGetInt(var1, &lbank);
  } else {
    SCWrite(pCon, "ERROR: lbank value not found!", eError);
  }
  if (lbank == 1) {
    pNew->pHistogram1 = FindHM(pSics,"hm1");
  }
  var3 = FindVariable(pServ->pSics, "ubank");
  if (var3) {
    VarGetInt(var3, &ubank);
  } else {
    SCWrite(pCon, "ERROR: ubank value not found!", eError);
  }
  if (ubank == 1) {
    pNew->pHistogram3 = FindHM(pSics,"hm3");
  }
  iRet = AddCommand(pSics, argv[1], FocusAverageDo, KillFA, pNew);
  if (!iRet) {
    sprintf(pBueffel, "ERROR: duplicate command %s not created", argv[1]);
    SCWrite(pCon, pBueffel, eError);
    KillFA(pNew);
    return 0;
  }
  iRet = AddCommand(pSics, "focusraw", FocusRaw, NULL, pNew);
  if (!iRet) {
    sprintf(pBueffel, "ERROR: duplicate command focusraw not created");
    SCWrite(pCon, pBueffel, eError);
    return 0;
  }
  return 1;
}

/*----------------------------------------------------------------------*/
HistInt *CheckBank(pFocusAverager self, SConnection * pCon,
                   int iLength, int iBank)
{

  pSicsVariable var1 = NULL;
  pSicsVariable var2 = NULL;
  pSicsVariable var3 = NULL;
  HistInt *lData = NULL;
  HistInt *mData = NULL;
  HistInt *uData = NULL;
  HistInt *mergData = NULL;
  int lbank=0, mbank=0, ubank=0;

  if (iBank == 2) {
    var2 = FindVariable(pServ->pSics, "mbank");
    if (var2) {
      VarGetInt(var2, &mbank);
    } else {
      SCWrite(pCon, "ERROR: mbank value not found!", eError);
    }
    if (mbank == 1) {
      mData = GetHistogramPointer(self->pHistogram2, pCon);
      if (mData == NULL) {
        return NULL;
      }
      setFMDataPointer(mData, iLength, 2);
      mData = getFMBankPointer(2);
      return mData;
    }
  }
  if (iBank == 3) {
    var1 = FindVariable(pServ->pSics, "lbank");
    if (var1) {
      VarGetInt(var1, &lbank);
    } else {
      SCWrite(pCon, "ERROR: lbank value not found!", eError);
    }
    if (lbank == 1) {
      lData = GetHistogramPointer(self->pHistogram1, pCon);
      if (lData == NULL) {
        return NULL;
      }
      setFMDataPointer(lData, iLength, 3);
      lData = getFMBankPointer(3);
      return lData;
    }
  }
  if (iBank == 1) {
    var3 = FindVariable(pServ->pSics, "ubank");
    if (var3) {
      VarGetInt(var3, &ubank);
    } else {
      SCWrite(pCon, "ERROR: ubank value not found!", eError);
    }
    if (ubank == 1) {
      uData = GetHistogramPointer(self->pHistogram3, pCon);
      if (uData == NULL) {
        return NULL;
      }
      setFMDataPointer(uData, iLength, 1);
      uData = getFMBankPointer(1);
      return uData;
    }
  }
  if (iBank == 4) {
    setFMDataPointer(mergData, iLength, 4);
    mergData = getFMBankPointer(4);
    return mergData;
  }
  return NULL;
}
