/*---------------------------------------------------------------------------
                             F O W R I T E
                             
   FOCUS data writing object.
   
   copyright: see copyright.h
   
   Mark Koennecke, November 1998
   
   Added code for three detector banks.

   Mark Koennecke, March 2000
-----------------------------------------------------------------------------*/
#include <stdlib.h>
#include <assert.h>
#include <time.h>
#include <tcl.h>
/* avoid irritating compiler warning M.Z.08.2001 */
#undef VOID
#include "fortify.h"
#include "sics.h"
#include "event.h"
#include "counter.h"
#include "HistMem.h"
#include "nxdict.h"
#include "nxutil.h"
#include "motor.h"
#include "selector.h"
#include "fowrite.h"
#include "scan.h"
#include "sicsvar.h"
#include "fitcenter.h"
#include "fomerge.h"

/* the name of the SICS chopper controller object */
#define CHOPPERNAME "choco" 

/*--------- the internal data structure ------------------------------------*/
  typedef struct {
                   pObjectDescriptor pDes;
                   pHistMem pHist;
                   int iNew;
                   time_t tUpdate;
                    int iInterval;
                   int iEnd;
                   SConnection *pCon;
                   char *pFile;
                   char *pDictFile;
                   pFit pFitter;
                   float fElastic;
                   int iUpper, iMiddle, iLower; 
                   /* detector availability flags */
                  } FoWrite, *pFoWrite;
/* ------------------- forward declaration of task function --------------*/
  static int FoTask(void *pData);                  
  static void FoUpdate(pFoWrite self, SConnection *pCon);
/*------------------ The Countstart Callback Function ----------------------*/
   static int Countstartcallback(int iEvent, void *pEventData, void *pUser)
   {
      pFoWrite self = NULL;
      
      if(iEvent == COUNTSTART)
      {
         self = (pFoWrite)pUser;
         assert(self);
         self->iNew = 1;
         self->iEnd = 0;
         self->tUpdate = time(NULL);
         self->pCon = (SConnection *)pEventData;
         TaskRegister(pServ->pTasker,FoTask,NULL,NULL,self,1);
         return 1;
      }
      return 1;
   }               
/*------------------ The Countend Callback Function ----------------------*/
   static int Countendcallback(int iEvent, void *pEventData, void *pUser)
   {
      pFoWrite self = NULL;
      
      if(iEvent == COUNTEND)
      {
         self = (pFoWrite)pUser;
         assert(self);
         self->tUpdate = time(NULL);
         self->iEnd = 1;
         FoUpdate(self,self->pCon);
         return 1;
      }
      return 1;
   }               
/*-----------------------------------------------------------------------*/
  static void SNError(void *pData, char *text)
  {
     SConnection *pCon;
     
     assert(pData);
     pCon = (SConnection *)pData;
     SCWrite(pCon,text,eError);
  }   
/*------------------------------------------------------------------------*/
  static void WriteSelector(NXhandle pFile, NXdict pDict, SConnection *pCon)
  {
     pSicsSelector pSel = NULL;
     char *pName = NULL;
     CommandList *pCom = NULL;
     pDummy pDum = NULL;
     float fTh, fTTH, fB1, fB2;
     int iRet;
     
     pCom = FindCommand(pServ->pSics,"mono");
     if(!pCom)
     {
        SCWrite(pCon,"ERROR: no monochromator found",eError);
        return ;
     }
     pSel = (pSicsSelector)pCom->pData;
     if(!pSel)
     {
        SCWrite(pCon,"ERROR: no monochromator found",eError);
        return ;
     }
     pDum = (pDummy)pSel;
     if(strcmp(pDum->pDescriptor->name,"CrystalSelector") != 0)
     {
        SCWrite(pCon,"ERROR: monochromator is invalid",eError);
        return ;
     }
     
     NXDputalias(pFile,pDict,"mname",MonoGetType(pSel));
     iRet = GetMonoPositions(pSel,pCon,&fTh, &fTTH, &fB1, &fB2);
     if(!iRet)
     {
       SCWrite(pCon,"ERROR: Problem reading monochromator positions",eError);
       SCWrite(pCon,"ERROR: monochromator data missing in file",eError);
       return; 
     }  
     NXDputalias(pFile,pDict,"mtheta",&fTh);
     NXDputalias(pFile,pDict,"mttheta",&fTTH);
     SNXSPutDrivable(pServ->pSics,pCon, pFile,pDict,"lambda","mlambda");
     SNXSPutDrivable(pServ->pSics,pCon, pFile,pDict,"qi","menergy");
  }  
/*----------------------------------------------------------------------*/
  static float CalculateElastic(pFoWrite self, SConnection *pCon)
  {
      pIDrivable pDriv;
      pSicsVariable pVar;
      CommandList *pCom = NULL;
      float fLambda, fDist, fResult;

      pCom = FindCommand(pServ->pSics,"lambda");
      if(!pCom)
          return 0.;
      pDriv = GetDrivableInterface(pCom->pData);
      if(!pDriv)
          return 0.;
      fLambda = pDriv->GetValue(pCom->pData,pCon);
      pVar = FindVariable(pServ->pSics,"sampledist");
      if(!pVar)
          return 0.;
      fDist = pVar->fVal;
      pVar = FindVariable(pServ->pSics,"detectordist");
      if(!pVar)
          return 0.;
      fDist += pVar->fVal;
      fResult =  252.78*fLambda*(fDist/1000.);
      return fResult;
  }
/*-------------------------------------------------------------------------
   FoStart writes all the fixed data items, creates a new file etc.
   A complete file is obtained after FoStart plus a call to FoUpdate
*/   
  static int FoStart(pFoWrite self, SConnection *pCon)
  {
     NXhandle pFile = NULL;
     NXdict   pDict = NULL;
     int iStat, iLength, i;
     char pBueffel[512];
     CounterMode eMode;
     float fVal, *fArray;
     const float *fTime;
     float *fTime2 = NULL;
     char pBuffer[50];     
     
     /* get a filename */
     if(self->pFile)
        free(self->pFile);
        
     self->pFile = SNXMakeFileName(pServ->pSics,pCon);
     if(!self->pFile)
     {
       SCWrite(pCon,"ERROR: Extra severe: failed to create data file name",
               eError);
       return 0;
     }
     
     /* create a Nexus file */
     NXopen(self->pFile,NXACC_CREATE,&pFile);
     if(!pFile)
     {
         SCWrite(pCon,"ERROR: cannot create data file ",eError);
         return 0;
     }
     
     /* tell Uwe User what we are doing */
     sprintf(pBueffel,"Writing %s ......",self->pFile);
     SCWrite(pCon,pBueffel,eWarning);
     
     /* write globals */
     SNXSPutGlobals(pFile,self->pFile,"FOCUS",pCon);
     
     /* open nxdict and configure nxdict parameters */
     iStat = NXDinitfromfile(self->pDictFile,&pDict);
     if(iStat != NX_OK)
     {
        sprintf(pBueffel,"ERROR: failed to open dictionary file %s",
                self->pDictFile);
        SCWrite(pCon,pBueffel,eError);
        SCWrite(pCon,"ERROR: Aborting data file writing",eError);
        SCWrite(pCon,"ERROR: This is a SERIOUS problem!",eError);
        SCWrite(pCon,"ERROR: DATA NOT WRITTEN",eError);
        NXclose(&pFile);
        return 0;        
     }

     /* put permanent data */
     SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"etitle","title");       
     SNXFormatTime(pBueffel,511);

     /* entry & instrument stuff */
     NXDputalias(pFile,pDict,"estart",pBueffel);
     SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"iname","instrument"); 
     NXDputalias(pFile,pDict,"sname","SINQ, PSI, Switzerland");
     NXDputalias(pFile,pDict,"stype","continous spallation source");

     /* disk chopper */
     NXDputalias(pFile,pDict,"cname","Dornier disk chopper");

     /* be-filter */
     NXDputalias(pFile,pDict,"bname","BE-filter");
     SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"bstatus","bestatus"); 

     /* flight path */
     SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"fltype","flightpath"); 
     SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"fllength","flightpathlength"); 

     /* monochromator */
     WriteSelector(pFile,pDict,pCon);  

     /* fermi chupper */
     SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"fcname","ferminame"); 
     SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"fcdist","fermidist"); 

     /* counting data */
     eMode = GetHistCountMode(self->pHist);
     if(eMode == eTimer)
     {
       strcpy(pBueffel,"timer");
     }           
     else
     {
        strcpy(pBueffel,"monitor");
     }
     NXDputalias(pFile,pDict,"cnmode",pBueffel);
     fVal = GetHistPreset(self->pHist);
     NXDputalias(pFile,pDict,"cnpreset",&fVal);

     
     /* detector banks */
     fTime = GetHistTimeBin(self->pHist,&iLength);
     fTime2 = (float *)malloc(iLength*sizeof(float));
     if(fTime2)
     {
       for(i = 0; i < iLength; i++)
       {
          fTime2[i] = fTime[i]/10.;
       }
       sprintf(pBueffel,"%d",iLength);
       NXDupdate(pDict,"timebin",pBueffel);
       if(self->iMiddle)
       {
           NXDupdate(pDict,"bank","bank1");
           NXDputalias(pFile,pDict,"dtime",fTime2);
       }
       if(self->iUpper)
       {
           NXDupdate(pDict,"bank","upperbank");
           NXDputalias(pFile,pDict,"dtime",fTime2);
       }
       if(self->iLower)
       {
           NXDupdate(pDict,"bank","lowerbank");
           NXDputalias(pFile,pDict,"dtime",fTime2);
       }
       if( (self->iLower || self->iUpper) && self->iMiddle)
       {
           NXDupdate(pDict,"bank","merged");
           NXDputalias(pFile,pDict,"dtime",fTime2);
       }
       NXDupdate(pDict,"bank","bank1");


       /* calculate theoretical position of elastic peak */
       fVal = CalculateElastic(self,pCon);
       self->fElastic = (fVal - fTime2[0]) / (fTime2[1] - fTime2[0]);
       free(fTime2);
       fTime2 = NULL;
     }
     else
     {
       SCWrite(pCon,"ERROR: out of memory while writing time binning",
               eError);
     }

     SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"ddist","detectordist"); 

     /* theta arrays */
     if(self->iMiddle)
     {
       NXDupdate(pDict,"bank","bank1");
       iLength = getFMdim(MIDDLE);
       sprintf(pBuffer,"%d",iLength);
       NXDupdate(pDict,"noofdetectors",pBuffer);              
       fArray = getFMBankTheta(MIDDLE);
       NXDputalias(pFile,pDict,"dtheta",fArray);
     }
     if(self->iLower)
     {
       NXDupdate(pDict,"bank","lowerbank");
       iLength = getFMdim(LOWER);
       sprintf(pBuffer,"%d",iLength);
       NXDupdate(pDict,"noofdetectors",pBuffer);              
       fArray = getFMBankTheta(LOWER);
       NXDputalias(pFile,pDict,"dtheta",fArray);
     }
     if(self->iUpper)
     {
       NXDupdate(pDict,"bank","upperbank");
       iLength = getFMdim(UPPER);
       sprintf(pBuffer,"%d",iLength);
       NXDupdate(pDict,"noofdetectors",pBuffer);              
       fArray = getFMBankTheta(UPPER);
       NXDputalias(pFile,pDict,"dtheta",fArray);
     }
     if(self->iMiddle && ( self->iLower || self->iUpper) )
     {
       NXDupdate(pDict,"bank","merged");
       iLength = getFMdim(MERGED);
       sprintf(pBuffer,"%d",iLength);
       NXDupdate(pDict,"noofdetectors",pBuffer);              
       fArray = getFMBankTheta(MERGED);
       NXDputalias(pFile,pDict,"dtheta",fArray);
     }

     NXDupdate(pDict,"bank","bank1");
     SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"ddelay","delay"); 
     
     
     /* sample info */
     SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"saname","sample"); 
     SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"senvir","environment"); 
     SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"sdist","sampledist"); 
     SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"saangle","sampleangle"); 
     
     /* close everything */
     NXclose(&pFile);
     NXDclose(pDict,NULL);

 }
/*---------------------------------------------------------------------------*/
  static void FoUpdate(pFoWrite self, SConnection *pCon)
  {
      char pBueffel[512];
      int iInt, iStat, iTime, i, j, iDet, iIndex;
      long lVal; 
      float fVal;
      const float *fTime;
      NXhandle pFile = NULL;
      NXdict   pDict;
      HistInt *lData = NULL;
      int *iSum = NULL;
      float *fAxis = NULL;
      long *lSum = NULL;
      float fCenter, fStdDev, fFWHM;
      
      /* open everything again */
     NXopen(self->pFile,NXACC_RDWR,&pFile);
     if(!pFile)
     {
         SCWrite(pCon,"ERROR: cannot reopen data file ",eError);
         return;
     }
     iStat = NXDinitfromfile(self->pDictFile,&pDict);
     if(iStat != NX_OK)
     {
        sprintf(pBueffel,"ERROR: failed to open dictionary file %s",
                self->pDictFile);
        SCWrite(pCon,pBueffel,eError);
        SCWrite(pCon,"ERROR: Aborting data file writing",eError);
        SCWrite(pCon,"ERROR: This is a SERIOUS problem!",eError);
        SCWrite(pCon,"ERROR: DATA NOT WRITTEN",eError);
        NXclose(&pFile);
        return;
      }
      
     /* tell the user that something is happening */
     sprintf(pBueffel,"Updating %s",self->pFile);
     SCWrite(pCon,pBueffel,eWarning);
      
      /* do the end time */
     SNXFormatTime(pBueffel,511);
     NXDputalias(pFile,pDict,"eend",pBueffel);
                
     /* chopper speeds */
     SNXSPutDrivable(pServ->pSics,pCon,pFile,pDict,"diskspeed","crot"); 
     SNXSPutDrivable(pServ->pSics,pCon,pFile,pDict,"fermispeed","fcrot"); 
     SNXSPutDrivable(pServ->pSics,pCon,pFile,pDict,"phase","fcphase"); 
     SNXSPutDrivable(pServ->pSics,pCon,pFile,pDict,"ratio","cratio"); 
     
     /* counter data */  
     fVal = GetHistCountTime(self->pHist,pCon);
     NXDputalias(pFile,pDict,"cntime",&fVal);
     lVal = GetHistMonitor(self->pHist,1,pCon);
     NXDputalias(pFile,pDict,"cnmon1",&lVal);
     lVal = GetHistMonitor(self->pHist,0,pCon);
     NXDputalias(pFile,pDict,"cnmon2",&lVal);
     lVal = GetHistMonitor(self->pHist,4,pCon);
     NXDputalias(pFile,pDict,"cnmon3",&lVal);


     /* histogram with three detector banks */
     fTime = GetHistTimeBin(self->pHist,&iInt);
     iTime = iInt;
     sprintf(pBueffel,"%d",iInt);
     NXDupdate(pDict,"timebin",pBueffel);
     lData = GetHistogramPointer(self->pHist,pCon);
     if(!lData)
     {
         SCWrite(pCon,"ERROR: failed to find Histogram Memory Data",eError);
         NXclose(&pFile);
         NXDclose(pDict,NULL);
         return;
     }
     setFMDataPointer(lData,iTime);     
     if(self->iUpper)
     {
       NXDupdate(pDict,"bank","upperbank");
       iDet = getFMdim(UPPER);
       sprintf(pBueffel,"%d",iDet);
       NXDupdate(pDict,"noofdetectors",pBueffel);
       lData = getFMBankPointer(UPPER);
       NXDputalias(pFile,pDict,"dcounts",lData);
       /* summed counts for each detector */
       iSum = (int *)malloc(iDet*sizeof(int));
       if(iSum)
       {
           memset(iSum,0,iDet*sizeof(int));
           for(i = 0; i < iDet; i++)
	   {
              iIndex = i * iTime;
              for(j = 0; j < iTime; j++)
	      {
                 iSum[i] += lData[iIndex+j];
              } 
            }
            NXDputalias(pFile,pDict,"dsums",iSum);
            free(iSum);
         }
         else
         {
             SCWrite(pCon,"WARNING: out of memory, failed to do sums",
                     eWarning);
          }
     }
     /* middle bank */
     if(self->iMiddle)
     {
       NXDupdate(pDict,"bank","bank1");
       iDet = getFMdim(MIDDLE);
       sprintf(pBueffel,"%d",iDet);
       NXDupdate(pDict,"noofdetectors",pBueffel);
       lData = getFMBankPointer(MIDDLE);
       NXDputalias(pFile,pDict,"dcounts",lData);
       /* summed counts for each detector */
       iSum = (int *)malloc(iDet*sizeof(int));
       if(iSum)
       {
           memset(iSum,0,iDet*sizeof(int));
           for(i = 0; i < iDet; i++)
	   {
              iIndex = i * iTime;
              for(j = 0; j < iTime; j++)
	      {
                 iSum[i] += lData[iIndex+j];
              } 
            }
            NXDputalias(pFile,pDict,"dsums",iSum);
            free(iSum);
         }
         else
         {
             SCWrite(pCon,"WARNING: out of memory, failed to do sums",
                     eWarning);
          }
     }
     if(self->iLower)
     {
       NXDupdate(pDict,"bank","lowerbank");
       iDet = getFMdim(LOWER);
       sprintf(pBueffel,"%d",iDet);
       NXDupdate(pDict,"noofdetectors",pBueffel);
       lData = getFMBankPointer(LOWER);
       NXDputalias(pFile,pDict,"dcounts",lData);
       /* summed counts for each detector */
       iSum = (int *)malloc(iDet*sizeof(int));
       if(iSum)
       {
           memset(iSum,0,iDet*sizeof(int));
           for(i = 0; i < iDet; i++)
	   {
              iIndex = i * iTime;
              for(j = 0; j < iTime; j++)
	      {
                 iSum[i] += lData[iIndex+j];
              } 
            }
            NXDputalias(pFile,pDict,"dsums",iSum);
            free(iSum);
         }
         else
         {
             SCWrite(pCon,"WARNING: out of memory, failed to do sums",
                     eWarning);
          }
     }
     /* merged data */
     if( (self->iUpper || self->iLower) && self->iMiddle)
     {
       NXDupdate(pDict,"bank","merged");
       iDet = getFMdim(MERGED);
       sprintf(pBueffel,"%d",iDet);
       NXDupdate(pDict,"noofdetectors",pBueffel);
       lData = getFMBankPointer(MERGED);
       NXDputalias(pFile,pDict,"dcounts",lData);
       /* summed counts for each detector */
       iSum = (int *)malloc(iDet*sizeof(int));
       if(iSum)
       {
           memset(iSum,0,iDet*sizeof(int));
           for(i = 0; i < iDet; i++)
	   {
              iIndex = i * iTime;
              for(j = 0; j < iTime; j++)
	      {
                 iSum[i] += lData[iIndex+j];
              } 
            }
            NXDputalias(pFile,pDict,"dsums",iSum);
            free(iSum);
         }
         else
         {
             SCWrite(pCon,"WARNING: out of memory, failed to do sums",
                     eWarning);
          }
     }

     
     /* calculate elastic peak position */
     NXDupdate(pDict,"bank","bank1");
     lData = getFMBankPointer(MIDDLE);
     if(lData)
     {
        lSum = (long *)malloc(iTime *sizeof(long));
        fAxis = (float *)malloc(iTime *sizeof(float));
        if( lSum && fAxis)
        {
           memset(lSum,0,iTime*sizeof(long));
           memset(fAxis,0,iTime*sizeof(float));
           for(i = 5; i < iDet - 5; i++)
	   {
             iIndex = i * iTime;
             for(j = 0; j < iTime; j++)
	     {
                lSum[j] += lData[iIndex+j];
             } 
          }
          for(i = 0; i < iTime; i++)
	  {
              fAxis[i] = (float)i;
          }
          iStat = CalculateFitFromData(self->pFitter,fAxis,lSum,iTime);
          if(iStat != 1)
	  {
              SCWrite(pCon,"WARNING: problem locating elastic peak",eWarning);
          }
          GetFitResults(self->pFitter,&fCenter,&fStdDev,&fFWHM,&fVal);
          fVal = fCenter - self->fElastic;
          if(fVal < 0.)
              fVal = - fVal;
           /* bad value, leave at theoretical value */
           if(fVal > 10.)
	   {
               SCWrite(pCon,
           "WARNING: bad fit result, using theoretical elastic peak position",
                eWarning);
            }
            else
	    {
              self->fElastic = fCenter;
            }
            free(lSum);
            free(fAxis);
         }          
         else
         {
           SCWrite(pCon,"WARNING: out of memory, failed to do sums",eWarning);
         }
     }
     sprintf(pBueffel,"Elastic peak found at detector: %f",self->fElastic);
     SCWrite(pCon,pBueffel,eWarning);
     NXDputalias(pFile,pDict,"delastic",&self->fElastic);


     /* sample temperature */
     SNXSPutEVVar(pFile,pDict,"temperature",pCon,"stemp",NULL);
            

     /* close everything */
     NXclose(&pFile);
     NXDclose(pDict,NULL);
      
  }                                              
/*-------------------------------------------------------------------------
  FoLink sets all the links for the NXdata vGroup. Had to be separate because
  at least one update is necessary before this can be done.
*/
  static void FoLink(pFoWrite self, SConnection *pCon)
  {
     NXhandle pFile;
     NXdict   pDict;
     int iStat;
     char pBueffel[512];
     
       /* open everything again */
     NXopen(self->pFile,NXACC_RDWR,&pFile);
     if(!pFile)
     {
         SCWrite(pCon,"ERROR: cannot reopen data file ",eError);
         return;
     }
     iStat = NXDinitfromfile(self->pDictFile,&pDict);
     if(iStat != NX_OK)
     {
        sprintf(pBueffel,"ERROR: failed to open dictionary file %s",
                self->pDictFile);
        SCWrite(pCon,pBueffel,eError);
        SCWrite(pCon,"ERROR: Aborting data file writing",eError);
        SCWrite(pCon,"ERROR: This is a SERIOUS problem!",eError);
        SCWrite(pCon,"ERROR: DATA NOT WRITTEN",eError);
        NXclose(&pFile);
        return;
      }

      if( (self->iUpper || self->iLower) && self->iMiddle)
      {  
         NXDupdate(pDict,"bank","merged");  
         NXDaliaslink(pFile,pDict,"dana","dcounts");
         NXDaliaslink(pFile,pDict,"dana","dtime");
         NXDaliaslink(pFile,pDict,"dana","dtheta"); 
         NXDaliaslink(pFile,pDict,"dana","cnmon1"); 
      }     
     
      if(self->iUpper)
      {  
         NXDupdate(pDict,"bank","upperbank");  
         NXDaliaslink(pFile,pDict,"dana","dcounts");
         NXDaliaslink(pFile,pDict,"dana","dtime");
         NXDaliaslink(pFile,pDict,"dana","dtheta"); 
         NXDaliaslink(pFile,pDict,"dana","cnmon1"); 
      }     
      if(self->iMiddle)
      {  
         NXDupdate(pDict,"bank","bank1");  
         NXDaliaslink(pFile,pDict,"dana","dcounts");
         NXDaliaslink(pFile,pDict,"dana","dtime");
         NXDaliaslink(pFile,pDict,"dana","dtheta"); 
         NXDaliaslink(pFile,pDict,"dana","cnmon1"); 
      }     
      if(self->iLower)
      {  
         NXDupdate(pDict,"bank","lowerbank");  
         NXDaliaslink(pFile,pDict,"dana","dcounts");
         NXDaliaslink(pFile,pDict,"dana","dtime");
         NXDaliaslink(pFile,pDict,"dana","dtheta"); 
         NXDaliaslink(pFile,pDict,"dana","cnmon1"); 
      }     


      /* close everything */
     NXclose(&pFile);
     NXDclose(pDict,NULL);
     self->iNew = 0;
  }  
/*---------------------------------------------------------------------------
  This is the task function for updating the data file any now and then
  automatically
*/
 static int FoTask(void *pData)
 {
    pFoWrite self = NULL;
    int iWrite, iRet;
    
    self = (pFoWrite)pData;
    if(!self)
      return 0;
      
    /* figure out if we need to write */
    iWrite = 0;
    iRet = 1;
    /* first case: update intervall */
    if(time(NULL) >= self->tUpdate)
    {
       self->tUpdate = time(NULL) + self->iInterval;
       iWrite = 1;
       iRet = 1;
    }  
    if(self->iEnd)
    {
       self->tUpdate = 0;
       iWrite = 0;
       iRet = 0;
    }
    
    if(iWrite)
    {
       if(self->iNew)
       {
          FoStart(self,self->pCon);
          FoUpdate(self,self->pCon);
          FoLink(self,self->pCon);
       }
       else
       {
          FoUpdate(self,self->pCon);
       }
    }
    return iRet;
 }     
/*------------------------------------------------------------------------*/
  static void KillFoWrite(void *pData)
  {
     pFoWrite self = NULL;
     
     self = (pFoWrite)pData;
     if(!self)
        return;
        
     if(self->pDes)
        DeleteDescriptor(self->pDes);
     
     if(self->pDictFile)
        free(self->pDictFile);
        
     if(self->pFile)
        free(self->pFile);      

     if(self->pFitter)
       DeleteFitCenter(self->pFitter);

     /* free fomerge */
      killFM();
        
     free(self);     
  } 
/*-----------------------------------------------------------------------*/
  int FoInstall(SConnection *pCon, SicsInterp *pSics, void *pData,
                  int argc, char *argv[])
  {
  
      pHistMem pHist= NULL;
      char pBueffel[512];
      pFoWrite pNew = NULL;
      pICallBack pCall = NULL;
      pDummy pDum;
      
      /* check arguments */
      if(argc < 4 )
      {
         SCWrite(pCon,"ERROR: Insufficient number of arguments to FoInstall",
                 eError);
         return 0;        
      }
      
      pHist = (pHistMem)FindCommandData(pSics,argv[1],"HistMem");
      if(!pHist)
      {
         SCWrite(pCon,"ERROR: Histogram memory NOT found",eError);
         return 0;
      }
      
      if(!initializeFM(argv[3]))
      {
         SCWrite(pCon,"ERROR: bad merge data file",eError);
         return 0;
      }

      /* allocate data structure */
      pNew = (pFoWrite)malloc(sizeof(FoWrite));
      if(!pNew)
      {
         SCWrite(pCon,"ERROR: out of memory in FoInstall",eError);
         return 0;
      }
      memset(pNew,0,sizeof(FoWrite));
      pNew->pDes = CreateDescriptor("FocusWrite");
      pNew->pFitter = CreateFitCenter(NULL);
      if( (!pNew->pDes) || (!pNew->pFitter) )
      {
         SCWrite(pCon,"ERROR: out of memory in FoInstall",eError);
         return 0;
      }
      pNew->pHist = pHist;
      pNew->pDictFile = strdup(argv[2]);
      pNew->iInterval = 20*60;
      pNew->iMiddle =1;
 
      /* install callbacks */
      pDum = (pDummy)pHist;
      pCall = (pICallBack)pDum->pDescriptor->GetInterface(pHist,
                                                  CALLBACKINTERFACE);
      if(!pCall)
      {
         SCWrite(pCon,
            "ERROR: no callback interface found at your histogram memory",
                 eError);
         KillFoWrite(pNew);
         return 0;        
      }
      RegisterCallback(pCall,COUNTSTART,Countstartcallback,pNew,NULL);
      RegisterCallback(pCall,COUNTEND,Countendcallback,pNew,NULL);
      
      /* install command */
      AddCommand(pSics,"StoreFocus",FoAction,KillFoWrite,pNew);
      return 1;       
 }                
/*-------------------------------------------------------------------------*/
 int FoAction(SConnection *pCon, SicsInterp *pSics, void *pData,
              int argc, char *argv[])
 {
    int iRet, iVal;
    pFoWrite self = NULL;
    char pBueffel[512];
     
    if(argc < 1)
    {
       SCWrite(pCon,"ERROR: Insufficient number of arguments to StoreFocus",
               eError);
       return 0;        
    }
    self = (pFoWrite)pData;
    assert(self);
    
    strtolower(argv[1]);
    if(strcmp(argv[1],"start") == 0)
    {
       FoStart(self,pCon);
       FoUpdate(self,pCon);
       FoLink(self,pCon);
       return 1;
    }
    else if(strcmp(argv[1],"update") == 0)
    {
      if((self->iNew) || (!self->pFile))
      {
        FoStart(self,pCon);
        FoUpdate(self,pCon);
        FoLink(self,pCon);
      }
      else
      {
        FoUpdate(self,pCon);
      }
      return 1; 
    }
    else if(strcmp(argv[1],"getfile") == 0)
    {
        sprintf(pBueffel,"storefocus.file = %s",self->pFile);
        SCWrite(pCon,pBueffel,eValue);
        return 1;
    }
    else if(strcmp(argv[1],"interval") == 0)
    {
       if(argc > 2) /* set value */
       {
          if(!SCMatchRights(pCon,usUser))  
          {
            return 0;
          }   
          iRet = Tcl_GetInt(pSics->pTcl,argv[2],&iVal);
          if(iRet != TCL_OK)
          {
             sprintf(pBueffel,"ERROR: cannot convert --> %s <-- to number ",argv[2]);
             SCWrite(pCon,pBueffel,eError);
             return 0;
          }
          self->iInterval = iVal*60; /* go to seconds from minutes*/
          SCSendOK(pCon);
          return 1;
       }
       else /* read the value */ 
       {
          sprintf(pBueffel,"storefocus.interval = %d",self->iInterval/60);
          SCWrite(pCon,pBueffel,eValue);
          return 1;
       }
    }
    else if(strcmp(argv[1],"middle") == 0)
    {
       if(argc > 2) /* set value */
       {
          if(!SCMatchRights(pCon,usMugger))  
          {
            return 0;
          }   
          iRet = Tcl_GetInt(pSics->pTcl,argv[2],&iVal);
          if(iRet != TCL_OK)
          {
             sprintf(pBueffel,"ERROR: cannot convert --> %s <-- to number ",
                     argv[2]);
             SCWrite(pCon,pBueffel,eError);
             return 0;
          }
          if(iVal < 0) iVal = 0;
          self->iMiddle = iVal;
          setFMconfiguration(self->iUpper,self->iMiddle,self->iLower);
          SCSendOK(pCon);
          return 1;
       }
       else /* read the value */ 
       {
          sprintf(pBueffel,"storefocus.middle = %d",self->iMiddle);
          SCWrite(pCon,pBueffel,eValue);
          return 1;
       }
    }
    else if(strcmp(argv[1],"lower") == 0)
    {
       if(argc > 2) /* set value */
       {
          if(!SCMatchRights(pCon,usMugger))  
          {
            return 0;
          }   
          iRet = Tcl_GetInt(pSics->pTcl,argv[2],&iVal);
          if(iRet != TCL_OK)
          {
             sprintf(pBueffel,"ERROR: cannot convert --> %s <-- to number ",
                     argv[2]);
             SCWrite(pCon,pBueffel,eError);
             return 0;
          }
          if(iVal < 0) iVal = 0;
          self->iLower = iVal;
          setFMconfiguration(self->iUpper,self->iMiddle,self->iLower);
          SCSendOK(pCon);
          return 1;
       }
       else /* read the value */ 
       {
          sprintf(pBueffel,"storefocus.lower = %d",self->iLower);
          SCWrite(pCon,pBueffel,eValue);
          return 1;
       }
    }
    else if(strcmp(argv[1],"upper") == 0)
    {
       if(argc > 2) /* set value */
       {
          if(!SCMatchRights(pCon,usMugger))  
          {
            return 0;
          }   
          iRet = Tcl_GetInt(pSics->pTcl,argv[2],&iVal);
          if(iRet != TCL_OK)
          {
             sprintf(pBueffel,"ERROR: cannot convert --> %s <-- to number ",
                     argv[2]);
             SCWrite(pCon,pBueffel,eError);
             return 0;
          }
          if(iVal < 0) iVal = 0;
          self->iUpper = iVal;
          setFMconfiguration(self->iUpper,self->iMiddle,self->iLower);
          SCSendOK(pCon);
          return 1;
       }
       else /* read the value */ 
       {
          sprintf(pBueffel,"storefocus.upper = %d",self->iUpper);
          SCWrite(pCon,pBueffel,eValue);
          return 1;
       }
    }
    SCWrite(pCon,"ERROR: subcommand to storefocus not recognized",eError);
    return 0;
 }              
