#ifndef MARS_MJCalibration
#define MARS_MJCalibration

#ifndef MARS_MJob
#include "MJob.h"
#endif
#ifndef MARS_MCalibrationIntensityChargeCam
#include "MCalibrationIntensityChargeCam.h"
#endif
#ifndef MARS_MCalibrationChargeCam
#include "MCalibrationChargeCam.h"
#endif
#ifndef MARS_MCalibrationIntensityBlindCam
#include "MCalibrationIntensityBlindCam.h"
#endif
#ifndef MARS_MCalibrationBlindCam
#include "MCalibrationBlindCam.h"
#endif
#ifndef MARS_MCalibrationChargePINDiode
#include "MCalibrationChargePINDiode.h"
#endif
#ifndef MARS_MCalibrationIntensityRelTimeCam
#include "MCalibrationIntensityRelTimeCam.h"
#endif
#ifndef MARS_MCalibrationRelTimeCam
#include "MCalibrationRelTimeCam.h"
#endif
#ifndef MARS_MCalibrationIntensityQECam
#include "MCalibrationIntensityQECam.h"
#endif
#ifndef MARS_MCalibrationQECam
#include "MCalibrationQECam.h"
#endif
#ifndef MARS_MBadPixelsIntensityCam
#include "MBadPixelsIntensityCam.h"
#endif
#ifndef MARS_MBadPixelsCam
#include "MBadPixelsCam.h"
#endif

class TH1D;

class MTask;
class MRunIter;
class MParList;
class MPedestalCam;
class MExtractor;
class MExtractTime;
class MEvtLoop;
class MExtractBlindPixel;
class MHCalibrationChargeBlindCam;
class MHCamera;

class MJCalibration : public MJob
{
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 gkSpecialPixelsContInstallation;   //! Run number upon which third 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 Double_t fgArrivalTimeMin;               //! Histogram minimum for arrival times 
  static const Double_t fgArrivalTimeMax;               //! Histogram maximum for arrival times
  
  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
  static const Float_t  fgRefArrivalTimeInner;          //! Reference value for the arrival time - inner
  static const Float_t  fgRefArrivalTimeOuter;          //! Reference value for the arrival time - outer
  static const Float_t  fgRefArrivalTimeRmsInner;       //! Reference value for the arrival time - inner
  static const Float_t  fgRefArrivalTimeRmsOuter;       //! Reference value for the arrival time - outer

  MRunIter       *fRuns;                                // Calibration files
  
  MExtractor     *fExtractor;                           // Signal extractor
  MExtractTime   *fTimeExtractor;                       // Arrival Time extractor
  
  MBadPixelsIntensityCam          fIntensBadCam;        // Bad Pixels cam, can be set from previous runs
  MBadPixelsCam                   fBadPixels;           // Bad Pixels cam, can be set from previous runs  
  MCalibrationIntensityChargeCam  fIntensCalibCam;      // Calibration conversion factors FADC2Phe for diff. intens.
  MCalibrationChargeCam           fCalibrationCam;      // Calibration conversion factors FADC2Phe  
  MCalibrationIntensityBlindCam   fIntensBlindCam;      // Calibration from Blind Pixel(s)
  MCalibrationBlindCam            fCalibrationBlindCam; // Calibration from Blind Pixel(s)  
  MCalibrationChargePINDiode      fCalibrationPINDiode; // Calibration from PIN Diode
  MCalibrationIntensityQECam      fIntensQECam;         // Quantum efficiency for diff. intensities, 
  MCalibrationQECam               fQECam;               // Quantum efficiency, can be set from previous runs
  MCalibrationIntensityRelTimeCam fIntensRelTimeCam;    // Calibration constants rel. times for diff. intensities
  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

  enum  Storage_t                                       // Possible devices for calibration
    {
      kNoStorage,
      kHistsStorage
    }; 
  
  Byte_t fStorage;                                     // Bit-field for chosen storage type

  enum { kRelTimes, kDataCheck, kDebug, kIntensity };  // Possible flags

  Byte_t fFlags;                                       // Bit-field for the flags
  
  TString fGeometry;                                   // Class name geometry
  
  Bool_t IsUseBlindPixel() const { return TESTBIT(fDevices,kUseBlindPixel); }
  Bool_t IsUsePINDiode  () const { return TESTBIT(fDevices,kUsePINDiode);   }

  Bool_t IsRelTimes     () const { return TESTBIT(fFlags,kRelTimes);  }
  Bool_t IsDataCheck    () const { return TESTBIT(fFlags,kDataCheck); }
  Bool_t IsDebug        () const { return TESTBIT(fFlags,kDebug);     }
  Bool_t IsIntensity    () const { return TESTBIT(fFlags,kIntensity); }

  Bool_t IsNoStorage    () const { return TESTBIT(fStorage,kNoStorage);    }
  Bool_t IsHistsStorage () const { return TESTBIT(fStorage,kHistsStorage); }
  
  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   ( TObject *geom );
  Bool_t WriteHists    ( MParList &plist );
  Bool_t WriteEventloop( MEvtLoop &evtloop   ) const;
  Bool_t WriteTasks    ( MTask *t1, MTask *t2) const;
  Bool_t CheckEnvLocal ();
  
  // WORKAROUNDS!!!
  void   InitBlindPixel(MExtractBlindPixel &blindext,
                        MHCalibrationChargeBlindCam &blindcam);
  
public:
  MJCalibration(const char *name=NULL, const char *title=NULL);
  
  const char* GetOutputFile() const;
  
  MCalibrationIntensityChargeCam  &GetIntensCalibrationCam() { return fIntensCalibCam;   }
  MCalibrationIntensityRelTimeCam &GetIntensRelTimeCam()     { return fIntensRelTimeCam; }
  MCalibrationIntensityQECam      &GetIntensQECam()          { return fIntensQECam;      }
  MBadPixelsIntensityCam          &GetIntensBadCam()         { return fIntensBadCam;     }  

  MCalibrationChargeCam           &GetCalibrationCam()       { return fCalibrationCam;   }
  MCalibrationRelTimeCam          &GetRelTimeCam()           { return fRelTimeCam;       }
  MCalibrationQECam               &GetQECam()                { return fQECam;            }
  MBadPixelsCam                   &GetBadPixels()            { return fBadPixels;        }
  
  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;         }
    
  // Displays
  void SetFullDisplay()      { fDisplayType = kFullDisplay;      }
  void SetDataCheckDisplay() { fDisplayType = kDataCheckDisplay; }
  void SetNormalDisplay()    { fDisplayType = kNormalDisplay;    }

  // Camera Geomtry
  void SetGeometry(const char *geom) { fGeometry = geom; }
  
  // Flags
  void SetRelTimeCalibration( const Bool_t b=kTRUE ) { b ? SETBIT(fFlags,kRelTimes)  : CLRBIT(fFlags,kRelTimes); }
  void SetDataCheck         ( const Bool_t b=kTRUE ) { b ? SetDataCheckDisplay(), SETBIT(fFlags,kDataCheck)
                                                                                     : CLRBIT(fFlags,kDataCheck); }
  void SetDebug             ( const Bool_t b=kTRUE ) { b ? SETBIT(fFlags,kDebug)     : CLRBIT(fFlags,kDebug); }
  void SetIntensity         ( const Bool_t b=kTRUE ) { b ? SETBIT(fFlags,kIntensity) : CLRBIT(fFlags,kIntensity); }
  
  // Devices
  void SetUseBlindPixel(const Bool_t b=kTRUE);
  void SetUsePINDiode(const Bool_t b=kTRUE);

  // Storage
  void SetNoStorage    ( const Bool_t b=kTRUE ) { b ? SETBIT(fStorage,kNoStorage)    : CLRBIT(fStorage,kNoStorage); }
  void SetHistsStorage ( const Bool_t b=kTRUE ) { b ? SETBIT(fStorage,kHistsStorage) : CLRBIT(fStorage,kHistsStorage); }
  
  // Precessing
  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
