/* ======================================================================== *\
!
! *
! * 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 "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 = -4.0; 
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())
    {
      //
      // 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 = -1;
      if (timehi>pixel.GetNumHiGainSamples())
          timehi = 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
      //
      if (pixel.HasLoGain() && (fMaxBinContent > fLoGainSwitch) )
      {
          deltasumlo  = 0; // make logain of MExtractedSignalPix valid
          deltatimelo = 0; // make logain of MArrivalTimePix valid

          fLoGainFirstSave = fLoGainFirst;
          const Byte_t logainstart = sathi 
            ? (sathi > (Int_t)(-fLoGainStartShift) ? sathi + (Int_t)fLoGainStartShift : fLoGainFirst)
            : (timehi+fLoGainStartShift > 0. ? (Byte_t)(timehi + fLoGainStartShift) : fLoGainFirst);
          fLoGainFirst = logainstart > fLoGainFirstSave ? logainstart : fLoGainFirstSave;

          // *fLog << err << (Int_t)fLoGainFirst << endl;

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

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

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

      tix.SetArrivalTime(timehi, deltatimehi, timelo-fOffsetLoGain, deltatimelo);
      tix.SetGainSaturation(sathi, 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);
}
