/**
 * \file TrkCore.cpp
 * \author Elena Vannuccini
 */
// .........................................................
// ROOT header files
// .........................................................
#include <TTree.h>
#include <TClassEdit.h>
#include <TObject.h>
#include <TList.h>
#include <TSystem.h>
#include <TSystemDirectory.h>
#include <TString.h>
#include <TFile.h>
#include <TClass.h>
#include <TCanvas.h>
#include <TH1.h>
#include <TH1F.h>
#include <TH2D.h>
#include <TLatex.h>
#include <TPad.h>
#include <TSQLServer.h>
#include <TSQLRow.h>
#include <TSQLResult.h>
#include <TBenchmark.h>
#include <TStopwatch.h>

// .........................................................
// other general header files
// .........................................................
#include <fstream>
#include <iostream>
// .........................................................
// files in the inc directory
// .........................................................
#include <RunInfo.h>
#include <GLTables.h>
#include <TrkVerl2.h>
#include <TrkProcess.h>
//#include <TrkStruct.h>
#include <TrkLevel2.h> 
#include <TrkLevel1.h> 
#include <TrkLevel0.h> 
#include <TrkHough.h>
// .........................................................
// YODA header files 
// .........................................................
#include <PamelaRun.h>
//#include <RegistryEvent.h>
#include <physics/tracker/TrackerEvent.h>
#include <CalibTrk1Event.h>
#include <CalibTrk2Event.h>
//
// Calorimeter level1 class header and definitions
//
#include <CaloLevel1.h> //EM
//
// ToF level2 class header and definitions
//
#include <ToFLevel2.h> //EM
//
// Extended tracking
//
#include <ExtTrkingAlg.h> //EV

// .........................................................
// namespaces
// .........................................................
using namespace std;
using namespace pamela;
//  ================================================================================
//
//
//  ================================================================================
/**
 * \brief Tracker data reduction routine. 
 *
 * It performs data reduction LEVEL0->LEVEL1->LEVEL2, producing LEVEL1 and or LEVEL2 output, in ROOT or HBOOK format.
 * Input parameters:
 * @param run id of the run to be processed (if run=0 a whole file is reprocessed)
 * @param dbc pointer to BD server
 * @param file1 output LEVEL1 file name (null if no LEVEL1 output is required)
 * @param file2 output LEVEL2 file name (null if no LEVEL2 output is required)
 * @param standalone (flag to run the program in standalone mode, that is without reading RunInfo)
 */
//short int TrkCore(Int_t run, TSQLServer *dbc, TString file1, TString file2, Bool_t standalone)
//int TrkCore(UInt_t run, TFile *f2, TSQLServer *dbc, int ncustom, char*vcustom[])     // EMILIANO
int TrkCore(UInt_t run, TFile *f2, GL_TABLES *glt, int ncustom, char*vcustom[])     // EMILIANO
{
    // ---------------------------
    // Define some basic variables
    // ---------------------------

    TString  filename     = ""; 
    Long64_t nentries = 0LL;
    
    // LEVEL0 input 
    TFile         *f0 = 0;
    TTree         *physicsTree = 0;
    TBranch       *b_trk       = 0;
    TBranch       *b_header    = 0;
    EventHeader   *header  = 0;
    PscuHeader    *pscu    = 0;
    TrkLevel0     *l0_event = new TrkLevel0();
    // RunInfo 
    ItoRunInfo *runinfo = 0;
    TArrayI    *runlist = 0;
    UInt_t      from_run;
    UInt_t      to_run;
    //    Bool_t      reprocessing = false; // EM GCC 4.7
    //LEVEL2 output - ROOT
    TTree     *t_level2 = 0; 
    TTree     *t_clone = 0; 
    TrkLevel2 *l2_event = new TrkLevel2();
    TrkLevel2 *l2_clone = new TrkLevel2();

    TrkLevel2 *l2_event_nuclei = NULL; // EM bug  new TrkLevel2();
    // EM    TrkLevel2 *l2_clone_nuclei = new TrkLevel2();
    //LEVEL1 output - ROOT
    TrkLevel1    *l1_event        = new TrkLevel1();
    TrkLevel1    *l1_event_nuclei = NULL; // EM, bug new TrkLevel1();
    TrkHough     *lh_event = new TrkHough();

    //Extended tracking:
    TTree     *t_cal = NULL;
    TTree     *t_tof = NULL;
    ExtTrkingAlg *trkAlg = NULL; 
    ExtTrkingAlg *trkAlg_nuclei = NULL; 
    TClonesArray *trk_array = NULL; 
    TClonesArray *trk_array_nuclei = NULL; 
    
    CaloLevel1 *cl1 = NULL;
    ToFLevel2  *tl2 = NULL;

    typedef struct  {

	Int_t nTrk;
	Int_t nTrkNuc;
	Int_t nTrkExt;
	Int_t nTrkExtNuc;

	Int_t nCl;
	Int_t nClCut;

	Float_t tTrk;
	Float_t tTrkExt;
	Float_t tTrkNuc;	


	void Dump(){
	    cout <<"  nTrk"<<setw(5)<<nTrk ;
	    cout <<"  nTrkNuc"<<setw(5)<<nTrkNuc ;
	    cout <<"  nTrkExt"<<setw(5)<< nTrkExt;
	    cout <<"  nTrkExtNuc"<<setw(5)<<nTrkExtNuc ;
	    cout <<"  nCl"<<setw(5)<<nCl ;
	    cout <<"  nClCut"<<setw(5)<<nClCut ;
	    cout <<"  tTrk"<<setw(15)<<tTrk ;
	    cout <<"  tTrkExt"<<setw(15)<<tTrkExt;
	    cout <<"  tTrkNuc"<<setw(15)<<tTrkNuc ;
	    cout << endl;
	}


    } TrkWatch;
    TrkWatch trkWatch; 
    TTree *tWatch = NULL;

    Int_t extAlgFlag=0;

// -----------------------
// -----------------------
// Handle input parameters
// (data)
//
// - open/create output files, determining the environment root/hbook from the estension
// - create the level1/level2 tree+branch/nt-uple
// -----------------------
// -----------------------	
    TrkProcess *p = new TrkProcess(run,f2);
    if( p->HandleCustomPar(ncustom,vcustom) )return(p->ostatus);;
    if( TrkParams::VerboseMode() )p->Dump();
//    p->Dump();

// ===================================
// Open/Create level2 output file
// ===================================
    if(p->get2){
	//-------------------------------------------
	// read RunInfo
	//-------------------------------------------
	if(!(p->standalone)){
	    // Open "RunInfo" tree and get list of run
	    runinfo = new ItoRunInfo(f2);
	    char *trkversion = TrkInfo(false);
	    int runinfo_error = runinfo->Update(run,"TRK",trkversion);  // EMQUI
	    if( runinfo_error ) throw runinfo_error;
	    runlist = runinfo->GetRunList();
            //	    reprocessing = runinfo->IsReprocessing();//????
	    if(TrkParams::VerboseMode()){
		cout << "#events       "<< runinfo->GetFileEntries() << endl;// #events in the file 
		cout << "#runs         "<< runinfo->GetRunEntries()  << endl;// #runs in the file
		cout << "1-st run      "<< runlist->At(0)            << endl;
		cout << "last run      "<< runlist->At(runinfo->GetRunEntries()-1) << endl;
		cout << "1-st event    "<< runinfo->GetFirstEntry()  << endl;// first event of our run
		cout << "last event+1  "<< runinfo->GetLastEntry()   << endl;// first event AFTER the last event of our run
                //		cout << "reprocessing  "<< runinfo->IsReprocessing() << endl << endl;
	    };
	};
	//-------------------------------------------
	//
	//-------------------------------------------	
	// Take (if present) the old Tracker tree
	t_clone = (TTree*)f2->Get("Tracker");
	if( t_clone != NULL ){
          t_clone->SetBranchAddress("TrkLevel2",&l2_clone);
          //          t_clone->SetMaxVirtualSize(2500000000LL); // EM residual Tracker-new tree in level2 files when NEVENTS is big
          t_clone->SetAutoSave(900000000000000LL); // EM residual Tracker-new tree in level2 files when NEVENTS is big	    
        }

	// Create NEW Tracker tree			
	t_level2 = new TTree("Tracker-new","PAMELA tracker level2 data ");
        //        t_level2->SetMaxVirtualSize(2500000000LL); // EM residual Tracker-new tree in level2 files when NEVENTS is big
        t_level2->SetAutoSave(900000000000000LL); // EM residual Tracker-new tree in level2 files when NEVENTS is big	    
	l2_event->Set(); // ****NBNBNBN*****
	t_level2->Branch("TrkLevel2","TrkLevel2",&l2_event);

	if(p->get1){
	    if(TrkParams::VerboseMode())cout << endl << "Requested LEVEL1 output" << endl;
	    l1_event->Set(); // ****NBNBNBN*****            
	    t_level2->Branch("TrkLevel1","TrkLevel1",&l1_event);
	}
	if(p->geth){
	  if(TrkParams::VerboseMode())cout << endl << "Requested Hough-Transform output" << endl;
	  t_level2->Branch("TrkHough","TrkHough",&lh_event);
	};
	if(p->getn){
	  if(TrkParams::VerboseMode())cout << endl << "Requested nuclei-algorythm output" << endl;
	  if(!l2_event_nuclei)l2_event_nuclei = new TrkLevel2();
	  if(!l1_event_nuclei)l1_event_nuclei = new TrkLevel1(); // EM bug
	  l2_event_nuclei->Set(); // ****NBNBNBN*****
	  t_level2->Branch("TrackNuclei",l2_event_nuclei->GetPointerToTrackArray());
	    
	}
	// =========================================================
	// extended tracking algorythm output
	// =========================================================
	if(p->gete){

            if(TrkParams::VerboseMode()) cout <<"Creating extended tracking algorythm "<<p->extAlgID<<endl;
	    trkAlg = new ExtTrkingAlg(p->extAlgID);// create the algorythm
	    trk_array = trkAlg->GetTrackArray();   // get the track array
	    if(!trk_array)throw -206;
	    t_level2->Branch("RecoveredTrack",trkAlg->GetPointerToTrackArray());// put it in a branch
	    if(p->getn){
		if(TrkParams::VerboseMode())cout << endl << "Requested extended nuclei-algorythm output" << endl; 
		trkAlg_nuclei = new ExtTrkingAlg(p->extAlgID);// create the algorythm for nuclei
		trk_array_nuclei = trkAlg_nuclei->GetTrackArray();// get the track array
		if(!trk_array_nuclei)throw -206;
		t_level2->Branch("RecoveredTrackNuclei",trkAlg_nuclei->GetPointerToTrackArray()); // put it in a branch
	    }
	    t_level2->Branch("extAlgFlag",&extAlgFlag,"extAlgFlag/I");
	    // ------------------------
	    // calorimeter and tof tree
	    // ------------------------
	    t_cal = (TTree*)f2->Get("Calorimeter");
	    if(!t_cal)throw -206;
	    cl1 = new CaloLevel1();
	    t_cal->SetBranchAddress("CaloLevel1",&cl1); 
            t_cal->SetAutoSave(900000000000000LL); // EM residual Tracker-new tree in level2 files when NEVENTS is big	    
            //            t_cal->SetMaxVirtualSize(2500000000LL); // EM residual Tracker-new tree in level2 files when NEVENTS is big
	    
	    
	    t_tof = (TTree*)f2->Get("ToF");
	    if(!t_tof)throw -206;
	    tl2 = new ToFLevel2();
	    t_tof->SetBranchAddress("ToFLevel2",&tl2);  
            t_tof->SetAutoSave(900000000000000LL); // EM residual Tracker-new tree in level2 files when NEVENTS is big	    
            //            t_tof->SetMaxVirtualSize(2500000000LL); // EM residual Tracker-new tree in level2 files when NEVENTS is big	    

	}
	if(p->watch){

//	    f2->cd();
	    tWatch = new TTree("TrkWatch"," Tracking timing info");

	    tWatch->Branch("nCl",&trkWatch.nCl,"nCl/I");
	    tWatch->Branch("nClCut",&trkWatch.nClCut,"nClCut/I");

	    tWatch->Branch("nTrk",&trkWatch.nTrk,"nTrk/I");
	    tWatch->Branch("nTrkExt",&trkWatch.nTrkExt,"nTrkExt/I");
	    tWatch->Branch("nTrkNuc",&trkWatch.nTrkNuc,"nTrkNuc/I");
	    tWatch->Branch("nTrkExtNuc",&trkWatch.nTrkExtNuc,"nTrkExtExt/I");

	    tWatch->Branch("tTrk",&trkWatch.tTrk,"tTrk/F");
	    tWatch->Branch("tTrkExt",&trkWatch.tTrkExt,"tTrkExt/F");
	    tWatch->Branch("tTrkNuc",&trkWatch.tTrkNuc,"tTrkNuc/F");


	}

	


    };




// -------------------------------------------
// define runs to be processed/reprocessed
// -------------------------------------------
    if(run == 0){
	// reprocessing ALL runs
	if(p->standalone)throw -298; // reprocessing not implemented
	from_run = runlist->At(0);
	to_run   = runlist->At(runinfo->GetRunEntries()-1);
    }else{
	// processing/reprocessing ONE single run
	from_run = run;
	to_run   = run;
    };
    
    //
    // init event counter
    //
    Int_t ev_count =0;
    Int_t pr_count =0;
    //
    // create query-results objects
    //
    GL_RUN       q1 = GL_RUN();
    GL_TRK_CALIB q2 = GL_TRK_CALIB();
    GL_ROOT      q3 = GL_ROOT();
    GL_PARAM     q4 = GL_PARAM();
	
// ------------------------------------------------------------
//  if reprocessing one run, copy all the events BEFORE the run
// ------------------------------------------------------------
    if( !(p->standalone) ){
      if(TrkParams::VerboseMode()) cout << " Reprocessing one run: "<<run<< endl;
	for(UInt_t i=0; i<runinfo->GetFirstEntry(); i++){
	    if ( t_clone->GetEntry(i) <= 0 ) throw -36;// EM
	    // COPY COPY COPY
	    *l2_event = *l2_clone;
	    t_level2->Fill();
	    l2_event->Clear();
	};
    };
// ------------------------------------------------------------
// ------------------------------------------------------------
//  START LOOP OVER RUNS TO PROCESS/REPROCESS
// ------------------------------------------------------------
// ------------------------------------------------------------
    for(UInt_t idRun = from_run; idRun <= to_run; idRun++){
	
	if(TrkParams::VerboseMode()) cout << endl<<" ========================= Run: "<< idRun << endl;
	UInt_t runheadtime       = 0;
	UInt_t runtrailtime      = 0;
	UInt_t runheadobt       = 0;
	UInt_t runtrailobt      = 0;
	UInt_t abstime          = 0;
	UInt_t    evfrom         = 0;
	UInt_t    evto           = 0;
	UInt_t    nevents        = 0;
	Int_t     id_root_l0     =-1;
	Int_t     trk_calib_used = 0;

 	TString host = glt->CGetHost();  // EMILIANO
 	TString user = glt->CGetUser();  // EMILIANO
 	TString psw = glt->CGetPsw();  // EMILIANO
// 	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;
	TSQLServer *dbc = TSQLServer::Connect(host.Data(),user.Data(),psw.Data());
	stringstream myquery;   // EMILIANO
	myquery.str("");  // EMILIANO
	myquery << "SET time_zone='+0:00';";  // EMILIANO
	delete dbc->Query(myquery.str().c_str());  // EMILIANO
        delete dbc->Query("SET sql_mode = 'NO_UNSIGNED_SUBTRACTION';");// EM
	if(p->standalone){
	    // ==============================
	    // first query: retrieve run info
	    // ==============================
	    if (q1.Query_GL_RUN(idRun,dbc) )throw -50;
	    id_root_l0     = q1.ID_ROOT_L0;
	    runheadtime    = q1.RUNHEADER_TIME;
	    runtrailtime   = q1.RUNTRAILER_TIME;
	    runheadobt    = q1.RUNHEADER_OBT;
	    runtrailobt   = q1.RUNTRAILER_OBT;
	    evfrom         = q1.EV_FROM;
	    evto           = q1.EV_TO;
	    nevents        = q1.NEVENTS;
	    trk_calib_used = q1.TRK_CALIB_USED;
	}else{
	    // ==============================
	    // get run info from RunInfo tree
	    // ==============================
	    int runinfo_error = runinfo->GetRunInfo(idRun);
	    if( runinfo_error ) throw runinfo_error;
		id_root_l0     = runinfo->ID_ROOT_L0;
	    runheadtime    = runinfo->RUNHEADER_TIME;
	    runtrailtime   = runinfo->RUNTRAILER_TIME;
	    runheadobt    = runinfo->RUNHEADER_OBT;
	    runtrailobt   = runinfo->RUNTRAILER_OBT;
	    evfrom         = runinfo->EV_FROM;
	    evto           = runinfo->EV_TO;
	    nevents        = runinfo->NEVENTS;
	    trk_calib_used = runinfo->TRK_CALIB_USED;

	};

	//
	if(TrkParams::VerboseMode()){
	    cout << "ROOT file ID    "<< id_root_l0     << endl;
	    cout << "RunHeader time  "<< runheadtime    << endl;
	    cout << "RunTrailer time "<< runtrailtime   << endl;
	    cout << " from event  "<< evfrom         << endl;
	    cout << " to event    "<< evto           << endl;
	    cout << " num. events "<< nevents        << endl;
	    cout << "trk-calibration used "<< trk_calib_used << endl;
	};
	// ========================================================
	// second query: search the LEVEL0 file that contains idRun
	// ========================================================
	TString lastfilename = filename;
	if( q3.Query_GL_ROOT(id_root_l0,dbc) )throw -51;
	filename = q3.PATH + q3.NAME;	
	// ========================================================
	// Open the input LEVEL0 data file
	// ========================================================
	if(filename.CompareTo(lastfilename)){
	    if(!lastfilename.IsNull())f0->Close();
	    //if( debug ) cout << "Opening LEVEL0 file: "<< filename << endl;
	    if(TrkParams::VerboseMode()) cout << "Opening LEVEL0 file: "<< filename << endl;
	    FileStat_t t;
	    if( gSystem->GetPathInfo(filename.Data(),t) )throw -6;
	    if ( f0 ) f0->Close();
	    f0 = new TFile(filename);
	    if ( !f0 ) throw -6;
	    physicsTree = (TTree*)f0->Get("Physics");          if(!physicsTree) throw -7;
	    b_header    = physicsTree ->GetBranch("Header");   if(!b_header   ) throw -8;
	    b_trk       = physicsTree ->GetBranch("Tracker");  if(!b_trk      ) throw -203;
	    l0_event->Set();
	    physicsTree->SetBranchAddress("Tracker" ,l0_event->GetPointerToTrackerEvent());
	    physicsTree->SetBranchAddress("Header",&header);
	    
	    nentries = physicsTree->GetEntries(); 
	    if ( nentries < 1 && nevents ) throw -11; // EMILIANO: go on if the file is empty, why not? in principle we should not have any event to process but if the case the next line will take care of exiting; exit only if the file is empty but nevents is not zero
	    if ( nentries < (evto+1) && nevents > 0 ) throw -12; // EMILIANO: if NEVENTS = 0 and the file is empty everything is ok but due to a mistake in the 
								 // variable EV_TO type (UInt_t instead of Int_t) that we don't want to change to avoid changing a lot of code, evto becomes inf hence 
								 // the condition ( nentries < (evto+1) ) is satisfied and DV exit with error even if the error is only in the DB.
	
	};
	
	GL_TIMESYNC *dbtime = new GL_TIMESYNC(id_root_l0,"ID",dbc);	


	// =============================================================
	// retrieve information about parameters to process LEVEL2
	// =============================================================  
	
        TrkParams::Set(runinfo->GetGL_RUN(),dbc);
        for(int i=0; i<p->npar; i++){
          if(TrkParams::VerboseMode())cout<<" ((( force parameters from input path )))"<<endl;
          TrkParams::Set(p->parpath[i],p->partype[i]);
        }

        TrkParams::Load();
        if( !TrkParams::IsLoaded() )throw -52;

	// =============================================================
	// retrieve calibration file needed to reduce data 
	// =============================================================

  	TrkParams::SetCalib(runinfo->GetGL_RUN(),dbc);
  	TrkParams::LoadCalib( );
  	if( !TrkParams::CalibIsLoaded() )throw -53;
	       
	TBenchmark *reduction = new TBenchmark();
	if(TrkParams::VerboseMode())reduction->Start("reduction");
	Int_t ntrk=0;
	// ====================================================
	// start looping on events cointained in the data file  
	// ====================================================		
	if ( TrkParams::GetSimuFlag() ){
          abstime = runheadtime;
        } else {
          if(dbc){
            dbc->Close();
            delete dbc;
          }        
        }

        ev_count =0; // EM, ev_count must be cleared before processing a run (to be consistent when/if reprocessing)
        pr_count =0;

	for (UInt_t re = evfrom+min(p->nskip,nevents); re < evfrom+nevents; re++){

	    ev_count++;

	    //
	    // NBNB check if events are aligned!!!!!!!!!!!
	    //

            /*	    if(p->gete){   10RED BUG: unsync with calo and tof...
	      int iev = 0;
	      int jumped = 0;
	      iev = runinfo->GetFirstEntry() + (re - evfrom -jumped);
	      if ( t_cal->GetEntry(iev) <= 0 ) throw -36;//EV
	      if ( t_tof->GetEntry(iev) <= 0 ) throw -36;//EV
	    } // EMI AAAAGGH COSI` NO!
            */
//	    if ( TrkParams::DebugMode() && re%100 == 0 && re > 0 ) cout << ".";
	    
	    if ( b_trk->GetEntry(re) <= 0 ) throw -36;//EM
	    if ( b_header->GetEntry(re) <= 0 ) throw -36;//EM
	    pscu = header->GetPscuHeader();

	    // =============================================================  
	    // The following 6 lines have been moved here by VALERIO.
	    if(TrkParams::GetSimuFlag()){
              abstime = runheadtime + (int) floor(0.03*(re-evfrom));   //If simulated data we need to assign a fake abstime. 30ms between each event.
              if(TrkParams::VerboseMode())cout << "Event: " << re-evfrom << " - Attempting to retrieve Mask Info for abstime=" << abstime << endl;
              if(!TrkParams::Set(runinfo->GetGL_RUN(),dbc,6,abstime))throw -52; // Setting to load mask (VALERIO)
              TrkParams::Load(6);
              if( !TrkParams::IsLoaded() )throw -52;
              if(TrkParams::VerboseMode())cout << "Mask Info for abstime=" << abstime << " retrieved" << endl;
	    }

	    if( TrkParams::DebugMode() )cout << ">>> "<<ev_count-1<<" @ OBT "<<pscu->GetOrbitalTime()<<endl;
		
	    if ( dbtime->DBabsTime(pscu->GetOrbitalTime()) > (runtrailtime+1) || dbtime->DBabsTime(pscu->GetOrbitalTime()) < (runheadtime-1)) {
			
		if (TrkParams::VerboseMode()){ 
		    printf(" TrkCore - WARNING: event outside the run time window, skipping it\n");
		    cout << " OBT "<<pscu->GetOrbitalTime()<<" RUN "<<runheadobt<<"-"<<runtrailobt<<" ABS-time "<<dbtime->DBabsTime(pscu->GetOrbitalTime())<<" RUN "<<runheadtime<<"-"<<runtrailtime<<endl;
		};		
	    }else{
		if ( TrkParams::DebugMode() )
		    printf("\n-------------------------------\nevent %d\n",re-evfrom);

                if(p->gete){ // must stay here to avoid losing sync with calo and tof in case of skipped events outside time window
                  int iev = 0;
                  iev = runinfo->GetFirstEntry() + pr_count;
                  if ( t_cal->GetEntry(iev) <= 0 ){
                    if (TrkParams::VerboseMode()) printf(" %i %i %i %i \n",iev,runinfo->GetFirstEntry(), pr_count ,evfrom);
                    if ( TrkParams::VerboseMode() ) printf("\n missing calorimeter entry! \n");
                    throw -36;//EV
                  }
                  if ( t_tof->GetEntry(iev) <= 0 ){
                    if (TrkParams::VerboseMode()) printf(" %i %i %i %i \n",iev,runinfo->GetFirstEntry(), pr_count ,evfrom);
                    if ( TrkParams::VerboseMode() ) printf("\n missing tof entry! \n");
                    throw -36;//EV
                  }
                } 
                pr_count++;
		//=============================================
		// EVENT PROCESSING
		//=============================================
		TStopwatch w; 
// 		cout << " -------------------------------------------------------------------------"<<endl;

		// ----------------------------------------------
		//  default tracking
		// ----------------------------------------------
		if(tWatch)w.Start(); 

		p->ProcessEvent(l0_event);//call f77 routine

		if(tWatch)w.Stop(); 	
		if(tWatch)trkWatch.tTrk = (float) w.CpuTime();
//		if(tWatch)cout << " t."<<trkWatch.tTrk<<endl;

					
// 		// ----------------
// 		//  LEVEL1 output
// 		// ----------------		
		l1_event->Clear(); 
		l1_event->SetFromLevel1Struct();

		// ----------------
		//  HOUGH output
		// ----------------
		if(p->geth){
		    if(p->ifrooth){ // root
			lh_event->Delete();
			lh_event->SetFromHoughStruct(&houghevent_);
		    }else{       // hbook
			throw -299;
		    };
		};

// 		// ----------------
// 		//  LEVEL2 output
// 		// ----------------
		l2_event->Clear();
		if(p->get1){
		    l2_event->SetFromLevel2Struct(&level2event_,l1_event);//set references to level1
		}else{
		    l2_event->SetFromLevel2Struct(&level2event_);
		}
		
		if( l2_event->ntrk()>0 )ntrk++;
		if(TrkParams::VerboseMode())l2_event->Dump();


		//----------------------------------------------
		// verify conditions to apply extended algorythm
		//----------------------------------------------
		bool APPLY_EXTENDED = false;
                extAlgFlag = 0; // EM: this is the standard situation, extAlgFlag must be set to zero here. It does not garantuee you have an extended track, it just states
                // that if there were no standard tracks the extended algorithm is used. If not cleared and if there is at least one standard track
                // extAlgFlag retains the previous value that could be DIFFERENT from zero if the previous event was a check event...
		if(
		    p->gete && 	//input flag set	   		    
		    true){

		    // regular condition for extended processing
		    if(l2_event->ntrk()==0){ 
			APPLY_EXTENDED = true;
                        extAlgFlag = 1;  // EM: meaning of extAlgFlag changed: 0 if not used, 1 if used, 2 if used for cross-check, 3 used for cross-check and no std tracks
		    }
		    // extended algorythm applied for cross-check
		    if(p->gete_ncheck>0 && ev_count%p->gete_ncheck==0){
			APPLY_EXTENDED = true; 
			extAlgFlag = 2; // EM read above
                        if(l2_event->ntrk()==0) extAlgFlag = 3; // EM make it easier to recognize events with no standard tracks AND forced extended algorithm // EM read above
		    }
		    
		}

//		cout << re<<" ev_count "<<ev_count<<" apply extended "<<APPLY_EXTENDED<<" flag "<<extAlgFlag<<endl;

		if(tWatch)w.Start(); 

		// ------------------
		//  extended-tracking
		// ------------------
		trkAlg->Clear();   
		if(APPLY_EXTENDED){
		    
// 		    //----------------------------
// 		    // check conditions
// 		    //----------------------------
// 		    if(
// 			l2_event->ntrk()==0 &&
// 			l1_event->nclstr()<24 &&
// 			true){
			
		    trkAlg->SetDebug(false);
		    trkAlg->SetTrkLevel2(l2_event);
		    trkAlg->SetTrkLevel1(l1_event);
		    trkAlg->SetCaloLevel1(cl1);
		    trkAlg->SetToFLevel2(tl2);
		    //    trkAlg->Dump();	
		    trkAlg->ProcessEvent();		  

// 		    }		    
		}

		if(tWatch)w.Stop(); 		
		if(tWatch)trkWatch.tTrkExt = (float) w.CpuTime();
//		if(tWatch)cout << " t."<<trkWatch.tTrkExt<<endl;


		if(p->getn){
		    
		    if(tWatch)w.Start();
 
		    l1_event_nuclei->Clear(); 
		    l1_event_nuclei->Set(l1_event,5.,0.2);//apply a cut
		    l1_event_nuclei->GetLevel1Struct();//from cleaned l1 object, to f77 common
		    l1_event_nuclei->ProcessEvent();   // default routine
		    l2_event_nuclei->Clear();
		    l2_event_nuclei->SetFromLevel2Struct(); // from f77 common to l2 object
		    
		    
		    trkAlg_nuclei->Clear();	

		    if(APPLY_EXTENDED){
			
			trkAlg_nuclei->SetDebug(false);
			trkAlg_nuclei->SetTrkLevel2(  l2_event_nuclei );
			trkAlg_nuclei->SetTrkLevel1(  l1_event_nuclei );//cleaned level1
			trkAlg_nuclei->SetCaloLevel1( cl1    );
			trkAlg_nuclei->SetToFLevel2(  tl2   );
			trkAlg_nuclei->ProcessEvent();
		    }		    

		    if(tWatch)w.Stop(); 
		    if(tWatch)trkWatch.tTrkNuc = (float) w.CpuTime();
//		    if(tWatch)cout << " t."<<trkWatch.tTrkNuc<<endl;

		}
                      

// 		if( l1_event->nclstr() > 15 && trkAlg->GetTrackArray()->GetEntries()>0){

// 		    if(l1_event)cout<<" std n.cl "<<l1_event->nclstr();
// 		    if(l2_event)cout<<" n.tr "<<l2_event->GetNTracks() ;
// 		    if(trkAlg)cout<<" ext  "<<trkAlg->GetTrackArray()->GetEntries();
// 		    cout<<endl;
		    
// 		    trkAlg->SetDebug(true);
// 		    trkAlg->CheckEvent();
// 		    trkAlg->SetDebug(false);


// 		}
		
// 		if( l1_event_nuclei->nclstr() <3 && trkAlg_nuclei->GetTrackArray()->GetEntries()>0){
// 		    if(l1_event_nuclei)cout<<" nuc n.cl "<<l1_event_nuclei->nclstr();
// 		    if(l2_event_nuclei)cout<<" n.tr "<<l2_event_nuclei->GetNTracks() ;
// 		    if(trkAlg_nuclei)cout<<" ext  "<<trkAlg_nuclei->GetTrackArray()->GetEntries();
// 		    cout<<endl;
// 		    trkAlg_nuclei->SetDebug(true);
// 		    trkAlg_nuclei->CheckEvent();
// 		    trkAlg_nuclei->SetDebug(false);
// 		}
		
		/////////////////////
		// ==== FILL ===== //
		/////////////////////
		t_level2->Fill();
		
		if(tWatch){

		    trkWatch.nCl    = (l1_event           ? l1_event->nclstr()                            :0);
		    trkWatch.nClCut = (l1_event_nuclei    ? l1_event_nuclei->nclstr()                     :0);

		    
		    trkWatch.nTrk    = (l2_event          ? l2_event->GetNTracks()                        :0);
		    trkWatch.nTrkNuc = (l2_event_nuclei   ? l2_event_nuclei->GetNTracks()                 :0);
		    
		    trkWatch.nTrkExt    = ( trkAlg        ? trkAlg->GetTrackArray()->GetEntries()        : 0);
		    trkWatch.nTrkExtNuc = ( trkAlg_nuclei ? trkAlg_nuclei->GetTrackArray()->GetEntries() : 0);

		    if(TrkParams::VerboseMode())trkWatch.Dump();


		    tWatch->Fill();
		}

	    };
	}; // end loop on events
	if(TrkParams::VerboseMode()){
	    cout << " >>> processed "<< ev_count <<" events"<< endl;
	    if(ev_count)cout << ntrk << " events with at least one track ("<<(Int_t)(100*ntrk/ev_count)<<"%)\n";
	    reduction->Show("reduction");
	}
	delete reduction;
	
	delete dbtime;

    }; // end loop on runs
    
    
// ------------------------------------------------------------
//  if reprocessing one run, copy all the events AFTER the run
// ------------------------------------------------------------
    if( !(p->standalone) ){
      if(TrkParams::VerboseMode()) cout << " Reprocessing one run: "<<run<< " copy all events AFTER the run"<< endl;
	for(UInt_t i=runinfo->GetLastEntry()+1; i<runinfo->GetFileEntries(); i++){
	    if ( t_clone->GetEntry(i) <= 0 ) throw -36;//EM
	    *l2_event = *l2_clone;
	    // COPY COPY COPY
	    t_level2->Fill();
	    l2_event->Clear();
	};
    };
    // ---------------
    // close the files
    // ---------------
    if(p->get2){
      if(TrkParams::VerboseMode()) cout << " Writing and closing the files, t_level2 "<< t_level2<< endl;
	f2->cd();
//	if(tWatch){
//	if(tWatch)f2->Write("TrkWatch",TObject::kOverwrite);
//	}
//	f2->Write("Tracker", TObject::kOverwrite);
//	if( t_level2 )t_level2->Write();

      	if( t_clone )t_clone->Delete("all");//delete old tree from file

	if( t_level2 )t_level2->SetName("Tracker");
	if( t_level2 )t_level2->Write("Tracker",TObject::kOverwrite);
       	if( t_level2 )t_level2->Delete();  //delete new tree from memory

	if( !(p->standalone) )runinfo->Close();
	if(tWatch)tWatch->Write();
	
    };


    if(f0) if(f0->IsOpen()) f0->Close();
   
    l1_event->Delete();
    l2_event->Delete();
    l2_clone->Delete();
    
        
    return(p->ostatus);
}

