#ifndef MARS_MJCalibration
#define MARS_MJCalibration

#ifndef MARS_MCalibrationChargeCam
#include "MCalibrationChargeCam.h"
#endif
#ifndef MARS_MCalibrationChargeBlindCam
#include "MCalibrationChargeBlindCam.h"
#endif
#ifndef MARS_MCalibrationChargePINDiode
#include "MCalibrationChargePINDiode.h"
#endif
#ifndef MARS_MCalibrationRelTimeCam
#include "MCalibrationRelTimeCam.h"
#endif
#ifndef MARS_MCalibrationQECam
#include "MCalibrationQECam.h"
#endif
#ifndef MARS_MBadPixelsCam
#include "MBadPixelsCam.h"
#endif

class TEnv;
class MRunIter;
class MSequence;
class MParList;
class MPedestalCam;
class MExtractor;
class MExtractTime;

class MExtractBlindPixel;
class MHCalibrationChargeBlindCam;
class MHCamera;
class TH1D;
class MJCalibration : public MParContainer
{
private:

  static const Int_t gkIFAEBoxInaugurationRun;         //! Run number of first IFAE box calibration
  static const Int_t gkSecondBlindPixelInstallation;   //! Run number upon which second blind pixel was installed
  static const Int_t gkThirdBlindPixelInstallation;    //! Run number upon which third blind pixel was installed

  static const Double_t fgConvFADC2PheMin;             //! Histogram minimum for conversion factor to phes
  static const Double_t fgConvFADC2PheMax;             //! Histogram maximum for conversion factor to phes
  static const Double_t fgConvFADC2PhotMin;            //! Histogram minimum for conversion factor to phs
  static const Double_t fgConvFADC2PhotMax;            //! Histogram maixmum for conversion factor to phs
  static const Double_t fgQEMin;                       //! Histogram minimum for quantum efficiency
  static const Double_t fgQEMax;                       //! Histogram maximum for quantum efficiency
  
  static const Float_t  fgRefConvFADC2PheInner;        //! Reference value for the conversion factor to phes - inner
  static const Float_t  fgRefConvFADC2PheOuter;        //! Reference value for the conversion factor to phes - outer
  static const Float_t  fgRefConvFADC2PhotInner;       //! Reference value for the conversion factor to phs - inner
  static const Float_t  fgRefConvFADC2PhotOuter;       //! Reference value for the conversion factor to phs - outer
  static const Float_t  fgRefQEInner;                  //! Reference value for the quantum eff. cascades - inner
  static const Float_t  fgRefQEOuter;                  //! Reference value for the quantum eff. cascades - outer

  TString fOutputPath;                                 // Path to the output files
  
  TEnv           *fEnv;                                // Input setup-file
  MRunIter       *fRuns;                               // Calibration files
  MSequence      *fSequence;                           // Sequence
  
  MExtractor     *fExtractor;                          // Signal extractor
  MExtractTime   *fTimeExtractor;                      // Arrival Time extractor
  
  MBadPixelsCam              fBadPixels;               // Bad Pixels cam, can be set from previous runs
  MCalibrationChargeCam      fCalibrationCam;          // Calibration conversion factors FADC2Phe
  MCalibrationChargeBlindCam fCalibrationBlindCam;     // Calibration from Blind Pixel(s)
  MCalibrationChargePINDiode fCalibrationPINDiode;     // Calibration from PIN Diode
  MCalibrationQECam          fQECam;                   // Quantum efficiency, can be set from previous runs
  MCalibrationRelTimeCam     fRelTimeCam;              // Calibration constants rel. times
  
  MCalibrationCam::PulserColor_t fColor;               // Colour of the pulsed LEDs
  
  enum  Display_t                                      // Possible Display types
    {
      kFullDisplay,
      kDataCheckDisplay,
      kNormalDisplay
    };
  
  Display_t fDisplayType;                              // Chosen Display type
  
  enum  Device_t                                       // Possible devices for calibration
    {
      kUseBlindPixel,
      kUsePINDiode
    }; 
  
  Byte_t fDevices;                                     // Bit-field for used devices for calibration
  
  Bool_t fRelTimes;                                    // Flag if relative times have to be calibrated
  Bool_t fDataCheck;                                   // Flag if the data check is run on raw data
  Bool_t fDebug;                                       // Flag if debug option is passed onto cal. classes
  
  void   DisplayResult        ( MParList &plist );
  void   DisplayReferenceLines( MHCamera *cam , const Int_t what                           ) const;
  void   DisplayOutliers      ( TH1D     *hist, const char* whatsmall, const char* whatbig ) const;
  void   DisplayDoubleProject ( MHCamera *cam , const char* whatsmall, const char* whatbig ) const;
  const  Int_t CountBadPixels ( MHCamera *cam , const Int_t what                           ) const;
  void   FixDataCheckHist     ( TH1D     *hist  ) const;
  
  Bool_t WriteResult();
  void   CheckEnv();
  
  // WORKAROUNDS!!!
  Bool_t FindColor();
  void   InitBlindPixel(MExtractBlindPixel &blindext,
                        MHCalibrationChargeBlindCam &blindcam);
  
public:

  MJCalibration(const char *name=NULL, const char *title=NULL);
  ~MJCalibration();
  
  const char* GetOutputFile() const;
  void SetEnv(const char *env);
  
  MCalibrationChargeCam  &GetCalibrationCam() { return fCalibrationCam; }
  MCalibrationRelTimeCam &GetRelTimeCam()     { return fRelTimeCam;     }
  MCalibrationQECam      &GetQECam()          { return fQECam;          }
  MBadPixelsCam          &GetBadPixels()      { return fBadPixels;      }
  
  Bool_t IsUseBlindPixel() const;
  Bool_t IsUsePINDiode()   const;
  
  void SetBadPixels(const MBadPixelsCam &bad)    { bad.Copy(fBadPixels);   }
  void SetExtractor(MExtractor* ext)             { fExtractor = ext; }
  void SetTimeExtractor(MExtractTime* ext)       { fTimeExtractor = ext; }
  void SetQECam(const MCalibrationQECam &qe)     { qe.Copy(fQECam);        }
  void SetColor(const MCalibrationCam::PulserColor_t color) { fColor = color; }
  
  void SetInput(MRunIter *iter) { fRuns=iter; }
  void SetSequence(MSequence *seq) { fSequence=seq; }
  void SetOutputPath(const char *path=".");
  
  // Displays
  void SetFullDisplay()      { fDisplayType = kFullDisplay;      }
  void SetDataCheckDisplay() { fDisplayType = kDataCheckDisplay; }
  void SetNormalDisplay()    { fDisplayType = kNormalDisplay;    }
  
  // Rel. Time
  void SetRelTimeCalibration(const Bool_t b=kTRUE) { fRelTimes = b; }
  
  // Data Check
  void SetDataCheck(const Bool_t b=kTRUE) { fDataCheck = b; SetDataCheckDisplay(); }
  
  // Debug
  void SetDebug(const Bool_t b=kTRUE) { fDebug = b; }
  
  // Devices
  void SetUseBlindPixel(const Bool_t b=kTRUE);
  void SetUsePINDiode(const Bool_t b=kTRUE);
  
  Bool_t ReadCalibrationCam();
  Bool_t ProcessFile(MPedestalCam &pedcam);
  Bool_t Process(MPedestalCam &pedcam);
  
  ClassDef(MJCalibration, 0) // Tool to run a calibration per pulser colour and intensity
};

#endif
