/* ======================================================================== *\
!
! *
! * This file is part of MARS, the MAGIC Analysis and Reconstruction
! * Software. It is distributed to you in the hope that it can be a useful
! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
! * It is distributed WITHOUT ANY WARRANTY.
! *
! * Permission to use, copy, modify and distribute this software and its
! * documentation for any purpose is hereby granted without fee,
! * provided that the above copyright notice appear in all copies and
! * that both that copyright notice and this permission notice appear
! * in supporting documentation. It is provided "as is" without express
! * or implied warranty.
! *
!
!
!   Author(s): Markus Gaug, 05/2004 <mailto:markus@ifae.es>
!
!   Copyright: MAGIC Software Development, 2000-2004
!
!
\* ======================================================================== */

//////////////////////////////////////////////////////////////////////////////
//
//   MExtractTimeAndCharge
//
//   Base class for the signal extractors which extract the arrival time 
//   and the signal at the same time. Uses the functions 
//   FindTimeAndChargeHiGain() and FindTimeAndChargeLoGain() to extract the signal and 
//   substract the pedestal value.
//
//   The following figure gives and example of possible inheritance trees. 
//   An extractor class can inherit from each of the following base classes:
//    - MExtractor
//    - MExtractTime
//    - MExtractTimeAndCharge
//
//Begin_Html
/*
<img src="images/ExtractorClasses.gif">
*/
//End_Html
//
//   The following variables have to be set by the derived class and 
//   do not have defaults:
//   - fNumHiGainSamples
//   - fNumLoGainSamples
//   - fSqrtHiGainSamples
//   - fSqrtLoGainSamples
//
// Input Containers:
//   MRawEvtData
//   MRawRunHeader
//   MPedestalCam
//
// Output Containers:
//   MArrivalTimeCam
//   MExtractedSignalCam
//
//////////////////////////////////////////////////////////////////////////////
#include "MExtractTimeAndCharge.h"

#include <TRandom.h>

#include "MLog.h"
#include "MLogManip.h"

#include "MParList.h"

#include "MRawEvtData.h"
#include "MRawEvtPixelIter.h"
#include "MRawRunHeader.h"

#include "MPedestalCam.h"
#include "MPedestalPix.h"

#include "MArrivalTimeCam.h"
#include "MArrivalTimePix.h"

#include "MExtractedSignalCam.h"
#include "MExtractedSignalPix.h"

ClassImp(MExtractTimeAndCharge);

using namespace std;

const Float_t MExtractTimeAndCharge::fgLoGainStartShift = -3.5; 
const Byte_t  MExtractTimeAndCharge::fgLoGainSwitch = 120; 

// --------------------------------------------------------------------------
//
// Default constructor. 
//
// Sets: 
// - fLoGainFirstSave to 0
// - fWindowSizeHiGain and fWindowSizeLoGain to 0
// - fLoGainStartShift to fgLoGainStartShift+fgOffsetLoGain
// - fLoGainSwitch     to fgLoGainSwitch
//
MExtractTimeAndCharge::MExtractTimeAndCharge(const char *name, const char *title)
    : fLoGainFirstSave(0), fWindowSizeHiGain(0), fWindowSizeLoGain(0)
{
    fName  = name  ? name  : "MExtractTimeAndCharge";
    fTitle = title ? title : "Base class for signal and time extractors";

    SetLoGainStartShift();
    SetLoGainSwitch();
}

// --------------------------------------------------------------------------
//
// The PreProcess searches for the following input containers:
//  - MRawEvtData
//  - MRawRunHeader
//  - MPedestalCam
//
// The following output containers are also searched and created if
// they were not found:
//
//  - MExtractedSignalCam
//  - MArrivalTimeCam    
//
Int_t MExtractTimeAndCharge::PreProcess(MParList *pList)
{

  if (!MExtractTime::PreProcess(pList))
    return kFALSE;
  
  fSignals = (MExtractedSignalCam*)pList->FindCreateObj("MExtractedSignalCam",AddSerialNumber(fNameSignalCam));
  if (!fSignals)
      return kFALSE;

  *fLog << flush << inf;
  return kTRUE;
}

// --------------------------------------------------------------------------
//
// The ReInit calls:
// -  MExtractor::ReInit()
//
// Call: 
// - MArrivalTimeCam::SetUsedFADCSlices(fHiGainFirst, fHiGainLast, fNumHiGainSamples,
//                                      fLoGainFirst, fLoGainLast, fNumLoGainSamples);
// - InitArrays();
//
Bool_t MExtractTimeAndCharge::ReInit(MParList *pList)
{

  if (!MExtractTime::ReInit(pList))
    return kFALSE;

  if (!InitArrays())
    return kFALSE;
  
  if (fSignals)
    fSignals->SetUsedFADCSlices(fHiGainFirst, fHiGainLast+fHiLoLast, fNumHiGainSamples,
                                fLoGainFirst, fLoGainLast, fNumLoGainSamples);


  return kTRUE;
}

// --------------------------------------------------------------------------
//
// Calculate the integral of the FADC time slices and store them as a new
// pixel in the MArrivalTimeCam container.
// Calculate the integral of the FADC time slices and store them as a new
// pixel in the MExtractedSignalCam container. 
// The functions FindTimeAndChargeHiGain() and FindTimeAndChargeLoGain() are 
// supposed to extract the signal themselves.
//
Int_t MExtractTimeAndCharge::Process()
{

  MRawEvtPixelIter pixel(fRawEvt);

  while (pixel.Next())
  {
      // COPY HERE PRODUCING ARRAY WITH SAMPLES

      /*
       const MPedestalPix  &ped = (*fPedestals)[pixidx];

       const Float_t pedes  = ped.GetPedestal();
       const Float_t ABoffs = ped.GetPedestalABoffset();

       const Float_t pedmean[2] = { pedes + ABoffs, pedes - ABoffs };

       const Int_t num = pixel.GetNumHiGainSamples()+pixel.GetNumLoGainSamples();

       MArrayF sample(num);

       const Int_t abflag = pixel.HasABFlag() ? 1 : 0;
       const Int_t ids0 = fHiGainFirst + abflag;

       Int_t ids  = ids0;

       Float_t  null   = 0;      // Starting value for maxpos
       Float_t *maxpos = &null;  // Position of maximum
       Float_t *sat1   = 0;      // First saturating slice
       Float_t *sat2   = 0;      // Last saturating slice

       const Float_t *beg = sample.GetArray();
       const Float_t *end = beg + num;

       Float_t *ptr = beg;
       while (ptr<end)
       {
          *sample++ = (Float_t)*ptr - pedmean[ids++&0x1];

          // This extraction must be done independant for lo- and hi-gain
          // if (*ptr > *maxpos)
          //     maxpos = ptr;
          //
          // if (*ptr >= fSaturationLimit)
          // {
          //    sat2 = ptr;
          //    if (!sat1)
          //       sat1 = ptr;
          // }
          //
          // ptr++;
        }
       */

      //
      // Find signal in hi- and lo-gain
      //
      Float_t sumhi =0., deltasumhi =0; // Set hi-gain of MExtractedSignalPix valid
      Float_t timehi=0., deltatimehi=0; // Set hi-gain of MArrivalTimePix valid
      Byte_t sathi=0;

      // Initialize fMaxBinContent for the case, it gets not set by the derived class
      fMaxBinContent = fLoGainSwitch + 1; 

      const Int_t pixidx = pixel.GetPixelId();
      const MPedestalPix  &ped = (*fPedestals)[pixidx];
      const Bool_t higainabflag = pixel.HasABFlag();

      FindTimeAndChargeHiGain(pixel.GetHiGainSamples()+fHiGainFirst, pixel.GetLoGainSamples(), 
                              sumhi, deltasumhi, timehi, deltatimehi,
                              sathi, ped, higainabflag);

      // Make sure that in cases the time couldn't be correctly determined
      // more meaningfull default values are assigned
      if (timehi<=0 || timehi>pixel.GetNumHiGainSamples())
          timehi = gRandom->Uniform(pixel.GetNumHiGainSamples());
      
      Float_t sumlo =0., deltasumlo =-1.; // invalidate logain of MExtractedSignalPix
      Float_t timelo=0., deltatimelo=-1;  // invalidate logain of MArrivalTimePix
      Byte_t satlo=0;
      
      //
      // Adapt the low-gain extraction range from the obtained high-gain time
      //

      // IN THIS CASE THE PIXEL SHOULD BE MARKED BAD!!!!
      // MEANS: Hi gain has saturated, but it is too early to extract
      // the lo-gain properly
      // THIS produces pulse positions ~= -1
      // The signal might be handled in MCalibrateData, but hwat's about
      // the arrival times in MCalibrateRelTime
      if (sathi && fMaxBinContent<=fLoGainSwitch)
          deltasumlo=deltasumhi=deltatimelo=deltatimehi=-1;

      // FIXME: What to do with the pixel if it saturates too early???
      if (pixel.HasLoGain() && (fMaxBinContent > fLoGainSwitch /*|| sathi>0*/) )
      {
          fLoGainFirstSave = fLoGainFirst;

          // sathi is the number (not index!) of the first saturating slice
          // 0 indicates that no saturation was found
          // FIMXME: Is 3 an accurate value?

          const Float_t pos = sathi==0 ? timehi : (int)(sathi)-3;

          if (pos+fLoGainStartShift>0)
              fLoGainFirst = (Byte_t)(pos + fLoGainStartShift);

          if (fLoGainFirst<fLoGainFirstSave)
              fLoGainFirst = fLoGainFirstSave;

          if (fLoGainLast-fLoGainFirst >= fWindowSizeLoGain)
          {
              deltasumlo  = 0; // make logain of MExtractedSignalPix valid
              deltatimelo = 0; // make logain of MArrivalTimePix valid

              const Bool_t logainabflag = (higainabflag + pixel.GetNumHiGainSamples()) & 0x1;
              FindTimeAndChargeLoGain(pixel.GetLoGainSamples()+fLoGainFirst,
                                      sumlo, deltasumlo, timelo, deltatimelo,
                                      satlo, ped, logainabflag);

              if (satlo>6)
                  deltasumlo=deltatimelo=-1;
          }
          fLoGainFirst = fLoGainFirstSave;

          // Make sure that in cases the time couldn't be correctly determined
          // more meaningfull default values are assigned
          if (timelo<=0 || timelo>pixel.GetNumLoGainSamples())
              timelo = gRandom->Uniform(pixel.GetNumLoGainSamples());
      }

      MExtractedSignalPix &pix = (*fSignals)[pixidx];
      MArrivalTimePix     &tix = (*fArrTime)[pixidx];
      pix.SetExtractedSignal(sumhi, deltasumhi, sumlo, deltasumlo);
      pix.SetGainSaturation(sathi, satlo);

      tix.SetArrivalTime(timehi, deltatimehi, timelo-fOffsetLoGain, deltatimelo);
      tix.SetGainSaturation(sathi, satlo);
 
    } /* while (pixel.Next()) */

  fArrTime->SetReadyToSave();
  fSignals->SetReadyToSave();

  return kTRUE;
}

// --------------------------------------------------------------------------
//
// In addition to the resources of the base-class MExtractor:
//   MJPedestal.MExtractor.LoGainStartShift: -2.8
//
Int_t MExtractTimeAndCharge::ReadEnv(const TEnv &env, TString prefix, Bool_t print)
{

    Bool_t rc = MExtractTime::ReadEnv(env, prefix, print);

    if (rc)
      SetLoGainStartShift();

    if (IsEnvDefined(env, prefix, "LoGainStartShift", print))
    {
        fLoGainStartShift = GetEnvValue(env, prefix, "LoGainStartShift", fgLoGainStartShift);
	SetLoGainStartShift(fLoGainStartShift);
        rc = kTRUE;
    }

    if (IsEnvDefined(env, prefix, "LoGainSwitch", print))
    {
        fLoGainSwitch = GetEnvValue(env, prefix, "LoGainSwitch", fLoGainSwitch);
        rc = kTRUE;
    }

    return rc;
}

void MExtractTimeAndCharge::Print(Option_t *o) const
{
  if (IsA()==Class())
    *fLog << GetDescriptor() << ":" << endl;

  *fLog << dec;
  *fLog << " Taking " << fWindowSizeHiGain
        << " HiGain samples from slice " << (Int_t)fHiGainFirst
        << " to " << (Int_t)(fHiGainLast+fHiLoLast) << " incl" << endl;
  *fLog << " Taking " << fWindowSizeLoGain
        << " LoGain samples from slice " << (Int_t)fLoGainFirst
        << " to " << (Int_t)fLoGainLast << " incl" << endl;
  
  *fLog << " LoGainStartShift:   " << fLoGainStartShift << endl;
  *fLog << " LoGainSwitch:       " << (Int_t)fLoGainSwitch << endl;
  MExtractTime::Print(o);
}
