/***************************************************************************/
/* emcspy_lib.c,
 *
 * Modification history:
 * 4-8-97 S.Miscetti: created.
 * modification of l2spy.c .. 
 * - process is in the template.
 * -- include all the goodies from emcmon in cxx.
 * - ready for pedestals.. soon for monitoring
 * a lot of functions from Simona: DaqPoi,EmcReadPeds,CeleMaker
 */
/***************************************************************************/
/*
#define DEBUG_ON
*/
#include <math.h>
#include <stdio.h>
#include <time.h>
#include <sys/times.h>
#include <sys/signal.h>
#include <unistd.h>
#include <errno.h>

#include <spy.h>
#include <rockml2frame.h>

/* Error */
#include "Error.h"

/* proctmpl */
#include "template.h"
extern struct command_list *first_cmd;

/* L2 */
/* #include <l2defs.h> */
#include <l2defs.h>
#include "emcspy.h"
#include <l2event.h>
#include <l2buffer.h>
#include <l2ybos.h>
#include <YbosLocate.h>
#include <dmap.h>
#include <GeoVme.h>
#include "raw2cele.h"
/*  unfortunately here we need some global variables ..
    pedval expecially + monitor values for Emc */
typedef struct EmcMonVal_
{
  int Dpoi;
  float occneg;
  float occpos;
  float mip;
  float deltat0;
} EmcMonVal;

#define FirstPedRound 200

SysTestPeds PedVal[NumberOfSlots][NumberOfChannels];
EmcMonVal   MonVal[NumberOfSlots][NumberOfChannels];

/* for double round loop on peds */
int  pedEvents[NumberOfSlots][NumberOfChannels][2] = 0;
int  adcminval[NumberOfSlots][NumberOfChannels] = 100;
int  adcmaxval[NumberOfSlots][NumberOfChannels] = 500;



/* .... NOW EmC PROTOTYPING .... */

/* 
The basic fundamental one
*/
int DaqPoiUpk(int *DaqPoi, int *Chain, int *Crate, int *Slot, int *Chan);
int CELEMaker(int *NWords, int *YBDataPoi, CELEStru *CaloEv, int *CELELen);

/* 
useful simple functions to get mean & rms 
*/
float emc_get_mean(int nev, float sum);
float emc_get_rms(int nev, float mean, float sum2);

/* 
   basic for monitoring
   */
int EmcReadPeds(char *PedFile);
int EmcInitMonitor(char *MonFile);
int EmcMakeMonitor(int celelen, CELEStru *CaloEv);
int EmcCloseMonitor(char *MonFile);
/* 
   basic for pedestals ... suggestion: same functions inside Builder 
   to do it for all ADCs ... TDCs .. per chain .. better typedef
   to be defined 
   */
int EmcInitPeds(void);
int EmcClosPeds(char *PedOut);
int EmcMakePeds(int banklen, int* bankptr);

static SPY_id spy_id;

/* counters */
static u_long events_spy    = 0;
static u_long bytes_processed = 0;

/* timing */
static struct tms tms_begin, tms_end; 
static u_long t_begin, t_end; 

static unsigned int count_events = 0;

/***************************************************************************/

int EmcspyInit (AskVariables* ask_var)
/*-------------------------------------
 * Should be called at program start.
 */
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
{
/*.........................................................................*/
  DEBUG(("EmcspyInit> IN\n"));
  DEBUG(("EmcspyInit> OUT\n"));
  return (Error_NO);
}

/***************************************************************************/
int EmcspyBegin (AskVariables* ask_var)
/*-------------------------------------
 * Should do all to make the state transition END_STATE -> RESUME_STATE to
 * allow a 'warm' start afterwards. It should also allow a defined restart
 * after EmcspyEnd() has be called!
 */
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
{
/*.........................................................................*/
  DEBUG(("EmcspyBegin> IN\n"));

  /* 1. Map to multiple circular buffer.
   */
  
  if ( spy_open_local(BUILD_CIRC_NAME,&spy_id) != SPY_ERROR_OK)
    {
      return Error_YES;
    }
  events_spy = 0;

  /* at begin run .. please read your maps or gracefully exit the program */
  

  {
    int Status;

    GeoVmeLoadMap("geovmefile");          /* Load GeoVmeMap */
    Status = dmap_load("dmapfile");       /*   Load DMAP    */  
    if ( Status != DMAP_ERROR_OK )
      {
        printf("Error in dmap_load \n");
        return Error_YES;
      }

    if( ask_var->monitor[0]!='m')
      {
	printf("emcspy: Reading Pedestals file from pedfile");
	Status = EmcReadPeds("pedfile");         /* Read Peds File */
	if ( Status != 0 )
	  {
	    return Error_YES;
	  }
	Status = EmcInitMonitor("monfile");
      }   
    else if ( ask_var->monitor[0]!='p')
      {
	int islot,ichan;

	printf("emcspy: opening Pedestals file pedout");
	Status = EmcInitPeds();
	if ( Status != 0 )
	  {
	    return Error_YES;
	  }
      }   
    else
      {
	printf(" Please select a default running condition m or p");
	printf(" by typing: ask emcspy monitor xx" );
      }
  }


/*   FillChainDef(); */
  YbosInit();

  /* 1. Open output file and put begin-of-run record
   */
  if (ask_var->record[0]!='\0')
    {
      YbosOpen(&ask_var->runnr,ask_var->record,ask_var->basedir,"spy");
    }

  DEBUG(("EmcspyBegin> OUT\n"));
  return (Error_NO);
}

/***************************************************************************/
int EmcspyEnd (AskVariables* ask_var)
/*-----------------------------------
 * Should do all to make the state transition BEGIN_STATE -> END_STATE. 
 * With EmcspyBegin() a defined restart should be possible also with
 * modified parameters (number of nodes etc.). In principle it should
 * never be necessary to kill the l2send process.
 */
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
{
/*.........................................................................*/
  DEBUG(("EmcspyEnd> IN\n"));

  /* 1. Close output file and put end-of-run record
   */
  if (ask_var->record[0]!='\0')
    {
      (void)YbosClose(&ask_var->runnr);
      printf("File closed...\n");
    }
  
  /* 2. Unmap from to multiple circular buffer.
   */
  spy_close(spy_id);
  /* 3. close all opened files */
  if ( ask_var->monitor[0] == 'm' )
    {
      EmcCloseMonitor("monfile");
    }
  else if( ask_var->monitor[0] == 'p')
    {
      EmcClosePeds("pedout");
    }
  else
    printf(" Nothing to close ..");
        
  
  DEBUG(("EmcspyEnd> OUT\n"));
  return (Error_NO);
}

/***************************************************************************/
int EmcspyProcess (AskVariables* ask_var)
/*----------------------------------------
 */
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
{
  struct timeval *wait = &ask_var->timeout;
  int i, n;
  int end_of_run = FALSE;
/*.........................................................................*/
 
  DEBUG(("EmcspyProcess> IN, build events from %d source\n",ncirc));

  if (ask_var->runstate == RUNNING_STATE)
    {
      /* start timing */
      t_begin = times(&tms_begin);
      printf("EmcspyProcess> Timing started ...\n");
    }
  while (ask_var->runstate == RUNNING_STATE)
    {

      l2buffer_header *buf;
      int buflen;
      int err;

      /* we read the streams of events from the head of the multiple circular
       * buffer. All pieces shoud have the same sequence event number because
       * they belong to the same events.
       */
      err = spy_get(spy_id,(char **) &buf,&buflen);

      if ( err == SPY_ERROR_OK)
	{
	  int i, j, Status, CELELen;
	  char *ptr = buf->data;
          CELEStru *CaloEv;

	  for (i=0; i<buf->events_in_buffer; i++)
	    {
	      l2buffer_event *ibuf = (l2buffer_event *) ptr;
	      int banklen,j;
	      int *bankptr;

	      ptr += ibuf->length+sizeof(int);

	      yboslocateinit_( (int *) ibuf->data);
	      yboslocate_((int *) ibuf->data,"CALR",
			  &banklen,&bankptr);
	      
	      if( ask_var->monitor[0]!='p')
		{
		  Status = EmcMakePeds(banklen, bankptr);
		  if ( Status != 0 )
		    {
		      printf(" RAW2CELE> Error while creating CELE structure. \n");
		      exit(1);
		    }
		}
	      else if( ask_var->monitor[0]!='m')
		{
		  CaloEv = (CELEStru* )calloc(banklen,sizeof(CELEStru));
		  Status = CELEMaker(&banklen,bankptr,(CELEStru*) CaloEv,&CELELen);
		  if ( Status != 0 )
		    {
		      printf(" RAW2CELE> Error while creating CELE structure. \n");
		      exit(1);
		    }
		  Status = EmcMakeMonitor(CELELen, (CELEStru*) CaloEv);
		  free(CaloEv);
		}
	    }
	  free(buf);
	}
      else /* polling time */
	{
	  select (1, (FD_SET_T)0, (FD_SET_T)0, (FD_SET_T)0, wait);
	  ask_var->timeouts++;
	}
      /* new command arrived ? */
    check_for_command:
      if (first_cmd != NULL)
	check_command(NULL);
    }
  return (Error_NO);
}

/***************************************************************************/
void EmcspyStatus (AskVariables* ask_var)
/*----------------------------------------
 */
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
{
  int i;
  double ticks, dt, dtcpu, bytes_rate;
/*.........................................................................*/

  ticks = (double)sysconf(_SC_CLK_TCK);

  t_end = times(&tms_end);

  dt=(double)(t_end-t_begin)/ticks;
  dtcpu=(double)(tms_end.tms_utime-tms_begin.tms_utime+
		 tms_end.tms_stime-tms_begin.tms_stime)/ticks;
  bytes_rate = (double)bytes_processed/1024./1024./dt;
  printf("------------------------------------------------------------\n");
  printf("Run state: %d\n ",ask_var->runstate); 
  printf("Total Time: %.1f sec, CPU usage: %4.1f%% for %d sub-events\n",
	 dt,dtcpu/dt*100.,events_spy);
  printf("%u bytes processed  ==> MByte/sec: %3.1f\n",
	 bytes_processed,bytes_rate) ;
  printf("buffer empty timeouts: %d\n",ask_var->timeouts);
  printf("------------------------------------------------------------\n");
}
/**********************************************************************/
int EmcMakePeds(int banklen, int *YBDataPoi)
/*********************************************************************
  It assumes a global matrix of structures for Pedestals
 *********************************************************************/
{
  int YBosData;
  int iloop,Status;
  rockm_l2frame Frame;
  DMAP_CHAN_EL El2Cal;

  for (iloop=0; iloop < banklen; iloop++)
    {
      int chan,crate,slot,chain,value, bit13;
      YBosData = (unsigned int) (YBDataPoi[iloop]); 
      rockm_l2frame_raw2l2frame(YBosData,Frame);
      if (Frame.chain == 0 ) Frame.chain = 1;
      Frame.chan &= 31;
      chan  = Frame.chan;
      chain = Frame.chain;
      crate = Frame.crate;
      slot  = Frame.slot;
      bit13 = Frame.res;
      value = Frame.data;
      
      Status = 
	dmap_get(chain, crate, slot, chan, &El2Cal);
      
      if ( Status != DMAP_ERROR_OK )
	{
	  printf("EmcMakePeds> Wrong DMAP association \n");
	  return -1;
	}
      else
	{
	  if( El2Cal.btype == DMAP_BTYPE_CALADC)
	    {
	      if ( bit13 == 0 )
		{
		  /*	
			if( pedEvents[slot][chan][0] == FirstPedRound)
		    {
		      int nev;
		      float sum,sum2;
		      float mean,rms;
		      nev = pedEvents[slot][chan][0];
		      sum = pedEvents[slot][chan].PedL;
		      sum2= pedEvents[slot][chan].SigL;
		      mean = get_mean(nev,sum);
		      rms  = get_rms (nev,mean,sum2);
		      adcminval[slot][chan] = mean-3*rms;
		      adcmaxval[slot][chan] = mean+3*rms;
		      adc
		    }
		    */

		  if( value > adcminval[slot][chan] ||
		      value < adcmaxval[slot][chan] )
		    {
		      pedEvents[slot][chan][0]++;
		      PedVal[slot][chan].PedL= 
			PedVal[slot][chan].PedL + value;
		      PedVal[slot][chan].SigL = 
			PedVal[slot][chan].SigL +
			value*value ;
		    }
		}
	      else
		{
		  if( value > adcminval[slot][chan] ||
		      value < adcmaxval[slot][chan] )
		    {
		      pedEvents[slot][chan][1]++;
		      PedVal[slot][chan].PedH= 
			PedVal[slot][chan].PedH + value;
		  PedVal[slot][chan].SigH = 
		    PedVal[slot][chan].SigH +
		    value*value ;
		    }
		}
	    }
	}
    }
}
/***************************************************************/
float emc_get_mean(int nev, float sum)
/***************************************************************/
{
  float mean;
  mean = -999.;
  if( nev>0)
    mean = sum/nev;
  return mean;
}
/***************************************************************/
float emc_get_rms(int nev, float mean, float sum2)
/***************************************************************/
{
 float rms,meda2;
 rms = -999.;
 if( nev>0 || mean != -999. )
   meda2  = sum2/nev;
   rms = sqrtf( abs(meda2-mean*mean));
   return rms;
}
/***************************************************************/

/**********************************************************************/
int EmcClosePeds(char *pedout)
/*********************************************************************
  It assumes a global matrix of structures for Pedestals
 *********************************************************************/
{
  int slot,chan,chain,crate,daqpoi,prognum,status;
  float med_all_2;
  float ped2_med;
  FILE* UsedFile;

  printf("Opening Output file for Pedestals pedout");
  UsedFile = fopen(pedout,"w");
  if( UsedFile == NULL )
    {
      printf(" EmcInitPeds> Failure opening PEDs file \n");
      return -1;
    }
  chain = 1;
  crate = 3;
  prognum = 0;
  for (slot=0; slot<NumberOfSlots; slot++)
    {
      for(chan=0; chan<NumberOfChannels; chan++)
	{
	  int nev;
	  float sum,sum2;
	  float mean,rms;
	  if( pedEvents[slot][chan][0]>0 )
	    {
	      nev = pedEvents[slot][chan][0];
	      sum = PedVal[slot][chan].PedL;
	      sum2= PedVal[slot][chan].SigL;
	      mean = emc_get_mean(nev,sum);
	      rms  = emc_get_rms (nev,mean,sum2);
	      PedVal[slot][chan].PedL = mean;
	      PedVal[slot][chan].SigL = rms;
	    }
	  if( pedEvents[slot][chan][1]>0)
	    {
	      nev = pedEvents[slot][chan][1];
	      sum = PedVal[slot][chan].PedH;
	      sum2= PedVal[slot][chan].SigH;
	      mean = emc_get_mean(nev,sum);
	      rms  = emc_get_rms (nev,mean,sum2);
	      PedVal[slot][chan].PedH = mean;
	      PedVal[slot][chan].SigH = rms;
	    }
	  prognum++;
	  daqpoi = 0;
	  status = DaqPoiUpk(&daqpoi,&chain,&crate,&slot,&chan);
	  fprintf(UsedFile,"%d %d %f %f %f %f %f %f",prognum,daqpoi,
	     PedVal[slot][chan].PedL,PedVal[slot][chan].SigL,PedVal[slot][chan].ChiL,
	     PedVal[slot][chan].PedH,PedVal[slot][chan].SigH,PedVal[slot][chan].ChiH);
	}
    }
  fclose(UsedFile);
  return 0;
}

int EmcInitMonitor(char *MonFile)
{
  printf(" Not Yet Implemented");
  return 0;
}
int EmcMakeMonitor(int celelen, CELEStru *CaloEv)
{
  printf(" Not Yet Implemented");
  return 0;
}

int EmcCloseMonitor(char *MonFile)
{
  printf(" Not Yet Implemented");
  return 0;
}
/*-----------------------------------------------------*/

int EmcInitPeds(void)
{     
  int slot,chan;
  for (slot=0; slot<NumberOfSlots; slot++)
    {
      for(chan=0; chan<NumberOfChannels; chan++)
	{
	  PedVal[slot][chan].PedL = 0;
	  PedVal[slot][chan].SigL = 0;
	  PedVal[slot][chan].ChiL = 0;
	  PedVal[slot][chan].PedH = 0;
	  PedVal[slot][chan].SigH = 0;
	  PedVal[slot][chan].ChiH = 0;
	}
    }
  return 0;
}

/* =====================================================================
    Unpack DAQ Pointer to get chain, crate, slot and channel number
   ===================================================================== */
int DaqPoiUpk(int *DaqPoi, int *Chain, int *Crate, int *Slot, int *Chan)
{
  int ChainMax = 1;
  int CratePerChain =  8;
  int BoardPerCrate = 16;
  int ChanPerBoard  = 30;
  int ChanPerCrate, Pointer;
  int CrateNum, SlotNum, ChanNum;

  Pointer = *DaqPoi;
  ChanPerCrate = BoardPerCrate*ChanPerBoard;
  CrateNum = (Pointer-1)/ChanPerCrate + 1;
  SlotNum  = (Pointer-1-(CrateNum-1)*ChanPerCrate) / ChanPerBoard;
  ChanNum  =  Pointer-1-(CrateNum-1)*ChanPerCrate-ChanPerBoard*SlotNum;

  *Chain = ChainMax;
  *Crate = CrateNum;
  *Slot  = SlotNum;
  *Chan  = ChanNum;

  return 0;
}

/* =====================================================================
    Read PEDs value from "pedfile"
   ===================================================================== */
int EmcReadPeds(char *PedFile)
{
  int   ILoop, Status;
  int   SerNum, DaqPoi;
  int   Chain, Crate, Slot, Chan;
  float PedValL, SigmaL, ChiSqL, PedValH, SigmaH, ChiSqH;
  FILE* UsedFile;

  printf(" ReadPeds> Opening PEDs File %s \n",PedFile);
  UsedFile = fopen(PedFile,"r");
  if( UsedFile == NULL )
    {
      printf(" ReadPeds> Failure opening PEDs file \n");
      return -1;
    }

  ILoop = 0;
  while ( ILoop < 480 )
    {
      fscanf(UsedFile,"%d %d %f %f %f %f %f %f",&SerNum,&DaqPoi,
	     &PedValL,&SigmaL,&ChiSqL,&PedValH,&SigmaH,&ChiSqH);
      if ( PedValH > 4096 )
	{
	  DaqPoi  =  480 + ILoop;
	  PedValH =  PedValH - 4095;
	}

      Chain = 0;
      Crate = 0;
      Slot  = 0;
      Chan  = 0;

      Status = DaqPoiUpk(&DaqPoi,&Chain,&Crate,&Slot,&Chan);
      if ( Status != 0 )
	{
	  printf(" ReadPeds> Unable unpacking daqpointer %d \n",DaqPoi);
	  return -1;
	}

      PedVal[Slot][Chan].DPoi = DaqPoi;
      PedVal[Slot][Chan].PedL = PedValL;
      PedVal[Slot][Chan].SigL = SigmaL;
      PedVal[Slot][Chan].ChiL = ChiSqL;
      PedVal[Slot][Chan].PedH = PedValH;
      PedVal[Slot][Chan].SigH = SigmaH;
      PedVal[Slot][Chan].ChiH = ChiSqH;

      ILoop++;
    }

  fclose(UsedFile);
  return 0;
}

/* =====================================================================
    Create CELE-like event from RAW data
   ===================================================================== */
int CELEMaker(int *NWords, int *YBDataPoi, CELEStru *CaloEv, int *CELELen)
{
  int   ILoop, Status, NVal;
  int   YBosData, JLoop, *CELEAdr;
  int   FstVal, DataFlg, NumEle, CELEPoi;
  int   CELEAddress, CELE_Id, CELExist;
  float Ped, Sig, ADCVal, TDCVal;
  rockm_l2frame Frame;
  DMAP_CHAN_EL El2Cal;

  NVal = *NWords;
  FstVal = *YBDataPoi;

  NumEle = 0;
  CELEAdr = (int* )calloc(NVal,sizeof(int));

  for ( ILoop=0; ILoop<NVal; ILoop++ )
    {
      YBosData = (unsigned int) (YBDataPoi[ILoop]); 
      rockm_l2frame_raw2l2frame(YBosData,Frame);
      Frame.chan &= 31;
      if (Frame.chain == 0 ) Frame.chain = 1;
      Status = 
	dmap_get(Frame.chain,Frame.crate,Frame.slot,Frame.chan,&El2Cal);

      if ( Status != DMAP_ERROR_OK )
	{
	  printf("CELEMaker> Wrong DMAP association \n");
	  return -1;
	}
      else
	{
	  DataFlg = 0;
	  ADCVal = 0.;
	  TDCVal = 0.;
	  switch ( El2Cal.btype )
	    {
	    case DMAP_BTYPE_CALADC:
	      if ( Frame.res == 0 )
		{
		  Ped = PedVal[Frame.slot][Frame.chan].PedL;
		  Sig = PedVal[Frame.slot][Frame.chan].SigL;
		}
	      else
		{
		  Ped = PedVal[Frame.slot][Frame.chan].PedH;
		  Sig = PedVal[Frame.slot][Frame.chan].SigH;
		}
	      if ( Sig > 20. ) Sig=20.;
	      ADCVal = Frame.data - Ped;
	      if ( abs(ADCVal) > 3*Sig ) DataFlg = 1;
	      break;
	    case DMAP_BTYPE_CALTDC:
	      if ( Frame.data < 4095 ) DataFlg = 1;
              TDCVal = Frame.data;
	      break;
	    default:
	      printf("CELEMaker> Invalid BTYPE for CALR %d \n",El2Cal.btype);
	      return 0;
	    }
	}

      if( DataFlg==1 )
	{
	  CELE_Id  = 0;
	  CELExist = 0;
	  CELEAddress = El2Cal.data.cal.detector    | 
	                El2Cal.data.cal.module << 8 | 
	                El2Cal.data.cal.plane  <<16 | 
	                El2Cal.data.cal.column <<24;

	  for ( JLoop=0; JLoop<NumEle; JLoop++ )
	    {
	      if ( CELEAdr[JLoop] == CELEAddress )
		{
		  CELExist = 1;
		  CELE_Id  = JLoop;
		}
	    }

	  if ( CELExist != 1 )
	    {
	      CELEPoi = NumEle;
	      CELEAdr[CELEPoi] = CELEAddress;
	      CaloEv[CELEPoi].Det = El2Cal.data.cal.detector;
	      CaloEv[CELEPoi].Wed = El2Cal.data.cal.module;
	      CaloEv[CELEPoi].Col = El2Cal.data.cal.column;
	      CaloEv[CELEPoi].Pla = El2Cal.data.cal.plane;
	      NumEle = NumEle + 1;
	    }
	  else
	    CELEPoi = CELE_Id;

	  switch ( El2Cal.btype )
	    {
	    case DMAP_BTYPE_CALADC:
	      if ( El2Cal.data.cal.side == 1 ) CaloEv[CELEPoi].Ea = ADCVal;
	      if ( El2Cal.data.cal.side == 2 ) CaloEv[CELEPoi].Eb = ADCVal;
	      break;
	    case DMAP_BTYPE_CALTDC:
	      if ( El2Cal.data.cal.side == 1 ) CaloEv[CELEPoi].Ta = TDCVal;
	      if ( El2Cal.data.cal.side == 2 ) CaloEv[CELEPoi].Tb = TDCVal;
	      break;
	    default: ;
	    }
	}

    }

  *CELELen = NumEle;

  free (CELEAdr);
  return 0;
}