class RhoCandList;
class RhoCandidate;
class PndAnaPidSelector;
class PndAnaPidCombiner;
class PndAnalysis;

// *** routine to only keep PID matched candidates in list
int SelectTruePid(PndAnalysis *ana, RhoCandList &l)
{
	int removed = 0;
	
	for (int ii=l.GetLength()-1;ii>=0;--ii)
	{
		if ( !(ana->McTruthMatch(l[ii])) )
		{
			l.Remove(l[ii]);
			removed++;
		}
	}
	
	return removed;
}

void printCand(RhoCandidate *c)
{
	TLorentzVector lv=c->P4();
	
	cout <<c->PdgCode()<<" ("<<lv.X()<<"/"<<lv.Y()<<"/"<<lv.Z()<<"/"<<lv.E()<<")"<<endl;
}

void countDoubles(RhoCandList &l, int &n1, int &n2, int &n3)
{
	int n_smc  = 0;
	int n_strk = 0;
	int n_both = 0;
	double d = 0.00001;
	
	for (int i=0;i<l.GetLength()-1;++i)
	{
		for (int j=i+1;j<l.GetLength();++j)
		{
			TLorentzVector dl = l[i]->P4() - l[j]->P4();
		
			bool chkmc = (l[i]->GetMcTruth()==l[j]->GetMcTruth());
			bool chktrk = (fabs(dl.X())<d) && (fabs(dl.Y())<d) && (fabs(dl.Z())<d) && (fabs(dl.E())<d);
			if (chkmc) n_smc++;
			if (chktrk) n_strk++;
			if (chktrk && chkmc) n_both++;
		}	
	}
	n1 = n_strk;
	n2 = n_smc;
	n3 = n_both;
}

void emc_anatest(int nevts=0)
{
        TDatabasePDG::Instance()->AddParticle("pbarpSystem","pbarpSystem",1.9,kFALSE,0.1,0,"",88888);
        TStopwatch timer;
	// *** some variables
	int i=0,j=0, k=0, l=0;
	gStyle->SetOptFit(1011);
	
	// *** the output file for FairRunAna
	TString OutFile="output.root";  
					
	// *** the files coming from the simulation
	TString inPidFile  = "new_pid_hc5kEvtG4.root";    // this file contains the PndPidCandidates and McTruth
	TString inParFile  = "simparams.root";
	
	// *** PID table with selection thresholds; can be modified by the user
	TString pidParFile = TString(gSystem->Getenv("VMCWORKDIR"))+"/macro/params/all.par";	
	
	// *** initialization
	FairLogger::GetLogger()->SetLogToFile(kFALSE);
	FairRunAna* fRun = new FairRunAna();
	FairRuntimeDb* rtdb = fRun->GetRuntimeDb();
	fRun->SetInputFile(inPidFile);
	
	// *** setup parameter database 	
	FairParRootFileIo* parIO = new FairParRootFileIo();
	parIO->open(inParFile);
	FairParAsciiFileIo* parIOPid = new FairParAsciiFileIo();
	parIOPid->open(pidParFile.Data(),"in");
	
	rtdb->setFirstInput(parIO);
	rtdb->setSecondInput(parIOPid);
	rtdb->setOutput(parIO);  
	
	fRun->SetOutputFile(OutFile);
	fRun->Init(); 
	
        // *** create an output file for all histograms
	TFile *out = TFile::Open("output_ana.root","RECREATE");
	
	// *** create some histograms
	
	TH1F *hetacm_all = new TH1F("hetacm_all","#eta_{c} mass (all)",200,0,4);
	TH1F *hhcm_all  = new TH1F("hhcm_all","h_{c} mass (all)",200,0,5);
	TH1F *hpi0m_all = new TH1F("hpi0m_all","#pi^{0} mass (all)",200,0,2);
	TH1F *hetam_all = new TH1F("hetam_all","#eta mass (all)",200,0,2);
	
	TH1F *hetacm_4cf  = new TH1F("hetacm_4cf","#eta_{c} mass (4C fit)",200,0,4); 
	TH1F *hetacm_mcf  = new TH1F("hetacm_mcf","#eta_{c} mass (mass constraint fit)",200,0,4); 
	
	TH1F *hhc_chi2_4c   = new TH1F("hhc_chi2_4c",  "h_{c}: #chi^{2} 4C fit",100,0,250); 
	TH1F *hetac_chi2_mf  = new TH1F("hetac_chi2_mf", "#eta_{c}: #chi^{2} mass fit",100,0,10); 

	TH1F *hhc_prob_4c   = new TH1F("hhc_prob_4c",  "h_{c}: Prob 4C fit",100,0,1); 
	TH1F *hetac_prob_mf  = new TH1F("hetac_prob_mf", "#eta_{c}: Prob mass fit",100,0,1); 

	//
	// Now the analysis stuff comes...
	//
	
	
	// *** the data reader object
	PndAnalysis* theAnalysis = new PndAnalysis();
	if (nevts==0) nevts= theAnalysis->GetEntries();
	
	// *** RhoCandLists for the analysis
	RhoCandList gamma, pi0, eta, etac, hc;
	
	// *** Mass selector for the jpsi cands
	double m0_etac = TDatabasePDG::Instance()->GetParticle(441)->Mass();   // Get nominal PDG mass of the etac
	RhoMassParticleSelector *etacMassSel=new RhoMassParticleSelector("etac",m0_etac,1.0);
	
	// *** the lorentz vector of the initial hc
	TLorentzVector ini(0, 0, 5.61, sqrt(5.61*5.61+9.3827203e-01*9.3827203e-01)+9.3827203e-01);

	pi0.SetType(111);
	eta.SetType(221);
	gamma.SetType(22);
	etac.SetType(441);
	hc.SetType(10443);
	
	// ***
	// the event loop
	// ***
	
	while (theAnalysis->GetEvent() && i++<nevts)
	{
		/*if ((i%100)==0)*/ cout<<"evt " << i << endl;
				
		// *** Select with no PID info ('All'); type and mass are set 		
		theAnalysis->FillList(gamma,  "Neutral");

		// *** combinatorics to get pi0's and eta's
		pi0.Combine(gamma,gamma);
		eta.Combine(gamma,gamma);

		// *** fill histo's with pi0 and eta candidates (just as a check)

		for (j=0;j<pi0.GetLength();++j) 
		{
			hpi0m_all->Fill( pi0[j]->M() );
		}

		for (j=0;j<eta.GetLength();++j) 
		{
			hetam_all->Fill( eta[j]->M() );
		}
		
		// *** combinatorics for etac -> pi0 pi0 eta
		etac.Combine(pi0, pi0, eta);
		
		// ***
		// *** do the TRUTH MATCH for etac
		// ***

		for (j=0;j<etac.GetLength();++j) 
		{
			hetacm_all->Fill( etac[j]->M() );
		}
		
		// *** some rough mass selection
		etac.Select(etacMassSel);
		
		// *** combinatorics for hc -> etac gamma
		hc.Combine(etac, gamma);

		// ***
		// *** do the TRUTH MATCH for hc
		// ***

		for (j=0;j<hc.GetLength();++j) 
		{
			hhcm_all->Fill( hc[j]->M() );
		}	

		
		// ***
		// *** do 4C FIT (initial hc system)
		// ***
		for (j=0;j<hc.GetLength();++j) 
		{
			PndKinFitter fitter(hc[j]);	// instantiate the kin fitter for hc
			fitter.Add4MomConstraint(ini);	// set 4 constraint
			fitter.Fit();		            // do fit
			
			double chi2_4c = fitter.GetChi2();	// get chi2 of fit
			double prob_4c = fitter.GetProb();	// access probability of fit
			hhc_chi2_4c->Fill(chi2_4c);
			hhc_prob_4c->Fill(prob_4c);			
			
			if ( prob_4c > 0.01 )			// when good enough, fill some histo
			{
				RhoCandidate *jfit = hc[j]->Daughter(0)->GetFit();	// get fitted etac
				
				hetacm_4cf->Fill(jfit->M());
			}
		}		
		
		
		// ***
		// *** do MASS CONSTRAINT FIT (etac)
		// ***
		for (j=0;j<etac.GetLength();++j) 
		{
			PndKinFitter mfitter(etac[j]);		// instantiate the PndKinFitter for etac
			mfitter.AddMassConstraint(m0_etac);	// add the mass constraint
			mfitter.Fit();						// do fit
			
			double chi2_m = mfitter.GetChi2();	// get chi2 of fit
			double prob_m = mfitter.GetProb();	// access probability of fit
			hetac_chi2_mf->Fill(chi2_m);
			hetac_prob_mf->Fill(prob_m);			
			
			if ( prob_m > 0.01 )				// when good enough, fill some histo
			{
				RhoCandidate *jfit = etac[j]->GetFit();	// access the fitted cand
				hetacm_mcf->Fill(jfit->M());
			}
		}		
		
	}
		
	// *** write out all the histos
	out->cd();
	
	hetacm_all->Write();
	hhcm_all->Write();
	hpi0m_all->Write();
	hetam_all->Write();

	hetacm_4cf->Write();
	hetacm_mcf->Write();
	
	hhc_chi2_4c->Write();
	hetac_chi2_mf->Write();
			
	hhc_prob_4c->Write();
	hetac_prob_mf->Write();
		
	out->Save();
        
        timer.Stop();
        Double_t rtime = timer.RealTime();
        Double_t ctime = timer.CpuTime();
        cout << endl << endl;
        cout << "Macro finished successfully." << endl;
        cout << "Real time " << rtime << " s, CPU time " << ctime << " s" << endl;
        cout << endl;
        // ------------------------------------------------------------------------
        cout << " Test passed" << endl;
        cout << " All ok " << endl;
        exit(0);
	
}
