#include <TROOT.h>
#include <TSystem.h>
#include <TChain.h>
#include <TFile.h>
#include <TString.h>
#include <iostream>

// #define MONITORS

//
// Constants for analyses
//
// event information
const int   mRefMult[2] = {0, 10000};
const float mImpPar[2]  = {0., 666.};


// track information
const double mChargedKaonMass = 0.493677;
const double mChargedPionMass = 0.13957018;
const int mPairChPionType = 7;
const int mPdgChargedPion = 211;
const int mPdgChargedKaon = 321;
const int mPosCharge = +1;
const int mNegCharge = -1;
const int mTrackType = 1;

const double mTrackMom[2]       = {0.15, 1.55};  //General momentum of the track [GeV/c]
const double mTrackTransvMom[2] = {0.15, 1.55};  //General transverse momentum of the track [GeV/c]
const double mTrackEta[2]       = { -1., 1.};

// pair information
const double mPairKt[2] = {0.,1.5};      //Pair transverse momentum cut
const double mPairQinv[2] = {0., 0.8};   //Pair relative momentum

// histograms values
const int    mQinvBins = 40;                 //10 MeV/c^2 bin width
const double mQinvVal[2] = {0., 0.4};        //Redefine for each analysis

const char *defaultInFile  = "O97Dst.root";
const char *defaultOutFile = "oSimPionO97Dst_1D.root";

//_________________
void simPionHBT_O97Dst_1D(const Char_t *inFileList = defaultInFile,
			  const Char_t *oFileName = defaultOutFile) {

  std::cout << "********************************" << std::endl
	    << "*     simPionHBT_O97Dst_1D     *" << std::endl
	    << "*            Start             *" << std::endl
	    << "********************************" << std::endl;

  //
  // Load libraries
  //
  std::cout << "Loading libraries..." << std::endl;
  gROOT->LoadMacro("$STAR/StRoot/StMuDSTMaker/COMMON/macros/loadSharedLibraries.C");
  loadSharedLibraries();
  gSystem->Load("libgfortran.so");
  gSystem->Load("libMinuit");
  gSystem->Load("StRefMultCorr");
  gSystem->Load("StFlowMaker");   // should be placed before HBT
  gSystem->Load("StHbtMaker");
  gSystem->Load("StarClassLibrary");
  gSystem->Load("libgsl");
  gSystem->Load("libgslcblas");
  gSystem->Load("StFemtoDstMaker");

  std::cout << "Libraries have been successfully loaded" << std::endl;

  //
  // Create chain
  //
  StChain* mChain = new StChain("StChain");
  mChain->SetDebug(0);
  StMuDebug::setLevel(0);

  //
  // Set StHbtMaker and StHbtManager
  //
  StHbtMaker *mHbtMaker = new StHbtMaker();
  mHbtMaker->SetDebug(0);
  StHbtManager *mHbtManager = mHbtMaker->HbtManager();

  //
  // Set StHbtReader
  //
  StHbtO97DstReader *O97Reader = new StHbtO97DstReader("", inFileList, "root", -1, true);
  Long64_t mNEvents = O97Reader->GetNEvents();
  mHbtManager->SetEventReader(O97Reader);

  //
  // Set basic event cut
  //
  nikEventCut *mBasicEventCut = new nikEventCut();
  mBasicEventCut->SetEventMult(mRefMult[0], mRefMult[1]);
  mBasicEventCut->SetImpactPar(mImpPar[0], mImpPar[1]);

  //
  // Set basic pion single-particle cut
  //
  nikTrackCut *mBasicTrackCut = new nikTrackCut();
  mBasicTrackCut->SetCharge(mPosCharge);
  mBasicTrackCut->SetPdg(mPdgChargedPion);
  mBasicTrackCut->SetEta(mTrackEta[0], mTrackEta[1]);
  mBasicTrackCut->SetP(mTrackMom[0], mTrackMom[1]);

  //
  // Set basic pion pair cut
  //
  nikTrackPairCut *mBasicPairCut = new nikTrackPairCut();

  int mLocalCharge, mMultBins, mMultLow, mMultHi, mEvents2Mix = 1, mCurrentPid;
  int mCentLo, mCentHi;

  //
  // StHbtAnalysis, cut, CF and monitor declarations
  //
  StHbtVertexMultAnalysis *mPionMultAnalysis[2];
  //StHbtAnalysis *mPionMultAnalysis[2];
  
  //Cuts
  nikEventCut       *mPionEventCut[2];
  nikTrackCut       *mPionTrackCut[2];
  nikTrackPairCut   *mPionPairCut[2];

  //CorrFctns
  QinvCorrFctnUrqmd *mPionQinvMixCF[2];

#ifdef MONITORS
  fxtEventCutMonitor *mFxtPionEventCutMonitorPass[2];
  fxtTrackCutMonitor *mFxtPionTrackCutMonitorPass[2];
  fxtPairCutMonitor  *mFxtPionPairCutMonitorPass[2];
  fxtEventCutMonitor *mFxtPionEventCutMonitorFail[2];
  fxtTrackCutMonitor *mFxtPionTrackCutMonitorFail[2];
  fxtPairCutMonitor  *mFxtPionPairCutMonitorFail[2];
#endif


  for (int iCharge = 1; iCharge < 2; iCharge++) {

	switch(iCharge) {
	case 1:  mLocalCharge = mPosCharge;    break;
	case 0:  mLocalCharge = mNegCharge;    break;
	default: mLocalCharge = mPosCharge;    break;
	};

	//
	//Create analysis
	//

	mPionMultAnalysis[iCharge] = new StHbtVertexMultAnalysis(1, -999., 999., 1, 0, 5000);
	mPionMultAnalysis[iCharge]->SetNumEventsToMix(mEvents2Mix);
	// mPionMultAnalysis[iCharge] = new StHbtAnalysis();
	
	//
	// Create event cut
	//
	mPionEventCut[iCharge] = new nikEventCut(*mBasicEventCut);

#ifdef MONITORS
	mFxtPionEventCutMonitorPass[iCharge] = 
	  new fxtEventCutMonitor("eventPionPass", Form("_%d", iCharge));
	mFxtPionEventCutMonitorFail[iCharge] = 
	  new fxtEventCutMonitor("eventPionFail", Form("_%d", iCharge));
	mPionEventCut[iCharge]->AddCutMonitor(mFxtPionEventCutMonitorPass[iCharge],
					      mFxtPionEventCutMonitorFail[iCharge]);
#endif
	mPionMultAnalysis[iCharge]->SetEventCut(mPionEventCut[iCharge]);

	//
	// Create single-track cuts and add them to the analysis
	//
	mPionTrackCut[iCharge] = new nikTrackCut(*mBasicTrackCut);

#ifdef MONITORS
	mFxtPionTrackCutMonitorPass[iCharge] = 
	  new fxtTrackCutMonitor(Form("_trackPionPass_%d", iCharge), mChargedPionMass);
	mFxtPionTrackCutMonitorFail[iCharge] = 
	  new fxtTrackCutMonitor(Form("_trackPionFail_%d", iCharge), mChargedPionMass);
	mPionTrackCut[iCharge]->AddCutMonitor(mFxtPionTrackCutMonitorPass[iCharge],
					      mFxtPionTrackCutMonitorFail[iCharge]);
#endif
	mPionMultAnalysis[iCharge]->SetFirstParticleCut(mPionTrackCut[iCharge]);
	mPionMultAnalysis[iCharge]->SetSecondParticleCut(mPionTrackCut[iCharge]);

	//
	// Create pair cut
	//
	mPionPairCut[iCharge] = new nikTrackPairCut(*mBasicPairCut);
#ifdef MONITORS
	mFxtPionPairCutMonitorPass[iCharge] = 
	  new fxtPairCutMonitor(Form("_pairPionPass_%d", iCharge));
	mFxtPionPairCutMonitorPass[iCharge]->SetParticleMass(mChargedPionMass);
	mFxtPionPairCutMonitorFail[iCharge] = 
	  new fxtPairCutMonitor(Form("_pairPionFail_%d", iCharge));
	mFxtPionPairCutMonitorFail[iCharge]->SetParticleMass(mChargedPionMass);
	mPionPairCut[iCharge]->AddCutMonitor(mFxtPionPairCutMonitorPass[iCharge],
					     mFxtPionPairCutMonitorFail[iCharge]);
#endif
	mPionMultAnalysis[iCharge]->SetPairCut(mPionPairCut[iCharge]);

	//
	// Correlation function initialization
	//
	mPionQinvMixCF[iCharge] = 
	  new QinvCorrFctnUrqmd(Form("hPionQinvMix_%d", iCharge),
				iCharge, iCharge, mChargedPionMass, mChargedPionMass, mPairChPionType,
				mQinvBins, mQinvVal[0], mQinvVal[1]);
	
	mPionMultAnalysis[iCharge]->AddCorrFctn(mPionQinvMixCF[iCharge]);

	//Add analysis to the manager
	mHbtManager->AddAnalysis(mPionMultAnalysis[iCharge]);
  } //for (int iCharge=0; iCharge<2; iCharge++)

  //
  // StChain initialization
  //
  mChain->Init();

  //
  //Start processing
  //
  Int_t   iRet              = 0;
  Int_t   mNEventsProcessed = 0;
  Float_t mPercentCounter   = 0.05;
  Float_t mProgress         = 0.;

  // time left feature
  unsigned int mSubCounter = 1;
  time_t       mStartTime, mStopTime, mDiffTime;
  float        mFrac;
  mStartTime = time(0);

  std::cout << "[MACRO]: total number of events = " << mNEvents << std::endl;

  for (int iEvent = 0; iEvent < mNEvents; iEvent++) {
    mProgress = (Float_t)iEvent / (Float_t)mNEvents;
    mNEventsProcessed++;
    
    if(mProgress >= mPercentCounter) {
      mPercentCounter += 0.05;
      mStopTime = time(0);
      mDiffTime = difftime(mStopTime, mStartTime);
      mFrac = (float)mDiffTime*(float)(mNEvents - iEvent)/(float)iEvent;
      mSubCounter++;
      std::cout << Form("Processing progress: %4.2f%%. Time left: %.1f sec", mProgress*100., mFrac)
		<< std::endl;
    }

    mChain->Clear();
    iRet = mChain->Make(iEvent);
    if(iRet!=0) {
      std::cout << "simPionHBT_O97Dst_1D[ERROR] - iRet: " << iRet << std::endl;
      break;
    }
  }

  mChain->Finish();

  //
  // Create output file
  //
  TFile *oFile = new TFile(oFileName, "RECREATE");

  // write hbt histograms to the file
  std::cout << "Writing histograms to the output file...";

#ifdef MONITORS
  for (int iCharge = 0; iCharge < 2; iCharge++) {
        mFxtPionEventCutMonitorPass[iCharge]->VertexZ()->Write();
	mFxtPionEventCutMonitorPass[iCharge]->VertexYvsVertexX()->Write();
	mFxtPionEventCutMonitorPass[iCharge]->RefMult()->Write();
	mFxtPionEventCutMonitorPass[iCharge]->VpdVzDiff()->Write();

	mFxtPionEventCutMonitorFail[iCharge]->VertexZ()->Write();
	mFxtPionEventCutMonitorFail[iCharge]->VertexYvsVertexX()->Write();
	mFxtPionEventCutMonitorFail[iCharge]->RefMult()->Write();
	mFxtPionEventCutMonitorFail[iCharge]->VpdVzDiff()->Write();

	mFxtPionTrackCutMonitorPass[iCharge]->P()->Write();
	mFxtPionTrackCutMonitorPass[iCharge]->Pt()->Write();
	mFxtPionTrackCutMonitorPass[iCharge]->Rapidity()->Write();
	mFxtPionTrackCutMonitorPass[iCharge]->PseudoRapidity()->Write();
	mFxtPionTrackCutMonitorPass[iCharge]->PtVsMassSqr()->Write();
	mFxtPionTrackCutMonitorPass[iCharge]->PvsInvBeta()->Write();

	mFxtPionTrackCutMonitorFail[iCharge]->P()->Write();
	mFxtPionTrackCutMonitorFail[iCharge]->Pt()->Write();
	mFxtPionTrackCutMonitorFail[iCharge]->Rapidity()->Write();
	mFxtPionTrackCutMonitorFail[iCharge]->PseudoRapidity()->Write();
	mFxtPionTrackCutMonitorFail[iCharge]->PtVsMassSqr()->Write();
	mFxtPionTrackCutMonitorFail[iCharge]->PvsInvBeta()->Write();

	mFxtPionPairCutMonitorPass[iCharge]->Kt()->Write();
	mFxtPionPairCutMonitorFail[iCharge]->Kt()->Write();
  } // for (int iCharge = 0; iCharge < 2; iCharge++)
#endif

  for (int iCharge = 1; iCharge < 2; iCharge++) {
    mPionQinvMixCF[iCharge]->MixedQinv()->Write();
    mPionQinvMixCF[iCharge]->MixedQinvWeight()->Write();
    mPionQinvMixCF[iCharge]->RealQinv()->Write();
  } // for (int iCharge = 0; iCharge < 2; iCharge++)

  std::cout << "\t[DONE]" << std::endl;

  //
  // Write the output file and close it
  //
  std::cout << "Saving the output file...";
  oFile->Write();
  oFile->Close();
  std::cout << "\t[DONE]" << std::endl;

  delete mChain;

  std::cout << "*********************************" << std::endl
	    << "*      simPionHBT_O97Dst_1D     *" << std::endl
	    << "*              Finish           *" << std::endl
	    << "*********************************" << std::endl << std::endl;
}
