/*---------------------------------------------------------------------------
                           F O M E R G E

 This module is for the FOCUS instrument. FOCUS has three detector 
 banks. These are sent to SICS as one big array by the histogram memory.
 However, depending on the situation these need to be accessed either:
 - as single banks.
 - or as a merged bank, where detector counts from all three banks are
   summed onto a common two theta scale. 

 This module is responsible for accessing single banks and for doing
 the merging operation.

 copyright: see copyright.h


 Mark Koennecke, March 2000
 --------------------------------------------------------------------------*/
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include "fortify.h"

/* change this in line with HistMem.h */
typedef int HistInt;

#include "fomerge.h"

/*================== module static data items ==========================*/
   static HistInt *masterData, *upperData, *mediumData, *lowerData, 
                  *mergedData = NULL;
   static int timeBin, nUpper, nLower, nMedium, nMerged;
   static float *upperTheta, *lowerTheta, *mediumTheta, *mergedTheta;
   static int *mergeUp, *mergeMed, *mergeLow;
   int iMerged;    

   int medium, upper, lower;
/*------------------------------------------------------------------------
   The tables with the theta values for the detector banks aand the merge
   table living in mergeUp, mergeMed, mergeLow is initialized from a
   data file, the mergefile. mergeUp, mergeLow, mergeMed contain a 0 if
   the detector from this bank does not need to be summed for this merged
   two theta value or the number of the detector from  this bank to use. 
   -------------------------------------------------------------------------*/
int initializeFM(char *mergefile)
{
   FILE *fd = NULL;
   int i, j, iTest;
   char pBuffer[132];
   char *pPtr = NULL;

   /* open the file */
   fd = fopen(mergefile,"r");
   if(!fd)
   {
      fprintf(stdout,"ERROR: merge data file not found!\n");
      return 0;
   }

   /* skip first line */
   pPtr = fgets(pBuffer,131,fd);

   /* read the number of merged data points */
   fgets(pBuffer,131,fd);
   iTest = sscanf(pBuffer,"%d",&nMerged);
   if((iTest != 1) || (nMerged <= 0) )
   {
      fprintf(stdout,"ERROR: Invalid or corrupt merge dat file");
      return 0;
   }

   /* allocate space */
   mergedTheta = (float *)malloc(nMerged*sizeof(float));
   mergeUp  =     (int *)malloc(nMerged*sizeof(int));
   mergeMed =     (int *)malloc(nMerged*sizeof(int));
   mergeLow =     (int *)malloc(nMerged*sizeof(int));
   if( mergedTheta == NULL || mergeUp == NULL || mergeMed == NULL 
       || mergeLow == NULL )
   {
      fprintf(stdout,"ERROR: out of memory in fomerge\n");
      return 0;
   }

   /* read the merge table */
   for(i = 0; i < nMerged; i++)
   {
     fgets(pBuffer,131,fd);
     iTest = sscanf(pBuffer,"%d %f %d %d %d",&j, &mergedTheta[i],
                    &mergeUp[i], &mergeMed[i], &mergeLow[i]);
     if(iTest != 5)
     {
      fprintf(stdout,"ERROR: Invalid or corrupt merge dat file");
      return 0;
     }
   }
 
   /* read upper bank two theta */
   fgets(pBuffer,131,fd);
   fgets(pBuffer,131,fd);
   iTest = sscanf(pBuffer,"%d",&nUpper);   
   if((iTest != 1) || (nUpper <= 0) )
   {
      fprintf(stdout,"ERROR: Invalid or corrupt merge data file");
      return 0;
   }
   upperTheta = (float *)malloc(nUpper*sizeof(float));
   if(upperTheta == NULL)
   {
      fprintf(stdout,"ERROR: out of memory in fomerge\n");
      return 0;
   }
   for(i = 0; i < nUpper; i++)
   {
      fgets(pBuffer,131,fd);
      sscanf(pBuffer,"%f",&upperTheta[i]);
   }

   /* read middle bank two theta */
   fgets(pBuffer,131,fd);
   fgets(pBuffer,131,fd);
   iTest = sscanf(pBuffer,"%d",&nMedium);   
   if((iTest != 1) || (nMedium <= 0) )
   {
      fprintf(stdout,"ERROR: Invalid or corrupt merge data file");
      return 0;
   }
   mediumTheta = (float *)malloc(nMedium*sizeof(float));
   if(mediumTheta == NULL)
   {
      fprintf(stdout,"ERROR: out of memory in fomerge\n");
      return 0;
   }
   for(i = 0; i < nMedium; i++)
   {
      fgets(pBuffer,131,fd);
      sscanf(pBuffer,"%f",&mediumTheta[i]);
   }

   /* read lower bank two theta */
   fgets(pBuffer,131,fd);
   fgets(pBuffer,131,fd);
   iTest = sscanf(pBuffer,"%d",&nLower);   
   if((iTest != 1) || (nLower <= 0) )
   {
      fprintf(stdout,"ERROR: Invalid or corrupt merge data file");
      return 0;
   }
   lowerTheta = (float *)malloc(nLower*sizeof(float));
   if(lowerTheta == NULL)
   {
      fprintf(stdout,"ERROR: out of memory in fomerge\n");
      return 0;
   }
   for(i = 0; i < nLower; i++)
   {
      fgets(pBuffer,131,fd);
      sscanf(pBuffer,"%f",&lowerTheta[i]);
   }
   
   /* done */
   timeBin = 0;
   iMerged = 0;
   medium =1;
   upper = lower = 0;

   fclose(fd);
   return 1;
}
/*--------------------------------------------------------------------------
   sets the data pointer and finds the starting points for the different 
   banks. This routine contains the knowledge how the detector banks are 
   laid out in the histogram returned from the histogram memory.
   -------------------------------------------------------------------------*/
int setFMDataPointer(HistInt *lData, int mytimeBins)
{
  HistInt *dataPtr;

  if(lData == NULL)
     return 0;

  /* the first set is the medium bank */
  masterData = dataPtr = lData;
  if(medium)
  {
    mediumData = masterData;
  }
  else
  {
    mediumData = NULL;
  }

  /* the next set is the upper bank */
  if(upper)
  {
    upperData = dataPtr + mytimeBins*nMedium;
    dataPtr = upperData;
  }   
  else
  {
    upperData = NULL;
  }

  /* the last is the lower bank */
  if(lower)
  {
    lowerData = dataPtr + mytimeBins*nUpper;
    dataPtr = lowerData;
  }   
  else
  {
    lowerData = NULL;
  }

  /* deal with data allocation for merged data */
  if(mytimeBins != timeBin)
  {
    if(mergedData != NULL)
    {
        free(mergedData);
    }
    timeBin = mytimeBins;
    mergedData = (HistInt *)malloc(nMerged*timeBin*sizeof(HistInt));
    if(mergedData == NULL)
    {
      fprintf(stdout,"ERROR: out of memory in fomerge.setFMdataPointer\n");
      return 0;
    }
    memset(mergedData,0,nMerged*timeBin*sizeof(HistInt));
  }

  iMerged = 0;
  return 1;
}
/*-----------------------------------------------------------------------*/
void setFMconfiguration(int up, int med, int low)
{
  upper = up;
  medium = med;
  lower = low;
}
/*--------------------------------------------------------------------------
  The algorithm is suboptimal because we have to take care of the case of
  missing detector banks due to missing or broken electronics or whatever.
*/ 
static void mergeData(void)
{
   int i, j, nDiv;
   HistInt *startMerge, *startData;

   assert(mergedData);

   for(i = 0; i < nMerged; i++)
   {
     startMerge = mergedData + i * timeBin;
     nDiv = 0;
     /* upper bank contribution */
     if( mergeUp[i] != 0 && upperData != NULL)
     {
       startData = upperData + (mergeUp[i]-1)*timeBin;
       for(j = 0; j < timeBin; j++)
       {
         startMerge[j] += startData[j]; 
       }
       nDiv++;
     }
     /* medium bank contribution */
     if( mergeMed[i] != 0 && mediumData != NULL)
     {
       startData = mediumData + (mergeMed[i]-1)*timeBin;
       for(j = 0; j < timeBin; j++)
       {
         startMerge[j] += startData[j]; 
       }
       nDiv++;
     }
     /* lower bank contribution */
     if( mergeLow[i] != 0 && lowerData != NULL)
     {
       startData = lowerData + (mergeLow[i]-1)*timeBin;
       for(j = 0; j < timeBin; j++)
       {
         startMerge[j] += startData[j]; 
       }
       nDiv++;
     }
     /* do we need to run a division? */
     if(nDiv > 1)
     {
        for(j = 0; j < timeBin; j++)
        {
           startMerge[j] /= nDiv;
        }
     }
  } /* end of for */
  iMerged = 1;
}  
/*-----------------------------------------------------------------------*/
void killFM(void)
{
  if(mergedData != NULL)
     free(mergedData);

   if(lowerTheta != NULL)
      free(lowerTheta);

   if(mediumTheta != NULL)
      free(mediumTheta);

   if(upperTheta != NULL)
      free(upperTheta);

   if(mergedTheta != NULL)
      free(mergedTheta);

   if(mergeUp != NULL)
     free(mergeUp);

   if(mergeMed != NULL)
     free(mergeMed);

   if(mergeLow != NULL)
     free(mergeLow);

}
/*--------------------------------------------------------------------------
  Below are some not very interesting data access routines 
  ---------------------------------------------------------------------------*/
HistInt *getFMBankPointer(int which)
{
  switch(which)
  {
    case UPPER:
         return upperData;
         break;
    case MIDDLE:
         return mediumData;
         break;
    case LOWER:
         return lowerData;
         break;
    case MERGED:
         if(!iMerged)
            mergeData();
         return mergedData;
         break;
    default:
         assert(0);
  }
}
/*-------------------------------------------------------------------------*/
float *getFMBankTheta(int which)
{
  switch(which)
  {
    case UPPER:
         return upperTheta;
         break;
    case MIDDLE:
         return mediumTheta;
         break;
    case LOWER:
         return lowerTheta;
         break;
    case MERGED:
         return mergedTheta;
         break;
    default:
         assert(0);
  }
}
/*-------------------------------------------------------------------------*/
int getFMdim(int which)
{
  switch(which)
  {
    case UPPER:
         return nUpper;
         break;
    case MIDDLE:
         return nMedium;
         break;
    case LOWER:
         return nLower;
         break;
    case MERGED:
         return nMerged;
         break;
    case TIMEBIN:
         return timeBin;
         break;
    default:
         assert(0);
  }
}
