/*---------------------------------------------------------------------------
                     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 "counter.h"
#include "HistMem.h"
#include "fomerge.h"
#include "faverage.h"

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

/*------------------------------------------------------------------------*/
  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;      

      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)
      {
         sprintf(pBueffel,"ERROR: cannot convert %d to integer",argv[1]);
         SCWrite(pCon,pBueffel,eError);
         return 0;
      }
      iRet = Tcl_GetInt(pSics->pTcl,argv[2],&iEnd);
      if(iRet != TCL_OK)
      {
         sprintf(pBueffel,"ERROR: cannot convert %d 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)
        {
           sprintf(pBueffel,"ERROR: cannot convert %d 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 */
      fTimeBin = GetHistTimeBin(self->pHist,&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 = GetHistogramPointer(self->pHist,pCon);
      if(hiData == NULL)
      {
         SCWrite(pCon,"ERROR: failed to read histogram memory data",eError);
         free(iData);
         return 0;
      }      
      setFMDataPointer(hiData, iLength);
      hiData = getFMBankPointer(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);
	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;      

      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 %d to integer",argv[1]);
         SCWrite(pCon,pBueffel,eError);
         return 0;
      }

      timeBin = GetHistTimeBin(self->pHist,&iLength);
      assert(timeBin);      
      hiData = GetHistogramPointer(self->pHist,pCon);
      if(hiData == NULL)
      {
         SCWrite(pCon,"ERROR: failed to read histogram memory data",eError);
         return 0;
      }      
      setFMDataPointer(hiData, iLength);
      hiData = getFMBankPointer(iBank);
      if(hiData == NULL)
      {
	SCWrite(pCon,"ERROR: BAD Configuration",eError);
	return 0;
      }

      /* 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;
     
     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)
     {
        sprintf(pBueffel,"ERROR: histogram memory %s NOT found!", argv[2]);
        SCWrite(pCon,pBueffel,eError);
        return 0;
     }   
     pDum = (pDummy)pCom->pData;
     if(!pDum)
     {
        sprintf(pBueffel,"ERROR: histogram memory %s INVALID!", argv[2]);
        SCWrite(pCon,pBueffel,eError);
        return 0;
     }
     if(strcmp(pDum->pDescriptor->name,"HistMem") != 0)
     {
        sprintf(pBueffel,"ERROR: %s is NO histogram memory 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;
     }
     pNew->pHist = (pHistMem)pDum;
     
     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;
  }   
