/* ======================================================================== *\
!
! *
! * 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
!
!
\* ======================================================================== */

/////////////////////////////////////////////////////////////////////////////
//                                                               
// MHCalibrationChargeCam                                               
//                                                               
// Contains a list of MHCalibrationPix 
// plus the MHCalibrationBlindPix 
// and the  MHCalibrationPINDiode
/////////////////////////////////////////////////////////////////////////////
#include "MHCalibrationChargeCam.h"

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

#include "MParList.h"

#include "MHCalibrationChargeHiGainPix.h"
#include "MHCalibrationChargeLoGainPix.h"

#include "MCalibrationChargeCam.h"
#include "MCalibrationChargePix.h"

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

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

ClassImp(MHCalibrationChargeCam);

using namespace std;

const Float_t MHCalibrationChargeCam::fgNumHiGainSaturationLimit = 0.01;
const Float_t MHCalibrationChargeCam::fgNumLoGainSaturationLimit = 0.01;
//
//
//
MHCalibrationChargeCam::MHCalibrationChargeCam(const char *name, const char *title)
{
    fName  = name  ? name  : "MHCalibrationChargeCam";
    fTitle = title ? title : "";

    fHiGainArray = new TObjArray;
    fHiGainArray->SetOwner();
    
    fLoGainArray = new TObjArray;
    fLoGainArray->SetOwner();

    SetNumHiGainSaturationLimit();
    SetNumLoGainSaturationLimit();
}

// --------------------------------------------------------------------------
//
// Delete the TClonesArray of MHCalibrationChargePix containers
// Delete the MHCalibrationPINDiode and the MHCalibrationBlindPix
//
// Delete the histograms if they exist
//
MHCalibrationChargeCam::~MHCalibrationChargeCam()
{
  delete fHiGainArray;
  delete fLoGainArray;
}

// --------------------------------------------------------------------------
//
// Get i-th pixel (pixel number)
//
MHCalibrationChargeHiGainPix &MHCalibrationChargeCam::operator[](UInt_t i)
{
  return *static_cast<MHCalibrationChargeHiGainPix*>(fHiGainArray->UncheckedAt(i));
}

// --------------------------------------------------------------------------
//
// Get i-th pixel (pixel number)
//
const MHCalibrationChargeHiGainPix &MHCalibrationChargeCam::operator[](UInt_t i) const
{
  return *static_cast<MHCalibrationChargeHiGainPix*>(fHiGainArray->UncheckedAt(i));
}

// --------------------------------------------------------------------------
//
// Get i-th pixel (pixel number)
//
MHCalibrationChargeLoGainPix &MHCalibrationChargeCam::operator()(UInt_t i)
{
  return *static_cast<MHCalibrationChargeLoGainPix*>(fLoGainArray->UncheckedAt(i));
}

// --------------------------------------------------------------------------
//
// Get i-th pixel (pixel number)
//
const MHCalibrationChargeLoGainPix &MHCalibrationChargeCam::operator()(UInt_t i) const
{
  return *static_cast<MHCalibrationChargeLoGainPix*>(fLoGainArray->UncheckedAt(i));
}

// --------------------------------------------------------------------------
//
// Our own clone function is necessary since root 3.01/06 or Mars 0.4
// I don't know the reason
//
TObject *MHCalibrationChargeCam::Clone(const char *) const
{
    const Int_t nhi = fHiGainArray->GetSize();
    const Int_t nlo = fLoGainArray->GetSize();

    //
    // FIXME, this might be done faster and more elegant, by direct copy.
    //
    MHCalibrationChargeCam *cam = new MHCalibrationChargeCam;

    cam->fHiGainArray->Expand(nhi);
    cam->fLoGainArray->Expand(nlo);

    for (int i=0; i<nhi; i++)
    {
        delete (*cam->fHiGainArray)[i];
        (*cam->fHiGainArray)[i] = (*fHiGainArray)[i]->Clone();
    }
    for (int i=0; i<nlo; i++)
    {
        delete (*cam->fLoGainArray)[i];
        (*cam->fLoGainArray)[i] = (*fLoGainArray)[i]->Clone();
    }
    return cam;
}

// --------------------------------------------------------------------------
//
// To setup the object we get the number of pixels from a MGeomCam object
// in the Parameter list.
//
Bool_t MHCalibrationChargeCam::SetupFill(const MParList *pList)
{
  
  fHiGainArray->Delete();
  fLoGainArray->Delete();
  return kTRUE;

}

Bool_t MHCalibrationChargeCam::ReInit(MParList *pList)
{

  fCam = (MCalibrationChargeCam*)pList->FindCreateObj("MCalibrationChargeCam");
  if (!fCam)
  {
      gLog << err << GetDescriptor() << ": ERROR: Could not find MCalibrationChargeCam ... aborting " << endl;
      return kFALSE;
  }

  fRawEvt = (MRawEvtData*)pList->FindObject("MRawEvtData");
  if (!fRawEvt)
  {
      gLog << err << dbginf << "MRawEvtData not found... aborting." << endl;
      return kFALSE;
  }

  MExtractedSignalCam *signal = (MExtractedSignalCam*)pList->FindObject("MExtractedSignalCam");
  if (!signal)
    {
      gLog << err << "No argument in MExtractedSignalCam::ReInit... abort." << endl;
      return kFALSE;
    }
  
  const Int_t n = signal->GetSize();

  fHiGainArray->Expand(n);
  
  for (Int_t i=0; i<n; i++)
  {
      (*fHiGainArray)[i] = new MHCalibrationChargeHiGainPix;
      (*this)[i].Init();
      (*this)[i].ChangeHistId(i);
      
  }

  fLoGainArray->Expand(n);
  
  for (Int_t i=0; i<n; i++)
  {
      (*fLoGainArray)[i] = new MHCalibrationChargeLoGainPix;
      (*this)(i).Init();
      (*this)(i).ChangeHistId(i);
  }
  
  return kTRUE;
}


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

  MExtractedSignalCam *signal = (MExtractedSignalCam*)par;
  if (!signal)
    {
      gLog << err << "No argument in MExtractedSignalCam::Fill... abort." << endl;
      return kFALSE;
    }
  
  const Int_t n = signal->GetSize();

  if (fHiGainArray->GetEntries() != n)
    {
      gLog << err << "ERROR - Size mismatch... abort." << endl;
      return kFALSE;
    }

  if (fLoGainArray->GetEntries() != n)
    {
      gLog << err << "ERROR - Size mismatch... abort." << endl;
      return kFALSE;
    }
  
  for (int i=0; i<n; i++)
    {
      const MExtractedSignalPix &pix = (*signal)[i];
      
      const Float_t sumhi  = pix.GetExtractedSignalHiGain();
      const Float_t sumlo  = pix.GetExtractedSignalLoGain();
      
      (*this)[i].FillHistAndArray(sumhi);
      (*this)(i).FillHistAndArray(sumlo);

      (*this)[i].SetSaturated((Int_t)pix.GetNumHiGainSaturated()); 
      (*this)(i).SetSaturated((Int_t)pix.GetNumLoGainSaturated()); 
    }

  MRawEvtPixelIter pixel(fRawEvt);
  
  while (pixel.Next())
    {
      
      const UInt_t pixid = pixel.GetPixelId();
      
      const Float_t timehi = (Float_t)pixel.GetIdxMaxHiGainSample();
      const Float_t timelo = (Float_t)pixel.GetIdxMaxLoGainSample();

      (*this)[pixid].FillAbsTime(timehi);
      (*this)(pixid).FillAbsTime(timelo);

    }

  return kTRUE;
}

// --------------------------------------------------------------------------
//
// 1) Return if the charge distribution is already succesfully fitted  
//    or if the histogram is empty
// 2) Fit the histograms with a Gaussian
// 3) In case of failure set the bit kFitted to false and take histogram means and RMS
// 4) Check for pickup noise
// 5) Check the fourier spectrum 
// 5) Retrieve the results and store them in this class
//
Bool_t MHCalibrationChargeCam::Finalize()
{
    
    for (int i=0; i<fHiGainArray->GetSize(); i++)
    {
     
	MHCalibrationChargeHiGainPix &pixhi = (*this)[i];
	MCalibrationChargePix        &pix   = (*fCam)[i];

        if (pixhi.IsEmpty())
            continue;

	if (pixhi.GetSaturated() > fNumHiGainSaturationLimit*pixhi.GetHGausHist()->GetEntries())
	{
	    pix.SetHiGainSaturation();
	    continue;
	}

        //
        // 2) Fit the Hi Gain histograms with a Gaussian
        //
	if (pixhi.FitGaus())
	    pix.SetHiGainFitted();
        //
        // 3) In case of failure set the bit kFitted to false and take histogram means and RMS
        //
	else if (pixhi.RepeatFit())
	    pix.SetHiGainFitted();
	else
	    pixhi.BypassFit();

	//
	// 4) Check for pickup
	//
	pixhi.CountPickup();

        //
        // 5) Check for oscillations
        // 
	pixhi.CreateFourierSpectrum();

	//
        // 6) Retrieve the results and store them in this class
	//
	pix.SetHiGainMeanCharge(     pixhi.GetMean()     );
	pix.SetHiGainMeanChargeErr(  pixhi.GetMeanErr()  );
	pix.SetHiGainSigmaCharge(    pixhi.GetSigma()    );
	pix.SetHiGainSigmaChargeErr( pixhi.GetSigmaErr() );
	pix.SetHiGainChargeProb    ( pixhi.GetProb()     );

	pix.SetAbsTimeMean         ( pixhi.GetAbsTimeMean());
	pix.SetAbsTimeRms          ( pixhi.GetAbsTimeRms() );

	pix.SetHiGainOscillating   ( !pixhi.IsFourierSpectrumOK() );
	pix.SetHiGainNumPickup     (  pixhi.GetPickup()           );
    }

    for (int i=0; i<fLoGainArray->GetSize(); i++)
    {
     
	MHCalibrationChargeLoGainPix &pixlo = (*this)(i);
	MCalibrationChargePix        &pix   = (*fCam)[i];

        if (pixlo.IsEmpty())
            continue;

	if (pixlo.GetSaturated() > fNumHiGainSaturationLimit*pixlo.GetHGausHist()->GetEntries())
	{
	    pix.SetLoGainSaturation();
	    continue;
	}
        //
        // 2) Fit the Lo Gain histograms with a Gaussian
        //
	if (pixlo.FitGaus())
	    pix.SetLoGainFitted();
        //
        // 3) In case of failure set the bit kFitted to false and take histogram means and RMS
        //
	else if (pixlo.RepeatFit())
	    pix.SetLoGainFitted();
	else
	    pixlo.BypassFit();

	//
	// 4) Check for pickup
	//
	pixlo.CountPickup();

        //
        // 5) Check for oscillations
        // 
	pixlo.CreateFourierSpectrum();

	//
        // 6) Retrieve the results and store them in this class
	//
	pix.SetLoGainMeanCharge(     pixlo.GetMean()     );
	pix.SetLoGainMeanChargeErr(  pixlo.GetMeanErr()  );
	pix.SetLoGainSigmaCharge(    pixlo.GetSigma()    );
	pix.SetLoGainSigmaChargeErr( pixlo.GetSigmaErr() );
	pix.SetLoGainChargeProb    ( pixlo.GetProb()     );

	if (pix.IsHiGainSaturation())
	{
	    pix.SetAbsTimeMean     ( pixlo.GetAbsTimeMean());
	    pix.SetAbsTimeRms      ( pixlo.GetAbsTimeRms() );
	}	    

	pix.SetLoGainOscillating   ( !pixlo.IsFourierSpectrumOK() );
	pix.SetLoGainNumPickup     (  pixlo.GetPickup()           );

    }

    return kTRUE;
}

Bool_t MHCalibrationChargeCam::GetPixelContent(Double_t &val, Int_t idx, const MGeomCam &cam, Int_t type) const
{
  return kTRUE;
}

// --------------------------------------------------------------------------
//
// What MHCamera needs in order to draw an individual pixel in the camera
//
void MHCalibrationChargeCam::DrawPixelContent(Int_t idx) const
{
  (*this)[idx].DrawClone();
}

