#include <stdlib.h>
#include <iostream>

#include <TString.h>
#include <TH1F.h>
#include <TH2F.h>
#include <TMath.h>
#include <TLine.h>
#include <TPolyMarker.h>
#include <TSelector.h>
#include <TFile.h>
#include <TGraphErrors.h>
#include <TMultiGraph.h>
#include <TCanvas.h>

#include <PamLevel2.h>
#include <CaloCore.h>
#include <CaloREPCore.h>
//#include <caloclassesfun.h>

using namespace std;

void CaloREPCore(TString ddir,TString list, TString options, TString outfile){
  //
  //
  //
  Bool_t verbose = true;
  Bool_t debug = false;
  //
  Bool_t trackanyway = true;
  //
  Float_t rigdefault = 50.;
  //
  Bool_t hZn = true;
  //
  Bool_t withtrk = true;
  //
  Bool_t st = true;
  //
  Bool_t mechal = false;
  //
  Float_t tmptrigty = -1.;      
  Int_t S3 = 0;
  Int_t S2 = 0;
  Int_t S12 = 0;
  Int_t S11 = 0;
  TString host = "mysql://localhost/pamelaprod";
  TString user = "anonymous";
  TString psw = "";
  const char *pamdbhost=gSystem->Getenv("PAM_DBHOST");
  const char *pamdbuser=gSystem->Getenv("PAM_DBUSER");
  const char *pamdbpsw=gSystem->Getenv("PAM_DBPSW");
  if ( !pamdbhost ) pamdbhost = "";
  if ( !pamdbuser ) pamdbuser = "";
  if ( !pamdbpsw ) pamdbpsw = "";
  if ( strcmp(pamdbhost,"") ) host = pamdbhost;
  if ( strcmp(pamdbuser,"") ) user = pamdbuser;
  if ( strcmp(pamdbpsw,"") ) psw = pamdbpsw;
  GL_TABLES *glt = new GL_TABLES(host,user,psw);
  //  -------------------- 
  //  read input file/list
  //  --------------------
  TString outfile_t ="";
  if( outfile.IsNull() ){
    if(!list.IsNull())outfile = list(0,list.Last('.'));	    
    else outfile = "output";	    
  }else{	
    if(outfile.Contains(".root"))outfile = outfile(0,outfile.Last('.'));
  };
  TFile *outft = NULL;
  outfile_t = outfile;
  outfile_t.Append("-tree.root");
  outft = (TFile*)gROOT->FindObject(outfile_t); if (outft) outft->Close();
  outft = new TFile(outfile_t,"RECREATE");
  if(outft->IsZombie()){
    cout << "Output file could not be created\n";
    return;
  };
  cout << "Created output file: "<<outft->GetName()<<endl;
  //
  PamLevel2 *event = new PamLevel2(ddir,list,options);
  event->SetMaxShift(500);
  //
  TTree::SetMaxTreeSize(1000*Long64_t(2000000000)); 
  //
  TrkParams::Load();
  //
  GL_ROOT *glroot = new GL_ROOT();
  //
  CaloLevel1 *c1 = new CaloLevel1();
  CaloLevel2 *ca = new CaloLevel2();  
  CaloLevel0 *c0 = new CaloLevel0();
  //
  outft->cd();
  event->CreateCloneTrees(outft);
  outft->cd();
  TTree *calo = new TTree("Calorimeter","PAMELA Level2 calorimeter data");
  calo->SetAutoSave(900000000000000LL);
  ca->Set();
  calo->Branch("CaloLevel2","CaloLevel2",&ca);
  calo->Branch("CaloLevel1","CaloLevel1",&c1);
  //  
  //  -----------------
  //  loop over events
  //  -----------------
  //
  UInt_t srun = 0;
  ULong64_t nevents = event->GetEntries();	
  //
  cout << endl<<" Start loop over events:  "<< nevents<<endl;
  //
  //
  for(ULong64_t iev=0; iev<nevents; iev++){
    //for(ULong64_t iev=261000; iev<nevents; iev++){
//  for(ULong64_t iev=0; iev<2000; iev++){
    //	
    if ( !(iev%1000) ) printf(" %lluK \n",iev/1000);
    event->Clear();
    //
    if( event->GetEntry(iev) ){ 
      //
      if ( event->GetYodaEntry() ){
	//
	event->FillCloneTrees();
	//	
	Int_t ye = event->GetYodaTree()->GetReadEntry();
	//
	UInt_t evfrom = ye;
	UInt_t totevent = 1;
	Int_t sgnl = 0;
	//
	// for every new run
	//
	if ( srun != event->GetRunInfo()->ID ){
	  //
	  UInt_t runheadtime = event->GetRunInfo()->RUNHEADER_TIME;
	  UInt_t runtrailtime = event->GetRunInfo()->RUNTRAILER_TIME;
	  srun = event->GetRunInfo()->ID;
	  //
	  // prepare the timesync for the db
	  //
	  TString host = glt->CGetHost();
	  TString user = glt->CGetUser();
	  TString psw = glt->CGetPsw();
	  TSQLServer *dbc = TSQLServer::Connect(host.Data(),user.Data(),psw.Data());
	  UInt_t ntry = 0;
	  while ( !dbc->IsConnected() && ntry < 10 ){
	    ntry++;
	    gSystem->Sleep(10000);
	    dbc = TSQLServer::Connect(host.Data(),user.Data(),psw.Data());
	  };
	  if ( !dbc->IsConnected() ){
	    return;
	  };

	  stringstream myquery;
	  myquery.str("");
	  myquery << "SET time_zone='+0:00'";
	  dbc->Query(myquery.str().c_str());
	  //
	  // Search in the DB the path and name of the LEVEL0 file to be processed.
	  //
	  glroot->Query_GL_ROOT(event->GetRunInfo()->ID_ROOT_L0,dbc);
	  //
	  if ( dbc ){
	    dbc->Close();
	    delete dbc;
	  };
	  //
	  TString fname;
	  stringstream ftmpname;
	  ftmpname.str("");
	  ftmpname << glroot->PATH.Data() << "/";
	  ftmpname << glroot->NAME.Data();
	  fname = ftmpname.str().c_str();
	  //
	  // print out informations
	  //
	  if ( verbose ) printf("\n LEVEL0 data file: %s \n",fname.Data());
	  if ( verbose ) printf(" RUN HEADER absolute time is:  %u \n",runheadtime);
	  if ( verbose ) printf(" RUN TRAILER absolute time is: %u \n",runtrailtime);
	  if ( verbose ) printf(" Run %u: from %i to %i (runheader %u runtrailer %u )\n\n",event->GetRunInfo()->ID,evfrom,evfrom+totevent,event->GetRunInfo()->EV_FROM,event->GetRunInfo()->EV_TO);
	  //
	  // Construct the event object, look for the calibration which include the first header
	  //  
	  if ( verbose ) printf(" Check for calorimeter calibrations and initialize event object \n");
	  //
	  c0->ProcessingInit(glt,runheadtime,sgnl,event->GetYodaTree(),debug,verbose);      
	  c0->SetCrossTalk(true);
	  c0->SetCrossTalkType(false);      
	  //	  c0->SetCrossTalkType(true);      
	  //
	  // Check if we have to load parameter files
	  //
	  sgnl = 0;
	  sgnl = c0->ChkParam(glt,runheadtime,mechal); // calorimeter parameter files
	  //
	  // Calculate the cross talk corrections needed for this run using calibration informations
	  //
	  sgnl = 0;
	  sgnl = c0->CalcCrossTalkCorr(glt,runheadtime); 
	  printf(" sgnl from crosstalkcorr %i \n",sgnl);
	  //
	  //
	};
	//
	//      printf(" ye is %i \n",ye);
	//
	// run over all the events of the run
	//
	UInt_t atime = event->GetOrbitalInfo()->absTime;  
	//
	// start processing
	//
	c1->Clear();
	ca->Clear();
	//
	// determine who generate the trigger for this event (TOF, S4/PULSER, CALO) 
	//
	S3 = 0;
	S2 = 0;
	S12 = 0;
	S11 = 0;
	S3 = event->GetTrigLevel2()->patterntrig[2];
	S2 = event->GetTrigLevel2()->patterntrig[3];
	S12 = event->GetTrigLevel2()->patterntrig[4];
	S11 = event->GetTrigLevel2()->patterntrig[5];
	if ( event->GetTrigLevel2()->patterntrig[1] & (1<<0) ) tmptrigty = 1.;
	if ( event->GetTrigLevel2()->patterntrig[0] ) tmptrigty = 2.;
	if ( S3 || S2 || S12 || S11 )  tmptrigty = 0.;    
	if ( !(event->GetTrigLevel2()->patterntrig[1] & (1<<0)) && !event->GetTrigLevel2()->patterntrig[0] && !S3 && !S2 && !S12 && !S11 ) tmptrigty = 1.;
	c0->clevel2->trigty = tmptrigty;
	//
	// check if the calibration we are using is still good, if not load another calibration
	//
	sgnl = 0;
	sgnl = c0->ChkCalib(glt,atime);
	//
	// do we have at least one track from the tracker? this check has been disabled
	//
	c0->clevel1->good2 = 1;
	//
	// use the whole calorimeter, 22 W planes
	//
	c0->clevel1->npla = 22;
	//
	// Use the standard silicon planes shifting
	//
	c0->clevel1->reverse = 0;
	//
	//
	// Calibrate calorimeter event "re" and store output in the two structures that will be passed to fortran routine
	//
	c0->Calibrate(ye);      
	//
	// Here we have calibrated data, ready to be passed to the FORTRAN routine which will extract topological variables.
	//
	//
	// Calculate variables common to all tracks (qtot, nstrip, etc.)
	//
	c0->GetCommonVar();
	//
	// Fill common variables
	//
	c0->FillCommonVar(c1,ca);
	//
	// Calculate variables related to tracks only if we have at least one track (from selftrigger and/or tracker)
	//
	UInt_t ntrkentry = 0;
	//
	Bool_t filled = false;
	//
	// Run over tracks (tracker or calorimeter )
	//
	if ( withtrk ){
	  for(Int_t nt=0; nt < event->GetTrkLevel2()->ntrk(); nt++){  
	    //
	    c0->clevel1->good2 = 1;
	    //
	    TrkTrack *ptt = event->GetTrkLevel2()->GetStoredTrack(nt)->GetTrkTrack(); 
	    //
	    c0->clevel1->trkchi2 = 0;
	    //
	    // Copy the alpha vector in the input structure
	    //
	    for (Int_t e = 0; e < 5 ; e++){
	      c0->clevel1->al_p[e][0] = ptt->al[e];
	    };	  
	    //
	    // Get tracker related variables for this track
	    //
	    c0->GetTrkVar();
	    //
	  // Save tracker track sequence number
	  //	
	    c0->trkseqno = nt;
	    //
	  // Copy values in the class ca from the structure clevel2
	  //
	    c0->FillTrkVar(ca,ntrkentry);
	    ntrkentry++;	
	    filled = true;
	    //
	  }; // loop on all the tracks
	};
	// 
	// if no tracks found but there is the possibility to have a good track we should try to calculate anyway the track related variables using the calorimeter 
	// fit of the track (to be used for example when TRK is off due to any reason like IPM3/5 off).
	// here we make an event selection so it must be done very carefully...
	//
	// conditions are: 0) no track from the tracker 1) we have a track fit both in x and y 2) no problems with calo for this event 3) no selftrigger event
	//
	if ( trackanyway && !filled && c0->clevel2->npcfit[0] >= 2 && c0->clevel2->npcfit[1] >= 2 && c0->clevel2->good != 0 && c0->clevel2->trigty < 2. ){
	  if ( debug ) printf(" Event with a track not fitted by the tracker at entry %i \n",ye);
	  //
	  // Disable "track mode" in the fortran routine
	  //
	  c0->clevel1->good2 = 0;
	  c0->clevel1->riginput = rigdefault;
	  //
	  // We have a selftrigger event to analyze.
	  //
	  for (Int_t e = 0; e < 5 ; e++){
	    c0->clevel1->al_p[e][0] = 0.;
	    c0->clevel1->al_p[e][1] = 0.;
	  };
	  c0->clevel1->trkchi2 = 0;
	  //
	  c0->GetTrkVar();
	  //
	  // if we had no problem (clevel1->good2 = 0, NOTICE zero, not one in this mode!), fill and go on
	  //
	  if ( c0->clevel1->good2 == 0 ) {
	    //
	    // In selftrigger mode the trkentry variable is set to -1
	    //
	    c0->trkseqno = -3;
	    //
	    // Copy values in the class ca from the structure clevel2
	    //
	    c0->FillTrkVar(ca,ntrkentry);
	    ntrkentry++;
	    filled = true;
	    //
	  } else {
	    if ( verbose ) printf(" Selftrigger: problems with event at entry %i \n",ye);
	  };
	  //
	};
	//
	// Call high energy nuclei routine
	//
	if ( hZn && c0->clevel2->trigty >= 2. ){
	  if ( debug ) printf(" Calling selftrigger high energy nuclei routine for entry %i \n",ye);
	  //
	  // Disable "track mode" in the fortran routine
	  //
	  c0->clevel1->good2 = 0;
	  //
	  // Set high energy nuclei flag to one
	  // 
	  c0->clevel1->hzn = 1; 
	  c0->clevel1->riginput = rigdefault;
	  //
	  // We have a selftrigger event to analyze.
	  //
	  for (Int_t e = 0; e < 5 ; e++){
	    c0->clevel1->al_p[e][0] = 0.;
	    c0->clevel1->al_p[e][1] = 0.;
	  };
	  c0->clevel1->trkchi2 = 0;
	  //
	  c0->GetTrkVar();
	  //
	  // if we had no problem (clevel1->good2 = 0, NOTICE zero, not one in this mode!), fill and go on
	  //
	  if ( c0->clevel1->good2 == 0 ) {
	    //
	    // In selftrigger mode the trkentry variable is set to -1
	    //
	    c0->trkseqno = -2;
	    //
	    // Copy values in the class ca from the structure clevel2
	    //
	    c0->FillTrkVar(ca,ntrkentry);
	    ntrkentry++;
	    filled = true;
	    //
	  } else {
	    if ( verbose ) printf(" Selftrigger: problems with event at entry %i \n",ye);
	  };
	  //
	};
	//
	// self trigger event
	//
	if ( st && c0->clevel2->trigty >= 2. ){
	  if ( verbose ) printf(" Selftrigger event at entry %i \n",ye);
	  //
	  // Disable "track mode" in the fortran routine
	  //
	  c0->clevel1->good2 = 0;
	  //
	  // disable high enery nuclei flag;
	  //
	  c0->clevel1->hzn = 0; 
	  //
	  // We have a selftrigger event to analyze.
	  //
	  for (Int_t e = 0; e < 5 ; e++){
	    c0->clevel1->al_p[e][0] = 0.;
	    c0->clevel1->al_p[e][1] = 0.;
	  };
	  c0->clevel1->trkchi2 = 0;
	  //
	  c0->GetTrkVar();
	  //
	  // if we had no problem (clevel2->good = 0, NOTICE zero, not one in selftrigger mode!), fill and go on
	  //
	  if ( c0->clevel1->good2 == 0 ) {
	    //
	    // In selftrigger mode the trkentry variable is set to -1
	    //
	    c0->trkseqno = -1;
	    //
	    // Copy values in the class ca from the structure clevel2
	    //
	    c0->FillTrkVar(ca,ntrkentry);
	    ntrkentry++;
	    filled = true;
	    //
	  } else {
	    if ( verbose ) printf(" Selftrigger: problems with event at entry %i \n",ye);
	  };
	};
	//
	// Clear structures used to communicate with fortran
	//
	c0->ClearStructs();
	//
	// Fill the rootple 
	//
	calo->Fill();
	//    
      };
      //
    }else{
      cout << "Chain entry "<<iev<<" -- ERROR --"<<endl;
    };
  };
  //
  cout <<endl;
  //
  event->Clear();
  //
  outft->cd();
  event->WriteCloneTrees();
  calo->Write();
  outft->Close();
  //
  return;
  //  
}
