/* ======================================================================== *\
!
! *
! * 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, 04/2004 <mailto:markus@ifae.es>
!
!   Copyright: MAGIC Software Development, 2000-2004
!
!
\* ======================================================================== */

/////////////////////////////////////////////////////////////////////////////
//
//  MJExtractCalibTest
//
// If the flag SetDataCheckDisplay() is set, only the most important distributions
//  are displayed. 
// Otherwise, (default: SetNormalDisplay()), a good selection of plots is given
//
/////////////////////////////////////////////////////////////////////////////
#include "MJExtractCalibTest.h"

#include <TFile.h>
#include <TStyle.h>
#include <TCanvas.h>
#include <TSystem.h>

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

#include "MRunIter.h"
#include "MParList.h"
#include "MTaskList.h"
#include "MTaskEnv.h"
#include "MEvtLoop.h"

#include "MHCamera.h"

#include "MPedestalCam.h"
#include "MBadPixelsCam.h"
#include "MBadPixelsTreat.h"
#include "MBadPixelsCalc.h"
#include "MBadPixelsMerge.h"
#include "MCerPhotEvt.h"
#include "MArrivalTime.h"
#include "MCalibrationChargeCam.h"
#include "MCalibrationRelTimeCam.h"
#include "MCalibrationQECam.h"
#include "MCalibrationTestCam.h"
#include "MCalibrationTestCalc.h"
#include "MHCamEvent.h"
#include "MHCalibrationTestCam.h"

#include "MReadMarsFile.h"
#include "MGeomApply.h"
#include "MExtractTimeAndChargeSlidingWindow.h"
#include "MExtractor.h"
#include "MExtractTime.h"
#include "MExtractTimeFastSpline.h"
#include "MFCosmics.h"
#include "MContinue.h"
#include "MFillH.h"
#include "MCalibrateData.h"
#include "MCalibrateRelTimes.h"

#include "MTriggerPattern.h"
#include "MTriggerPatternDecode.h"
#include "MFTriggerPattern.h"

#include "MStatusDisplay.h"

ClassImp(MJExtractCalibTest);

using namespace std;
// --------------------------------------------------------------------------
//
// Default constructor. 
//
// Sets fUseCosmicsFilter to kTRUE, fRuns to 0, fExtractor to NULL, fTimeExtractor to NULL
// fDisplay to kNormalDisplay
//
MJExtractCalibTest::MJExtractCalibTest(const char *name, const char *title) 
    : fUseCosmicsFilter(kTRUE), fRuns(NULL), fExtractor(NULL), fTimeExtractor(NULL),
      fDisplayType(kNormalDisplay), fGeometry("MGeomCamMagic")
{
    fName  = name  ? name  : "MJExtractCalibTest";
    fTitle = title ? title : "Tool to extract, calibrate and test signals from a file";
}


void MJExtractCalibTest::DisplayResult(MParList &plist)
{
  if (!fDisplay)
    return;
  
  //
  // Update display
  //
  TString title = fDisplay->GetTitle();
  title += "--  Extraction-Calibration-Test ";
  title += fRuns->GetRunsAsString();
  title += "  --";
  fDisplay->SetTitle(title);

  //
  // Get container from list
  //
  MGeomCam  &geomcam = *(MGeomCam*) plist.FindObject("MGeomCam");
  MHCalibrationTestCam &testcam = *(MHCalibrationTestCam*)plist.FindObject("MHCalibrationTestCam");
  
  // Create histograms to display
  MHCamera disp1 (geomcam, "Test;Photons",           "Mean of calibrated Photons");
  MHCamera disp2 (geomcam, "Test;SigmaPhotons",      "Sigma of calibrated photons");
  MHCamera disp3 (geomcam, "Test;PhotonsPerArea",    "Equiv. Cherenkov Photons per Area");
  MHCamera disp4 (geomcam, "Test;SigmaPhotPerArea",  "Sigma equiv. Cher. Photons per Area");
  MHCamera disp5 (geomcam, "Test;Phot",              "Calibrated Photons");
  MHCamera disp6 (geomcam, "Test;PhotPerArea",       "Calibrated Photons per Area");
  MHCamera disp7 (geomcam, "Test;NotInterpolate",    "Not interpolated pixels");
  MHCamera disp8 (geomcam, "Test;DeviatingPhots",    "Deviating Number Photons");

  // Fitted charge means and sigmas
  disp1.SetCamContent(testcam,  0);
  disp1.SetCamError(  testcam,  1);
  disp2.SetCamContent(testcam,  2);
  disp2.SetCamError(  testcam,  3);
  disp3.SetCamContent(testcam,  7);
  disp3.SetCamError(  testcam,  8);
  disp4.SetCamContent(testcam,  9);
  disp4.SetCamError(  testcam,  10);

  disp5.SetCamContent(fTestCam,  0);
  disp5.SetCamError(  fTestCam,  1);
  disp6.SetCamContent(fTestCam,  2);
  disp6.SetCamError(  fTestCam,  3);
  disp7.SetCamError(  fTestCam,  4);

  disp8.SetCamError(  fBadPixels, 22);


  disp1.SetYTitle("Photons");
  disp2.SetYTitle("\\sigma_{phot}");
  disp3.SetYTitle("Photons per Area [mm^{-2}]");
  disp4.SetYTitle("\\sigma_{phot} per Area [mm^{-2}]");

  disp5.SetYTitle("Photons");
  disp6.SetYTitle("Photons per Area [mm^{-2}]");
  disp7.SetYTitle("[1]");
  disp8.SetYTitle("[1]");
  
  gStyle->SetOptStat(1111);
  gStyle->SetOptFit();

  if (fDisplayType == kNormalDisplay)
    {

      TCanvas &c = fDisplay->AddTab("TestCharges");
      c.Divide(4,4);
      
      disp1.CamDraw(c, 1, 4, 2, 1);
      disp2.CamDraw(c, 2, 4, 2, 1);        
      disp3.CamDraw(c, 3, 4, 1, 1);        
      disp4.CamDraw(c, 4, 4, 2, 1);        
    }
  
  TCanvas &c2 = fDisplay->AddTab("TestResult");
  c2.Divide(2,4);

  disp5.CamDraw(c2, 1, 2, 2, 1);
  disp6.CamDraw(c2, 2, 2, 2, 1);        

  TCanvas &c3 = fDisplay->AddTab("TestDefects");
  c3.Divide(2,2);

  disp7.CamDraw(c3, 1, 2, 0);
  disp8.CamDraw(c3, 2, 2, 0);        

  return;

}


void MJExtractCalibTest::DisplayResultT(MParList &plist)
{
  if (!fDisplay)
    return;
  
  //
  // Update display
  //
  TString title = fDisplay->GetTitle();
  title += "--  Extraction-Calibration-Test-Time";
  title += fRuns->GetRunsAsString();
  title += "  --";
  fDisplay->SetTitle(title);

  //
  // Get container from list
  //
  MGeomCam &geomcam = *(MGeomCam*)plist.FindObject("MGeomCam");
  
  // Create histograms to display
  MHCamera disp1 (geomcam, "Test;Arr.Times",           "Mean of calibrated Arr.Times");
  MHCamera disp2 (geomcam, "Test;SigmaArr.Times",      "Sigma of calibrated Arr.Times");

  // Fitted charge means and sigmas
  disp1.SetCamContent(fTestTimeCam,  0);
  disp1.SetCamError(  fTestTimeCam,  1);
  disp2.SetCamContent(fTestTimeCam,  2);
  disp2.SetCamError(  fTestTimeCam,  3);

  disp1.SetYTitle("Mean Arr.Times [FADC units]");
  disp2.SetYTitle("\\sigma_{t} [FADC units]");
  
  gStyle->SetOptStat(1111);
  gStyle->SetOptFit();

  TCanvas &c = fDisplay->AddTab("TestTimes");
  c.Divide(2,4);

  disp1.CamDraw(c, 1, 2,  5, 1);
  disp2.CamDraw(c, 2, 2,  5, 1);        

  return;

}


const char* MJExtractCalibTest::GetOutputFile() const
{

  if (fSequence.IsValid())
    return Form("%s/test%08d.root", (const char*)fPathOut, fSequence.GetSequence());
  
  if (!fRuns)
    return "";
  
  return Form("%s/%s-Test.root", (const char*)fPathOut, (const char*)fRuns->GetRunsAsFileName());
  
}


Bool_t MJExtractCalibTest::Process(MPedestalCam &pedcam, 
				   MCalibrationChargeCam &calcam, MCalibrationQECam &qecam, 
				   MCalibrationRelTimeCam &reltime, 
				   Byte_t filetype)
{
    // const TString fname = GetOutputFile();
  
//  if (gSystem->AccessPathName(fname, kFileExists))
    return ProcessFile(pedcam,calcam,qecam,reltime,filetype);
  
    // return kTRUE;
}

Bool_t MJExtractCalibTest::ProcessFile(MPedestalCam &pedcam, 
				       MCalibrationChargeCam &calcam, MCalibrationQECam &qecam, 
				       MCalibrationRelTimeCam &relcam,
				       Byte_t filetype)
{
  if (!fRuns)
    {
      *fLog << err << "No Runs choosen... abort." << endl;
      return kFALSE;
    }
  if (fRuns->GetNumRuns() != fRuns->GetNumEntries())
    {
      *fLog << err << "Number of files found doesn't match number of runs... abort." << endl;
      return kFALSE;
    }

  *fLog << inf;
  fLog->Separator(GetDescriptor());
  *fLog << "Calculate MExtractedSignalCam from Runs " << fRuns->GetRunsAsString() << endl;
  *fLog << endl;

  MBadPixelsCam badcam;
  
  // Setup Lists
  MParList plist;
  plist.AddToList(this); // take care of fDisplay!
  plist.AddToList(&fTestCam);
  plist.AddToList(&fTestTimeCam);
  plist.AddToList(&badcam);
  plist.AddToList(&pedcam);
  plist.AddToList(&calcam);
  plist.AddToList(&qecam);
  plist.AddToList(&relcam);

  MCerPhotEvt          cerphot;
  MPedPhotCam          pedphot;
  MHCalibrationTestCam testcam;

  plist.AddToList(&cerphot);
  plist.AddToList(&pedphot);
  plist.AddToList(&testcam);

  pedcam.SetName("MPedestalFundamental");
  
  MTaskList tlist;
  plist.AddToList(&tlist);

  // Setup Task-lists
  MReadMarsFile read("Events");
  read.DisableAutoScheme();
  static_cast<MRead&>(read).AddFiles(*fRuns);
  
  // Check for interleaved events
  MTriggerPatternDecode decode;
  MFTriggerPattern      fcalib;
  fcalib.DenyCalibration();
  MContinue conttp(&fcalib, "ContTrigPattern");

  MGeomApply            apply; // Only necessary to craete geometry
  apply.SetGeometry(fGeometry);  
  MBadPixelsMerge       merge(&fBadPixels);

  MExtractTimeAndChargeSlidingWindow extrsw;
  MExtractTimeFastSpline             extime;
  extime.SetPedestals(&pedcam);

  MTaskEnv taskenv1("ExtractSignal");
  MTaskEnv taskenv2("ExtractTime");

  if (fExtractor)
    { 
      fExtractor->SetPedestals(&pedcam);
      taskenv1.SetDefault(fExtractor);
    }
  if (fTimeExtractor)
    {
      fTimeExtractor->SetPedestals(&pedcam);
      taskenv2.SetDefault(fTimeExtractor);
    }
  else
    {
      extrsw.SetPedestals(&pedcam);
      extrsw.SetWindowSize(8,8);
      taskenv2.SetDefault(&extrsw);
      *fLog << warn << GetDescriptor() 
            << ": No extractor has been chosen, take default MExtractTimeAndChargeSlidingWindow " << endl;
    }

  MCalibrateData        photcalc;
  MCalibrateRelTimes    caltimes;
  if (filetype==3) // MC file
    {
      photcalc.SetCalibrationMode(MCalibrateData::kFfactor);
      photcalc.SetPedestalFlag(MCalibrateData::kRun);
      photcalc.AddPedestal("MPedestalCam", "MPedPhotFundamental");
    }
  else
    {
      photcalc.SetCalibrationMode(MCalibrateData::kFfactor);
      photcalc.AddPedestal("Fundamental");
      photcalc.SetPedestalFlag(MCalibrateData::kEvent);
      photcalc.SetSignalType(MCalibrateData::kPhot);
    }

  MBadPixelsCalc        badcalc;
  MBadPixelsTreat       badtreat;
  badtreat.SetProcessTimes(kFALSE);

  badcalc.SetNamePedPhotCam("MPedPhotFundamental");
  //badtreat.SetUseInterpolation();
  badtreat.AddNamePedPhotCam("MPedPhotFundamental");

  MCalibrationTestCalc  testcalc;

  if (!fSequence.IsValid())
    {
      testcalc.SetOutputPath(fPathOut);
      testcalc.SetOutputFile(Form("%s-TestCalibStat.txt",(const char*)fRuns->GetRunsAsFileName()));
    }
  
  MHCamEvent evt0(0,"Signal", "Un-Calibrated Signal;;S [FADC cnts]" );
  MHCamEvent evt1(3,"CalSig", "Calibrated and Interpolated Signal;;S [\\gamma]");
  MHCamEvent evt2(0,"Times" , "Arrival Time;;T [slice]");

  MFillH fill0(&evt0, "MExtractedSignalCam", "FillUncalibrated");
  MFillH fill1(&evt1, "MCerPhotEvt", "FillCalibrated");
  MFillH fill2(&evt2, "MArrivalTime","FillTimes");
  
  MFillH fillcam("MHCalibrationTestCam", "MCerPhotEvt");
  fillcam.SetNameTab("Test");
  MFillH filltme("MHCalibrationTestTimeCam", "MArrivalTime");
  filltme.SetNameTab("TestTime");

  MFCosmics cosmics;
  cosmics.SetNamePedestalCam("MPedestalFundamental");
  MContinue contcos(&cosmics,"ContCosmics");
  
  tlist.AddToList(&read);
  tlist.AddToList(&decode);
  tlist.AddToList(&apply);
  tlist.AddToList(&merge);
  tlist.AddToList(&conttp);
  tlist.AddToList(&taskenv1);
  if (!fExtractor->InheritsFrom("MExtractTimeAndCharge"))
    tlist.AddToList(&taskenv2);
  tlist.AddToList(&contcos);
  tlist.AddToList(&fill0);
  tlist.AddToList(&photcalc);
  tlist.AddToList(&caltimes);
  tlist.AddToList(&badcalc);
  tlist.AddToList(&badtreat);
  tlist.AddToList(&fill1);
  tlist.AddToList(&fill2);
  tlist.AddToList(&fillcam);
  tlist.AddToList(&filltme);
  tlist.AddToList(&testcalc);
  
  // Create and setup the eventloop
  MEvtLoop evtloop(fName);
  evtloop.SetParList(&plist);
  evtloop.SetDisplay(fDisplay);
  evtloop.SetLogStream(fLog);
  
  // Execute first analysis
  if (!evtloop.Eventloop())
    {
      *fLog << err << GetDescriptor() << ": Failed." << endl;
      return kFALSE;
    }
  
  tlist.PrintStatistics();
  
  DisplayResult(plist);

  if (!WriteResult())
    return kFALSE;

  *fLog << inf << GetDescriptor() << ": Done." << endl;
  
  return kTRUE;
}

Bool_t MJExtractCalibTest::ReadPedPhotCam()
{

  const TString fname = GetOutputFile();
  
  if (gSystem->AccessPathName(fname, kFileExists))
    {
      *fLog << err << "Input file " << fname << " doesn't exist." << endl;
      return kFALSE;
    }
  
  *fLog << inf << "Reading from file: " << fname << endl;
  
  TFile file(fname, "READ");
  if (fPedPhotCam.Read()<=0)
    {
      *fLog << "Unable to read MPedPhotCam from " << fname << endl;
      return kFALSE;
    }

  if (file.FindKey("MBadPixelsCam"))
    {
      MBadPixelsCam bad;
      if (bad.Read()<=0)
        {
          *fLog << "Unable to read MBadPixelsCam from " << fname << endl;
          return kFALSE;
        }
      fBadPixels.Merge(bad);
    }
  
  if (fDisplay /*&& !fDisplay->GetCanvas("Pedestals")*/) // FIXME!
    fDisplay->Read();
  
  return kTRUE;
}

Bool_t MJExtractCalibTest::WriteResult()
{
    
    if (fPathOut.IsNull())
        return kTRUE;
    
    const TString oname(GetOutputFile());

    *fLog << inf << "Writing to file: " << oname << endl;
    
    TFile file(oname, "UPDATE");
    
    if (fDisplay && fDisplay->Write()<=0)
    {
        *fLog << err << "Unable to write MStatusDisplay to " << oname << endl;
        return kFALSE;
    }

    if (fPedPhotCam.Write()<=0)
    {
        *fLog << err << "Unable to write MPedPhotCam to " << oname << endl;
        return kFALSE;
    }

    if (fTestCam.Write()<=0)
    {
        *fLog << err << "Unable to write MCalibrationTestCam to " << oname << endl;
        return kFALSE;
    }

    if (fTestTimeCam.Write()<=0)
    {
        *fLog << err << "Unable to write MCalibrationTestCam to " << oname << endl;
        return kFALSE;
    }

    return kTRUE;

}



Bool_t MJExtractCalibTest::CheckEnv()
{
    if (HasEnv("DataCheckDisplay"))
      fDisplayType = GetEnv("DataCheckDisplay", kFALSE) ? kDataCheckDisplay : kNormalDisplay;

    SetOverwrite(GetEnv("Overwrite", fOverwrite));

    return MJob::CheckEnv();
}
