/* ======================================================================== *\
!
! *
! * 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   02/2004 <mailto:markus@ifae.es>
!              
!   Copyright: MAGIC Software Development, 2000-2004
!
!
\* ======================================================================== */

/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// MHCalibrationRelTimeCam                                                            //
//                                                                         //
// Hold the CalibrationRelTime information for all pixels in the camera              //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
#include "MHCalibrationRelTimeCam.h"

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

#include "MParList.h"

#include "MHCalibrationRelTimePix.h"

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

ClassImp(MHCalibrationRelTimeCam);

using namespace std;
const Float_t MHCalibrationRelTimeCam::fgTimeSliceWidth  = 3.3;
const Int_t   MHCalibrationRelTimeCam::fgPulserFrequency = 500;
// --------------------------------------------------------------------------
//
// Default constructor. Creates a MHCalibrationRelTimePix object for each pixel
//
MHCalibrationRelTimeCam::MHCalibrationRelTimeCam(const char *name, const char *title) 
{

  fName  = name  ? name  : "MHCalibrationRelTimeCam";
  fTitle = title ? title : "Histogram container for the relative time calibration of the camera";
  
  //
  // loop over all Pixels and create two histograms
  // one for the Low and one for the High gain
  // connect all the histogram with the container fHist
  //
  fArray = new TObjArray;
  fArray->SetOwner();
  

  SetTimeSliceWidth();
  SetPulserFrequency();
}

// --------------------------------------------------------------------------
//
// Delete the array conatining the pixel pedest information
//
MHCalibrationRelTimeCam::~MHCalibrationRelTimeCam()
{
  delete fArray;
}

// --------------------------------------
//
void MHCalibrationRelTimeCam::Clear(Option_t *o)
{

  fArray->ForEach(TObject, Clear)();
  return;
}

// --------------------------------------------------------------------------
//
// Get i-th pixel (pixel number)
//
MHCalibrationRelTimePix &MHCalibrationRelTimeCam::operator[](UInt_t i)
{
  return *(MHCalibrationRelTimePix*)(fArray->At(i));
}

// --------------------------------------------------------------------------
//
// Get i-th pixel (pixel number)
//
const MHCalibrationRelTimePix &MHCalibrationRelTimeCam::operator[](UInt_t i) const
{
  return *(MHCalibrationRelTimePix*)(fArray->At(i));
}


// --------------------------------------------------------------------------
//
// Our own clone function is necessary since root 3.01/06 or Mars 0.4
// I don't know the reason
//
TObject *MHCalibrationRelTimeCam::Clone(const char *) const
{

  const Int_t n = fArray->GetSize();
  
  //
  // FIXME, this might be done faster and more elegant, by direct copy.
  //
  MHCalibrationRelTimeCam *cam = new MHCalibrationRelTimeCam;
  
  cam->fArray->Expand(n);
  
  for (int i=0; i<n; i++)
    {
      delete (*cam->fArray)[i];
      (*cam->fArray)[i] = (*fArray)[i]->Clone();
    }

  return cam;
}



// --------------------------------------------------------------------------
//
//
Bool_t MHCalibrationRelTimeCam::SetupFill(const MParList *pList)
{

  return kTRUE;

}

// --------------------------------------------------------------------------
//
Bool_t MHCalibrationRelTimeCam::Fill(const MParContainer *par, const Stat_t w)
{

  MArrivalTimeCam *arrtime = (MArrivalTimeCam*)par;
  if (!arrtime)
    {
      gLog << err << "No argument in MArrivalTime::Fill... abort." << endl;
      return kFALSE;
    }
  
  const Int_t n = arrtime->GetSize();
  
  if (fArray->GetEntries()==0)
    {
      fArray->Expand(n);
      
      for (Int_t i=0; i<n; i++)
        {
          (*fArray)[i] = new MHCalibrationRelTimePix;
          MHCalibrationRelTimePix &hist = (*this)[i];
          hist.ChangeHistId(i);
          hist.Init();
          hist.SetEventFrequency(fPulserFrequency);
        }
    }
  
  if (fArray->GetEntries() != n)
    {
      gLog << err << "ERROR - Size mismatch... abort." << endl;
      return kFALSE;
    }
  
  const MArrivalTimePix &refpix = (*arrtime)[1];
  const Float_t reftime = refpix.IsLoGainUsed() ? refpix.GetArrivalTimeLoGain() : refpix.GetArrivalTimeHiGain();

  for (Int_t i=0; i<n; i++)
    {
	const MArrivalTimePix &pix = (*arrtime)[i];
	const Float_t time = pix.IsLoGainUsed() ? pix.GetArrivalTimeLoGain() : pix.GetArrivalTimeHiGain();  

	MHCalibrationRelTimePix  &hist = (*this)[i];
	hist.FillHistAndArray(time - reftime);
    }
  
  return kTRUE;
}

Bool_t MHCalibrationRelTimeCam::Finalize()
{

  for (Int_t i=0; i<fArray->GetSize(); i++)
    {
      
      MHCalibrationRelTimePix &hist = (*this)[i];
      
      //
      // 1) Return if the charge distribution is already succesfully fitted
      //    or if the histogram is empty
      //
      if (hist.IsGausFitOK() || hist.IsEmpty())
        continue;
      
      //
      // 2) Fit the Hi Gain histograms with a Gaussian
      //
      hist.FitGaus();
      
      //
      // 3) If fit does not succeed , bypass the fit and take the histogram means and sigmas
      //
      if (!hist.IsGausFitOK())
        hist.BypassFit();
      
      //
      // 4) Create the fourier transform of the arrays 
      //
      hist.CreateFourierSpectrum();
      
      //
      // 5) Renormalize to the real time in ns.
      //
      hist.Renorm(fTimeSliceWidth);
      
    }
  return kTRUE;
}

// --------------------------------------------------------------------------
//
// The types are as follows:
// 
// Fitted values:
// ============== 
//
// 0: Fitted Mean Relative Arrival Time
// 1: Error of fitted Mean Relative Arrival Time
// 2: Sigma of fitted Relative Arrival Time
// 3: Error of Sigma of fitted Relative Arrival Time
//
//
// Useful variables derived from the fit results:
// =============================================
//
// 4: Returned probability of Gauss fit to Charge distribution
//
// Localized defects:
// ==================
//
// 5: Gaus fit not OK
// 6: Fourier spectrum not OK
//
Bool_t MHCalibrationRelTimeCam::GetPixelContent(Double_t &val, Int_t idx, const MGeomCam &cam, Int_t type) const
{

  if (fArray->GetSize() <= idx)
    return kFALSE;

  switch (type)
    {
    case 0:
      val = (*this)[idx].GetMean();
      break;
    case 1:
      val = (*this)[idx].GetMeanErr();
      break;
    case 2:
      val = (*this)[idx].GetSigma();
      break;
    case 3:
      val = (*this)[idx].GetSigmaErr();
      break;
    case 4:
      val = (*this)[idx].GetProb();
      break;
    case 5:
      if (!(*this)[idx].IsGausFitOK())
        val = 1.;
      break;
    case 6:
      if (!(*this)[idx].IsFourierSpectrumOK())
        val = 1.;
      break;
    default:
      return kFALSE;
    }
  return kTRUE;
}

void MHCalibrationRelTimeCam::DrawPixelContent(Int_t idx) const
{
  (*this)[idx].DrawClone();
}
