/* ======================================================================== *\
!
! *
! * 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, 11/2003 <mailto:markus@ifae.es>
!
!   Copyright: MAGIC Software Development, 2000-2004
!
!
\* ======================================================================== */
/////////////////////////////////////////////////////////////////////////////
//
//  calibration.C
//
//  Needs as arguments the run number of a calibration file ("*_C_*.root") and 
//  the run number of the corresponding pedestal file ("*_P_*.root"). 
//
//  The TString inpath has to be set correctly.
//
//  The macro searches for the pulser colour which corresponds to the calibration
//  run number. If the run number is smaller than 20000, pulser colour "CT1" 
//  is assumed, otherwise, it searches for the strings "green", "blue", "uv" or 
//  "ct1" in the filenames. If no colour or multiple colours are found, the 
//  execution is aborted.  
//
//  The container MBadPixelsCam is created and followed during the execution of the 
//  rest of the macro.
// 
//  A first loop over the pedestal file is performed using the class MJPedestal
//
//  The container MCalibrationQECam is created and followed during the execution of the 
//  rest of the macro.
//
//  A loop over the calibration files is performed using the class MJCalibration. 
//  The results are displayed using the MJCalibration::SetNormalDisplay() facility, 
//  but other displays can easily be uncommented. 
//  The call to MJCalibration skips the relative time calibration, which can be 
//  uncommented as well. 
// 
//  Last, a third loop is performed over the calibration file again in order to 
//  "calibrate" it and test the resulting outcome.
//
//////////////////////////////////////////////////////////////////////////////////////////
#include <TStyle.h>
#include <TObject.h>
#include <TObjectTable.h>
#include <TCanvas.h>
#include <TPad.h>
#include <TH1.h>
#include <TPaveStats.h>
#include <TApplication.h>
#include <TClass.h>

#include "getExtractor.C"

#include "MJPedestal.h"
#include "MJCalibration.h"
#include "MJExtractCalibTest.h"
#include "MJCalibrateSignalFromOutside.h"
#include "MRunIter.h"
#include "MStatusDisplay.h"
#include "MCalibrationQECamMagic.h"
#include "MCalibrationQECam.h"
#include "MBadPixelsCam.h"
#include "MArgs.h"
#include "MArray.h"
#include "MParContainer.h"
#include "MGeomCamMagic.h"

using namespace std;

static TString outpath = "./";
static TString inpath  = "/home/rootdata/Calib/2004_09_22";
static TString badfile;
//static TString badfile = "badpixels_only0_388_559.dat";
//
// the default pedestal run for the calibration
//
static const Int_t   pedrun  = 38995;
//
// the default start calibration run 
//
static const Int_t   calrun1 = 38997;
//
// the default last calibration run (if 0, only one run is taken, otherwise consecutive runs 
// between calrun1 and calrun2)
//
static  const Int_t calrun2 = 0;
//
// the default start data run 
//
static const Int_t   datrun1 = 39000;
//
// the default last calibration run (if 0, only one run is taken, otherwise consecutive runs 
// between calrun1 and calrun2)
//
static  const Int_t datrun2 = 0;
//
// A switch to output debugging information about Objects use
//
static Bool_t debug = kFALSE;
//
// A switch to use the Blind Pixel
//
static Bool_t blindpix = kTRUE;
//
// A switch to use the PIN Diode 
//
static Bool_t pindiode = kTRUE;
//
// Tell if you want to use the display:
//
static Bool_t useDisplay = kTRUE;
//
// Tell if you want to test the result afterwards
//
static Bool_t useTest = kFALSE;
//
// Tell if you want to test the intensity calibration
//
static Bool_t useIntensity  = kFALSE;
//
// Tell if you want to store and read the F0 and F1- files
//
static Bool_t useStorage   = kTRUE;
//
// Tell which extractor you want to use. The flags are counted according 
// to the extractor-TDAS
// 
static Int_t extractorflag = 33;
//
Int_t calibration(const UInt_t extflag=extractorflag, const Int_t prun=pedrun, 
                  const Int_t crun1=calrun1, const Int_t crun2=calrun2, 
                  const Int_t drun1=datrun1, const Int_t drun2=datrun2)
{

  MExtractor *extractor = getExtractor(extflag==33 ? 32 : extflag);

  if (!extractor)
    return 99;

  extractor->SetName(Form("%s_Run_%05d",extractor->GetName(),prun));
  const Bool_t timeandcharge = extractor->InheritsFrom("MExtractTimeAndCharge");

  gStyle->SetOptStat(1111);
  gStyle->SetOptFit();
  gStyle->SetTitleSize(0.35,"u");
  gStyle->SetTitleFontSize(0.9);
  gStyle->SetTitleH(0.12);
  gStyle->SetTitleW(0.95);
  gStyle->SetLineWidth(1);

  if (debug)
    TObject::SetObjectStat(kTRUE);

  MRunIter pruns;
  MRunIter cruns;

  pruns.AddRun(prun,inpath);

  if (crun1!=0)
    if (crun2==0)
      cruns.AddRun(crun1,inpath);
    else
      cruns.AddRuns(crun1,crun2,inpath);

  MStatusDisplay *display = NULL;

  if (useDisplay)
    {
      display = new MStatusDisplay;
      display->SetUpdateTime(3000);
      display->Resize(850,700);
    }
  
  /*****************************************/
  /* FIRST LOOP: PURE PEDESTAL COMPUTATION */
  /*****************************************/
  //
  // Hand over to the jobs a QE Cam with Cornings initialized
  // 
  MGeomCamMagic          geomcam;
  MCalibrationQECamMagic qecam;
  MBadPixelsCam          badcam;
  badcam.InitSize(geomcam.GetNumPixels());
  //
  // If you want to exclude pixels from the beginning, read 
  // an ascii-file with the corr. pixel numbers (see MBadPixelsCam)
  //
  if (!badfile.IsNull())
    {
      ifstream f(badfile.Data());
      badcam.AsciiRead((istream&)f);  
      f.close();
    }

  MJPedestal pedloop1;
  pedloop1.SetNoStorage(!useStorage);
  pedloop1.SetEnvDebug(debug);
  pedloop1.SetExtractor(extractor);
  pedloop1.SetInput(&pruns);
  pedloop1.SetPathOut(outpath.Data());
  if (useDisplay)
    {
      pedloop1.SetDisplay(display);
      pedloop1.SetDataCheckDisplay();
    }
  pedloop1.SetBadPixels(badcam);
  
  if (!pedloop1.Process())
    return 1;

  /****************************************/
  /* SECOND LOOP: CALIBRATION COMPUTATION */
  /****************************************/

  MJPedestal    pedloop2;
  MJCalibration calloop;

  if (timeandcharge)
    {
      /***********************************************************/
      /* NEEDED FOR SECOND LOOP: EXTRACTOR RESOLUTION COMPUTATION */
      /***********************************************************/
      
      pedloop2.SetUseData();
      pedloop2.SetNoStorage(!useStorage);
      pedloop2.SetEnvDebug(debug);
      pedloop2.SetExtractor(extractor);
      pedloop2.SetExtractorResolution();
      pedloop2.SetPedestals(pedloop1.GetPedestalCam());  
      pedloop2.SetInput(&pruns);
      pedloop2.SetPathOut(outpath.Data());
      if (useDisplay)
        {
          pedloop2.SetDisplay(display);
          pedloop2.SetDataCheckDisplay();
        }
      pedloop2.SetBadPixels(badcam);
      
      if (!pedloop2.Process())
        return 1;
      
      calloop.SetExtractorCam(pedloop2.GetPedestalCam());
    }

  if (crun1 == 0)
    return 0;

  MPedestalCam &pedcam = pedloop1.GetPedestalCam();

  if (debug)
    calloop.SetDebug();
  calloop.SetEnvDebug(debug);
  if (useIntensity)
    calloop.SetIntensity();
  //  calloop.SetHistsStorage();
  calloop.SetNoStorage(!useStorage);
  calloop.SetRelTimeCalibration(kTRUE);
  //
  // If you want to set a colour explicitely from outside (not recommanded!)
  //  calloop.SetColor(MCalibrationCam::kUV);
  //
  // If you want to run the data-check on RAW DATA!!!, choose:
  //  calloop.SetDataCheck();
  // 
  // If you want to see the data-check plots only, choose:
  calloop.SetDataCheckDisplay();
  //calloop.SetNormalDisplay();
  // 
  // For everything, you have ever dreamed of, choose:
  //  calloop.SetFullDisplay();

  //
  // If you want to calibrate the times as well, choose:
  //
  calloop.SetExtractor(extractor);
  calloop.SetInput(&cruns);
  calloop.SetPathOut(outpath.Data());
  if (useDisplay)
    calloop.SetDisplay(display);
  calloop.SetUseBlindPixel(blindpix);
  calloop.SetUsePINDiode(pindiode);
  calloop.SetQECam(qecam);
  calloop.SetBadPixels(badcam);

  if (!calloop.Process(pedcam))
    return 2;

  //
  // The next lines are the use the Print() function and have 
  // all the results as ascii-tables:
  //
  if (debug)
    {
      MCalibrationQECam     &nqecam      = calloop.GetQECam();
      MBadPixelsCam         &badbad      = calloop.GetBadPixels();
      MCalibrationChargeCam &chargecam   = calloop.GetCalibrationCam();
      chargecam.Print();
      nqecam.Print();
      badbad.Print();
    }

  /********************************************************************/
  /* THIRD LOOP: APPLY CALIBRATION TO THE CALIBRATION FILES FOR TESTS */
  /********************************************************************/

  if (useTest)
    {
      
      MJExtractCalibTest testloop;
      
      // If you want to see the data-check plots only, choose:
      testloop.SetDataCheckDisplay();

      testloop.SetExtractor(extractor);
      testloop.SetInput(&cruns);
      testloop.SetPathOut(outpath);
      if (useDisplay)
        testloop.SetDisplay(display);
      testloop.SetBadPixels(calloop.GetBadPixels());
      
      if (!testloop.ProcessD(pedloop1.GetPedestalCam(),calloop.GetCalibrationCam(),calloop.GetQECam()))
        return 3;
      
   }

  if (drun1 == 0)
    return 4;

  MRunIter druns;

  if (drun2==0)
    druns.AddRun(drun1,inpath);
  else
    druns.AddRuns(drun1,drun2,inpath);


  /********************************************************************/
  /* FOURTH LOOP: APPLY CALIBRATION TO THE PEDESTAL FILES             */
  /********************************************************************/

  if (extflag == 33)
    {
      delete extractor;
      extractor = getExtractor(28);
    }
  
  MJCalibrateSignalFromOutside calibloop;

  calibloop.SetExtractor(extractor);
  calibloop.SetInput(&druns);
  //  calibloop.SetOutputPath(outpath);
  if (useDisplay)
    calibloop.SetDisplay(display);
  calibloop.SetBadPixels(calloop.GetBadPixels());
  calibloop.SetNoStorage(!useStorage);
  
  if (!calibloop.ProcessFile(pedloop1.GetPedestalCam(),timeandcharge ? pedloop2.GetPedestalCam() : pedloop1.GetPedestalCam(), calloop.GetCalibrationCam(),calloop.GetQECam(), calloop.GetRelTimeCam()))
    return 5;

  if (debug)
    TObject::SetObjectStat(kFALSE);

  //
  // Debugging at the end:
  // 
  if (debug)
    gObjectTable->Print();


  return 0;
}

static void Usage()
{
    gLog << endl;
    gLog << "Usage:" << endl;
    gLog << endl;
    gLog << "   calibration [ped.run nr.] [first cal.run nr.] [last cal.run nr.] [first dat.run nr.] [last dat.run nr.]" << endl ;
    gLog << endl;
    gLog << "   ped.run.nr:        Run number of the pedestal file." << endl;
    gLog << "   first cal.run nr.: Run number of the first calibration file." << endl;
    gLog << "   last  cal.run nr.: Run number of the last  calibration file." << endl;
    gLog << "   first dat.run nr.: Run number of the first data file to be calibrated." << endl;
    gLog << "   last  dat.run nr.: Run number of the last  data file to be calibrated." << endl;
    gLog << endl;
    gLog << "All calibration runs between (first cal.run nr.) and (last cal.run nr.) will be used" << endl;
    gLog << "If last.cal.run.nr is 0 (default), only one calibration run is taken"                  << endl;
    gLog << endl;
    gLog << "All data runs between (first dat.run nr.) and (last dat.run nr.) will be used" << endl;
    gLog << "If last.dat.run.nr is 0 (default), only one data run is taken"                  << endl;
    gLog << endl;
    gLog << "Additional Options: " << endl;
    gLog << "     --extractor=#    Choose one of the following possible extractors (integer)" << endl;
    gLog << "                      (default: Nr. 33)                                        " << endl;
    gLog << endl;
    gLog << "    Nr.    Extractor   Parameters " << endl;
    gLog << endl;    
    gLog << "       MExtractFixedWindow: " << endl;
    gLog << "           with the following parameters: " << endl;
    gLog << "     1:           SetRange(4,7,6,9) " << endl;
    gLog << "     2:           SetRange(4,7,5,10)" << endl;
    gLog << "     3:           SetRange(3,8,5,10)" << endl;
    gLog << "     4:           SetRange(2,9,4,11)" << endl;
    gLog << "     5:           SetRange(0,13,4,13)" << endl;
    gLog << "       MExtractFixedWindowSpline: " << endl;
    gLog << "     6:           SetRange(3,7,5,9)" << endl;
    gLog << "     7:           SetRange(3,7,5,11)" << endl;
    gLog << "     8:           SetRange(3,9,5,11)" << endl;
    gLog << "     9:           SetRange(2,10,4,12)" << endl;
    gLog << "     10:          SetRange(0,14,4,14)" << endl;
    gLog << "       MExtractFixedWindowPeakSearch: " << endl;
    gLog << "                  SetRange(0,18,2,14) and the following parameters:" << endl;
    gLog << "     11:          SetWindows(2,2,2)" << endl;
    gLog << "                  SetOffsetFromWindow(0)" << endl;
    gLog << "     12:          SetWindows(4,4,2)" << endl;
    gLog << "                  SetOffsetFromWindow(1)" << endl;
    gLog << "     13:          SetWindows(4,6,4)" << endl;
    gLog << "                  SetOffsetFromWindow(0)" << endl;
    gLog << "     14:          SetWindows(6,6,4)" << endl;
    gLog << "                  SetOffsetFromWindow(1)" << endl;
    gLog << "     15:          SetWindows(8,8,4)" << endl;
    gLog << "                  SetOffsetFromWindow(1)" << endl;
    gLog << "     16:          SetWindows(14,10,4)" << endl;
    gLog << "                  SetOffsetFromWindow(2)" << endl;
    gLog << "      MExtractTimeAndChargeSlidingWindow:" << endl;
    gLog << "                  SetRange(0,18,2,14) and the following parameters:" << endl;
    gLog << "     17:          SetWindowSize(2,2)" << endl;
    gLog << "     18:          SetWindowSize(4,4)" << endl;
    gLog << "     19:          SetWindowSize(4,6)" << endl;
    gLog << "     20:          SetWindowSize(6,6)" << endl;
    gLog << "     21:          SetWindowSize(8,8)" << endl;
    gLog << "     22:          SetWindowSize(14,10)" << endl;
    gLog << "      MExtractTimeAndChargeSpline: " << endl; 
    gLog << "     23:          SetChargeType(MExtractTimeAndChargeSpline::kAmplitude) and:" << endl;
    gLog << "                  SetRange(0,13,2,13) " << endl; 
    gLog << "     24:          SetChargeType(MExtractTimeAndChargeSpline::kIntegral) and:" << endl;
    gLog << "                  SetRiseTime(0.5); SetFallTime(0.5)" << endl;
    gLog << "     25:          SetRiseTime(0.5); SetFallTime(1.5)" << endl;
    gLog << "     26:          SetRiseTime(1.0); SetFallTime(3.0)" << endl;
    gLog << "     27           SetRiseTime(1.5); SetFallTime(4.5)" << endl;
    gLog << "      MExtractTimeAndChargeDigitalFilter" << endl;
    gLog << "                  SetRange(0,18,2,14) and the following parameters:" << endl; 
    gLog << "     28:          SetNameWeightsFile('msignal/cosmics_weights.dat')" << endl;
    gLog << "     29:          SetNameWeightsFile('msignal/cosmics_weights4.dat')" << endl;
    gLog << "     30:          SetNameWeightsFile('msignal/cosmics_weights_logain6.dat')" << endl;
    gLog << "     31:          SetNameWeightsFile('msignal/cosmics_weights_logain4.dat')" << endl;
    gLog << "     32:          SetNameWeightsFile('msignal/calibration_weights_UV.dat')" << endl;
    gLog << "     33:          Use calibration weights for calibration events and cosmics weights for data events" << endl;
    gLog << endl;
    gLog << "     --inpath=#          Find the data in inpath"                      << endl;
    gLog << "     --outpath=#         Write the output containers to outpath"       << endl;
    gLog << "     --badfile=#         Use the file # to exclude pixels from the beginning" << endl;
    gLog << "     --debug             Use the TObjectTable for debugging    "       << endl;
    gLog << "                             and write out the pixels as ascii tables" << endl;
    gLog << "     --useTest           Use the class MJExtractCalibTest to test the calibration on itself" << endl;
    gLog << "     --skipBlindPix      Skip the blind pixel calibration"             << endl;
    gLog << "     --skipPINDiode      Skip the PIN Diode   calibration"             << endl;
}


int main(int argc, char **argv)
{


  MArray::Class()->IgnoreTObjectStreamer();
  MParContainer::Class()->IgnoreTObjectStreamer();
  //
  // Evaluate arguments
  //
  MArgs arg(argc, argv);
  
  if (arg.HasOnly("-?") || arg.HasOnly("-h") || arg.HasOnly("--help"))
    {
      Usage();
      return -1;
    }
  
  debug    = arg.HasOnlyAndRemove("--debug")    || arg.HasOnlyAndRemove("-d");
  useTest  = arg.HasOnlyAndRemove("--useTest")  || arg.HasOnlyAndRemove("-e");
  blindpix = !(arg.HasOnlyAndRemove("--skipBlindPix"));
  pindiode = !(arg.HasOnlyAndRemove("--skipPINDiode"));

  if (arg.HasOption("--extractor="))
    extractorflag = arg.GetIntAndRemove("--extractor=");

  if (arg.HasOption("--inpath="))
    inpath = arg.GetStringAndRemove("--inpath=");

  if (arg.HasOption("--outpath="))
    outpath = arg.GetStringAndRemove("--outpath=");

  if (arg.HasOption("--badfile="))
    badfile = arg.GetStringAndRemove("--badfile=");

  if (gSystem->AccessPathName(badfile,kFileExists))
  {
    gLog << "WARNING: the bad pixels file '" << badfile.Data() << "' doesn't exist." << endl;
    badfile = "";
  }

  // check for the right usage of the program
  //
  if (arg.GetNumArguments()>6)
    {
      Usage();
      return -1;
    }

  //
  // Initialize Non-GUI (batch) mode
  //
  gROOT->SetBatch();
  
  //
  // Switch off the display
  //
  useDisplay = kTRUE;
  //
  // check for the arguments
  //
  Int_t pedr  = 0;
  Int_t calr1 = 0;
  Int_t calr2 = 0;
  Int_t datr1 = 0;
  Int_t datr2 = 0;

  const Int_t nargs = arg.GetNumArguments();

  if (nargs>=5)
    {
      pedr = arg.GetArgumentInt(0);
      calr1 = arg.GetArgumentInt(1);
      calr2 = arg.GetArgumentInt(2);
      datr1 = arg.GetArgumentInt(3);
      datr2 = arg.GetArgumentInt(4);
      return calibration(extractorflag,pedr,calr1,calr2,datr1,datr2);
    }

  if (nargs>=4)
    {
      pedr = arg.GetArgumentInt(0);
      calr1 = arg.GetArgumentInt(1);
      calr2 = arg.GetArgumentInt(2);
      datr1 = arg.GetArgumentInt(3);
      datr2 = arg.GetArgumentInt(4);
      return calibration(extractorflag,pedr,calr1,calr2,datr1,0);
    }

  if (nargs>=3)
    {
      pedr = arg.GetArgumentInt(0);
      calr1 = arg.GetArgumentInt(1);
      calr2 = arg.GetArgumentInt(2);
      return calibration(extractorflag,pedr,calr1,calr2,0);
    }

  if (nargs>=2)
    {
      pedr = arg.GetArgumentInt(0);
      calr1 = arg.GetArgumentInt(1);
      gLog << "PEDR: " << pedr << " CALR1: " << calr1 << " CALR2 " << calr2 << endl;
      gLog << "inpath: " << inpath << endl;
      gLog << "extractor: " << extractorflag << endl;
      return calibration(extractorflag,pedr,calr1,0,0);
    }

  if (nargs>=1)
    {
      pedr = arg.GetArgumentInt(0);
      return calibration(extractorflag,pedr,0);
    }

  return calibration(extractorflag,pedr,calr1,calr2);
}

