/* ======================================================================== *\
!
! *
! * 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
!
!
\* ======================================================================== */
/////////////////////////////////////////////////////////////////////////////
//                                                               
// MHCalibrationChargeBlindCam                                               
//
// Histogram class for blind pixels in the camera. Incorporates the TObjArray's:
// - fBlindPixelsArray (for calibrated High Gains per pixel)
//
/////////////////////////////////////////////////////////////////////////////
#include "MHCalibrationChargeBlindCam.h"
#include "MHCalibrationChargeBlindPix.h"

#include <TVirtualPad.h>
#include <TCanvas.h>
#include <TPad.h>

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

#include "MCalibrationChargeBlindPix.h"
#include "MCalibrationChargeBlindCam.h"

#include "MExtractedSignalBlindPixel.h"

#include "MParList.h"

#include "MRawRunHeader.h"

ClassImp(MHCalibrationChargeBlindCam);

using namespace std;

// --------------------------------------------------------------------------
//
// Default Constructor. 
//
// Sets:
// - all pointers to NULL
//
// Initializes and sets owner of:
// - fBlindPixelsArray
// 
// Sets fFitFunc to kEPoisson4
//
MHCalibrationChargeBlindCam::MHCalibrationChargeBlindCam(const char *name, const char *title)
    :  fCam(NULL), fRunHeader(NULL)
{

  fName  = name  ? name  : "MHCalibrationChargeBlindCam";
  fTitle = title ? title : "Class to fille the blind pixel histograms";

  fBlindPixelsArray = new TObjArray;
  fBlindPixelsArray->SetOwner();

  fFitFunc = MHCalibrationChargeBlindPix::kEPoisson4;

}

// --------------------------------------------------------------------------
//
// Deletes the TClonesArray of:
// - fBlindPixelsArray
//
MHCalibrationChargeBlindCam::~MHCalibrationChargeBlindCam()
{
  delete fBlindPixelsArray;
}

// --------------------------------------------------------------------------
//
// Get i-th High Gain pixel (pixel number)
//
MHCalibrationChargeBlindPix &MHCalibrationChargeBlindCam::operator[](UInt_t i)
{
  return *static_cast<MHCalibrationChargeBlindPix*>(fBlindPixelsArray->UncheckedAt(i));
}

// --------------------------------------------------------------------------
//
// Get i-th High Gain pixel (pixel number)
//
const MHCalibrationChargeBlindPix &MHCalibrationChargeBlindCam::operator[](UInt_t i) const
{
  return *static_cast<MHCalibrationChargeBlindPix*>(fBlindPixelsArray->UncheckedAt(i));
}

// --------------------------------------------------------------------------
//
// Our own clone function is necessary since root 3.01/06 or Mars 0.4
// I don't know the reason. 
//
// Creates new MHCalibrationChargeBlindCam
// Deletes the TObjArray's and Clones them individually
//
TObject *MHCalibrationChargeBlindCam::Clone(const char *name) const
{

  const Int_t nhi   = fBlindPixelsArray->GetEntries();
  
  //
  // FIXME, this might be done faster and more elegant, by direct copy.
  //
  MHCalibrationChargeBlindCam *cam = new MHCalibrationChargeBlindCam();

  cam->fBlindPixelsArray->Expand(nhi);

  for (int i=0; i<nhi; i++)
    (*cam->fBlindPixelsArray)[i] = (*fBlindPixelsArray)[i]->Clone();

  return cam;
}

// --------------------------------------------------------------------------
//
// Gets the pointers to:
// - MRunHeader
// - MExtractedSignalBlindPix
//
// Calls Delete-Function of:
// - MHCalibrationChargeBlindCam::fBlindPixelsArray
//
Bool_t MHCalibrationChargeBlindCam::SetupFill(const MParList *pList)
{
  
  fRunHeader = (MRawRunHeader*)pList->FindObject("MRawRunHeader");
  if (!fRunHeader)
  {
    *fLog << warn << GetDescriptor() 
          << ": MRawRunHeader not found... will not store run numbers." << endl;
    return kFALSE;
  }

  fSignal  = (MExtractedSignalBlindPixel*)pList->FindObject("MExtractedSignalBlindPixel");
  if (!fSignal)
    {
      *fLog << err << "MExtractedSignalBlindPixel not found... aborting " << endl;
      return kFALSE;
    }

  return kTRUE;
}


// --------------------------------------------------------------------------
//
// Initializes, if empty to MExtractedSignalCam::GetSize() for:
// - MHCalibrationChargeBlindCam::fBlindPixelsArray
//
// Calls InitializeHists() for every entry in:
// - MHCalibrationChargeBlindCam::fBlindPixelsArray
//
// Retrieves the run numbers from MRawRunHeader and stores them in fRunNumbers
//
Bool_t MHCalibrationChargeBlindCam::ReInit(MParList *pList)
{

  const UInt_t nblindpixels  = fSignal->GetNumBlindPixels();

  Int_t runnr = 0;

  if (fRunHeader)
    runnr = fRunHeader->GetRunNumber();

  fCam = (MCalibrationChargeBlindCam*)pList->FindCreateObj("MCalibrationChargeBlindCam");
  if (!fCam)
    {
      *fLog << err << "Cannot find nor create MCalibrationChargeBlindCam ... abort." << endl;
      return kFALSE;
    }

  if (fCam->GetNumBlindPixels() != nblindpixels)
    {
        *fLog << err;
        *fLog << "Size mismatch in MCalibrationChargeBlindCam ... abort." << endl;
        *fLog << "  Size of MCalibrationChargeBlindCam: " << fCam->GetNumBlindPixels() << endl;
        *fLog << "  Size of MExtractedSignalBlindPixel: " << nblindpixels << endl;
      return kFALSE;
    }
  

  const Int_t samples = fSignal->GetNumFADCSamples();
  const Int_t integ   = fSignal->IsExtractionType( MExtractBlindPixel::kIntegral );

  if (fBlindPixelsArray->GetEntries()==0)
  {

      fBlindPixelsArray->Expand(nblindpixels);

      for (UInt_t i=0; i<nblindpixels; i++)
	{
	  (*fBlindPixelsArray)[i] = new MHCalibrationChargeBlindPix;
	  (*this)[i].ChangeHistId(i);
	  if (integ)
	    {
	      (*this)[i].SetLast( samples * integ * 
				  ((*this)[i].GetLast()+0.5) - 0.5 );
	      (*this)[i].SetSinglePheCut( samples * integ *
					  (*this)[i].GetSinglePheCut() );
	    }
	  (*this)[i].InitBins();
	  TH1F *h = (*this)[i].GetHGausHist();
	  h->SetTitle( Form("%s%s", h->GetTitle()," Runs: "));
          (*this)[i].ChangeFitFunc(fFitFunc);
	  (*this)[i].SetupFill(pList);
	  (*this)[i].SetCalibrationChargeBlindPix(&(*fCam)[i]);
      }
  }

  for (UInt_t i=0; i<nblindpixels; i++)
    {
      TH1F *h = (*this)[i].GetHGausHist();
      h->SetTitle( Form("%s%i%s", h->GetTitle(),runnr," "));
    }

  return kTRUE;
}


//--------------------------------------------------------------------------------
//
// Retrieves from MExtractedSignalBlindPixel:
// - number of blind pixels
//
// For all TObjArray's, the following steps are performed: 
//
// 1) Test size and return kFALSE if not matching
// 2) 
//
Bool_t MHCalibrationChargeBlindCam::Fill(const MParContainer *par, const Stat_t w)
{

  const Int_t nblindpixels  = fSignal->GetNumBlindPixels();
  
  if (GetSize() != nblindpixels)
    {
      gLog << err << "ERROR - Size mismatch... abort." << endl;
      return kFALSE;
    }
  
  for (Int_t i=0; i<nblindpixels; i++)
    (*this)[i].Fill(par,w);

  return kTRUE;
}

// --------------------------------------------------------------------------
//
// Calls the Finalize() function of the blind pixels
//
Bool_t MHCalibrationChargeBlindCam::Finalize()
{

  for (Int_t i=0; i<GetSize(); i++)
    if (!(*this)[i].Finalize())
      return kFALSE;

  return kTRUE;
}



// -----------------------------------------------------------------------------
// 
// Default draw:
//
// Displays the averaged areas, both High Gain and Low Gain 
//
// Calls the Draw of the fAverageHiGainAreas and fAverageLoGainAreas objects with options
//
void MHCalibrationChargeBlindCam::Draw(Option_t *opt)
{

  const Int_t size = fBlindPixelsArray->GetEntries();

  if (size == 0)
    return;
  
  TString option(opt);
  option.ToLower();

  TVirtualPad *pad = gPad ? gPad : MH::MakeDefCanvas(this);  
  pad->SetBorderMode(0);

  switch (size)
    {
    case 1: 
      break;
    case 2:
      pad->Divide(2,1);
      break;
    case 3:
    case 4:
      pad->Divide(2,2);
      break;
    default:
      pad->Divide(size/2+1,size/2+1);
      break;
    }

  for (Int_t i=0; i<size;i++) 
    {
      pad->cd(i+1);
      (*this)[i].Draw(option);
    }

  pad->Modified();
  pad->Update();

}
