/**
 * \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>

// .........................................................
// 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>

// .........................................................
// 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[])   
{
    // ---------------------------
    // Define some basic variables
    // ---------------------------

    TString  filename     = 0; 
//    TString  trkcalibfile = 0;
//    Int_t    last_trk_calib_used = 0;
    Long64_t nentries = 0LL;
    
    // LEVEL0 input 
    TFile         *f0 = 0;
//    TFile         *f0_c = 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;
    //LEVEL2 output - ROOT
    TTree     *t_level2 = 0; 
    TTree     *t_clone = 0; 
    TrkLevel2 *l2_event = new TrkLevel2();
    TrkLevel2 *l2_clone = new TrkLevel2();
    //LEVEL1 output - ROOT
    TrkLevel1    *l1_event = new TrkLevel1();
    TrkHough     *lh_event = new TrkHough();
// -----------------------
// -----------------------
// 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);
    p->HandleCustomPar(ncustom,vcustom);
    if( p->DebugMode() )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);
	    if( runinfo_error ) throw runinfo_error;
	    runlist = runinfo->GetRunList();
	    reprocessing = runinfo->IsReprocessing();//????
	    if(p->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);
	// Create NEW Tracker tree			
	t_level2 = new TTree("Tracker","PAMELA tracker level2 data ");
	l2_event->Set(); // ****NBNBNBN*****
	t_level2->Branch("TrkLevel2","TrkLevel2",&l2_event);
	if(p->get1){
	    if(p->VerboseMode())cout << endl << "Requested LEVEL1 output" << endl;
	    l1_event->Set(); // ****NBNBNBN*****
	    t_level2->Branch("TrkLevel1","TrkLevel1",&l1_event);
	}
	if(p->geth){
	  if(p->VerboseMode())cout << endl << "Requested Hough-Transform output" << endl;
	  t_level2->Branch("TrkHough","TrkHough",&lh_event);
	};
    };
	

// -------------------------------------------
// 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 "expiration date" of calibrations and parameters
    //
//     UInt_t pedsig_time =0;
//     UInt_t B_time      =0;
//     UInt_t mip_time    =0;
//     UInt_t charge_time =0;
//     UInt_t eta_time    =0;
//     UInt_t align_time  =0;
//     UInt_t mask_time   =0;
    //
    // init event counter
    //
    Int_t ev_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) ){
	for(UInt_t i=0; i<runinfo->GetFirstEntry(); i++){
	    t_clone->GetEntry(i);
	    *l2_event = *l2_clone;
	    t_level2->Fill();
	    l2_event->Clear();
	    // COPY COPY COPY
	};
    };
// ------------------------------------------------------------
// ------------------------------------------------------------
//  START LOOP OVER RUNS TO PROCESSED/REPROCESSED
// ------------------------------------------------------------
// ------------------------------------------------------------
    for(UInt_t idRun = from_run; idRun <= to_run; idRun++){
	
	if(p->VerboseMode()) cout << endl<<" ========================= Run: "<< idRun << endl;
	UInt_t runheadtime       = 0;
	UInt_t runtrailtime      = 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;

	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;
	    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;
	    evfrom         = runinfo->EV_FROM;
	    evto           = runinfo->EV_TO;
	    nevents        = runinfo->NEVENTS;
	    trk_calib_used = runinfo->TRK_CALIB_USED;

	};

	//
	if(p->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(p->VerboseMode()) cout << "Opening LEVEL0 file: "<< filename << endl;
	    FileStat_t t;
	    if( gSystem->GetPathInfo(filename.Data(),t) )throw -6;
	    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 ) throw -11;
	    if ( nentries < (evto+1)) throw -12;
	
	};
	
	GL_TIMESYNC *dbtime = new GL_TIMESYNC(id_root_l0,"ID",dbc);	


	// =============================================================
	// retrieve information about parameters to process LEVEL2
	// =============================================================  
	
	TrkParams::Set(runinfo->GetGL_RUN(),dbc);
	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;
	       
	// =============================================================
	// retrieve calibration file needed to reduce data 
	// =============================================================
	// if run OBT is > last calibration "expiration date"
	//  - search for new calibration packet
	//  - load calibration parameters (full + truncated)
	// =============================================================
// 	if(p->VerboseMode() )cout << "Full pedestals for cluster finding:";
// 	if(runheadtime > pedsig_time){
// 	    if( q2.Query_GL_TRK_CALIB(runheadtime,dbc) )throw -53;
// 	    pedsig_time = q2.TO_TIME;
// 	    if(q2.EV_ROOT_CALIBTRK1 != q2.EV_ROOT_CALIBTRK2) 
// 		printf("WARNING!! ---> EV_ROOT_CALIBTRK1=%d it's different from EV_ROOT_CALIBTRK2=%d \n\n",q2.EV_ROOT_CALIBTRK1,q2.EV_ROOT_CALIBTRK2);
// 	    if( q3.Query_GL_ROOT(q2.ID_ROOT_L0,dbc) )throw -51;
// 	    trkcalibfile = q3.PATH + q3.NAME;
// 	    if(p->VerboseMode()) cout << " >> Loading from LEVEL0 file: "<< trkcalibfile << endl;

// 	    if(trkcalibfile.CompareTo(filename)){
// 		FileStat_t t;
// 		if( gSystem->GetPathInfo(trkcalibfile.Data(),t) )throw -6;
// 		f0_c=new TFile(trkcalibfile);
// 		if ( !f0_c ) throw -6;
// 	    }else{
// 		f0_c = f0;
// 	    };
// 	    if(p->VerboseMode()) {
// 		cout << " calibration entries "<< q2.EV_ROOT_CALIBTRK1 << " " << q2.EV_ROOT_CALIBTRK2; 
// 		cout << " (from time "<< q2.FROM_TIME <<" to time "<< q2.TO_TIME <<")"<<endl;
// 	    };
// 	    TrkParams::FillACalibFrom(f0_c,q2.EV_ROOT_CALIBTRK1,q2.EV_ROOT_CALIBTRK2);
// 	    TrkParams::FillMask(f0_c,q2.EV_ROOT_CALIBTRK1,q2.EV_ROOT_CALIBTRK2);
	    
// 	}else{
// 		if(p->VerboseMode() )cout<< " already loaded. "<< endl;
// 	};
// 	if( p->DebugMode() ) for(int i=0; i<12; i++) cout << " DSP "<< i << " "<< pedsigbad_.pedestal[64][12][i] << endl;
	// =============================================================
	// retrieve calibration file needed to uncompress data
	// =============================================================
	// if the run was compressed using default calib
	// load truncated pedestals from default
	// otherwise reload them from on-line calibration
	// =============================================================
// 	if(p->VerboseMode() )cout << "Truncated pedestals for uncompression:";
// 	if(trk_calib_used==104 && last_trk_calib_used!=104){
	    
// 	    if(p->VerboseMode() )cout << " >> Loading default calibration " << endl; 
// 	    if (q4.Query_GL_PARAM(runheadtime,7,dbc) )throw -52;	    
// 	    TrkParams::FillTCalibFrom(q4.PATH+q4.NAME);
	    
// 	}else if( (trk_calib_used==1 | trk_calib_used==2) &&  last_trk_calib_used==104  ){

// 	    if(p->VerboseMode()) cout << ">> Re-loading on-line calibration " << endl;
// 	    TrkParams::FillTCalibFrom(f0_c,q2.EV_ROOT_CALIBTRK1,q2.EV_ROOT_CALIBTRK2);
// 	}else{
// 	    if(p->VerboseMode()) cout << ">> already loaded " << endl;
// 	};
// 	if( p->DebugMode() ) for(int i=0; i<12; i++) cout << " DSP "<< i << " "<< pedsigbad_.pedestal_t[64][12][i] << endl;
// 	last_trk_calib_used = trk_calib_used;
	


// 	//
// 	// magnetic field
// 	//
// 	if(p->VerboseMode()) cout << "Loading parameter files : "<< endl;
	
//  	if(runheadtime > B_time){
//  	    if( q4.Query_GL_PARAM(runheadtime,1,dbc) )throw -52;
//  	    B_time = q4.TO_TIME;
//  	    l2_event->LoadField(q4.PATH+q4.NAME);
//  	    if(path_.error) throw -215;
//  	};
	
// 	//
// 	// mip conversion parameters
// 	//
// 	if(runheadtime > mip_time){
// 	    if( q4.Query_GL_PARAM(runheadtime,2,dbc) )throw -52;
// 	    mip_time = q4.TO_TIME;
// 	    path_.FillWith(q4.PATH+q4.NAME);
// 	    readmipparam_();
// 	    if(path_.error) throw -212;
// 	};
// 	//
// 	// charge correlation parameters
// 	//
// 	if(runheadtime > charge_time){
// 	    if( q4.Query_GL_PARAM(runheadtime,3,dbc) )throw -52;
// 	    charge_time = q4.TO_TIME;
// 	    path_.FillWith(q4.PATH+q4.NAME);
// 	    readchargeparam_();
// 	    if(path_.error) throw -213;
// 	};
// 	//
// 	// eta p.f.a. parameters
// 	//
// 	if(runheadtime > eta_time){
// 	    if( q4.Query_GL_PARAM(runheadtime,4,dbc) )throw -52;
// 	    eta_time = q4.TO_TIME;
// 	    path_.FillWith(q4.PATH+q4.NAME);
// 	    readetaparam_();
// 	    if(path_.error) throw -214;
// 	};
// 	//
// 	// alignment parameters
// 	//
// 	if(runheadtime > align_time){
// 		if( q4.Query_GL_PARAM(runheadtime,5,dbc) )throw -52;
// 	    align_time = q4.TO_TIME;
// 	    path_.FillWith(q4.PATH+q4.NAME);
// 	    readalignparam_();
// 	    if(path_.error) throw -211;
// 	};
// 	//
// 	// viking mask 
// 	//
// 	if(runheadtime > mask_time){
// 	    if( q4.Query_GL_PARAM(runheadtime,6,dbc) )throw -52;
// 	    mask_time = q4.TO_TIME; 
// 	    path_.FillWith(q4.PATH+q4.NAME);
// 	    readvkmask_();
// 	    if(path_.error) throw -210;
// 	};

	TBenchmark *reduction = new TBenchmark();
	if(p->VerboseMode())reduction->Start("reduction");
	Int_t ntrk=0;
	// ====================================================
	// start looping on events cointained in the data file  
	// ====================================================		
	for (UInt_t re = evfrom; re < evfrom+nevents; re++){
	    
	    ev_count++;
	    
//	    if ( p->DebugMode() && re%100 == 0 && re > 0 ) cout << ".";
	    
	    b_trk->GetEntry(re);
	    b_header->GetEntry(re);
	    pscu = header->GetPscuHeader();
		
	    if( p->DebugMode() )cout << ">>> "<<ev_count-1<<" @ OBT "<<pscu->GetOrbitalTime()<<endl;
		
	    if ( dbtime->DBabsTime(pscu->GetOrbitalTime()) > runtrailtime || dbtime->DBabsTime(pscu->GetOrbitalTime()) < runheadtime) {
			
		if (p->VerboseMode()){ 
		    printf(" TrkCore - WARNING: event outside the run time window, skipping it\n");
		    cout << " OBT "<<pscu->GetOrbitalTime()<<" ABS-time "<<dbtime->DBabsTime(pscu->GetOrbitalTime())<<" RUN "<<runheadtime<<"-"<<runtrailtime<<endl;
		};		
	    }else{
		if ( p->DebugMode() )
		    printf("\n-------------------------------\nevent %d\n",re-evfrom);

		p->ProcessEvent(l0_event);
					
		// ----------------
		//  LEVEL1 output
		// ----------------
		if(p->get1){
		    if(p->ifroot1){ // root
			l1_event->Clear();
//			l1_event->SetFromLevel1Struct(&level1event_,p->full1);
			l1_event->SetFromLevel1Struct(p->full1);
			//				t_level1->Fill();
		    }else{       // hbook
			throw -299;
		    };
		};
		// ----------------
		//  HOUGH output
		// ----------------
		if(p->geth){
		    if(p->ifrooth){ // root
			lh_event->Delete();
			lh_event->SetFromHoughStruct(&houghevent_);
		    }else{       // hbook
			throw -299;
		    };
		};
		// ----------------
		//  LEVEL2 output
		// ----------------
		if(p->get2){
		    l2_event->Clear();
		    if(p->get1){
			l2_event->SetFromLevel2Struct(&level2event_,l1_event);//set references to level1
		    }else{
			l2_event->SetFromLevel2Struct(&level2event_);
		    }
//				l2_event->Dump();
		    t_level2->Fill();
		    if( l2_event->ntrk()>0 )ntrk++;
		};
	    };
	}; // end loop on events
	if(p->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) ){
	for(UInt_t i=runinfo->GetLastEntry()+1; i<runinfo->GetFileEntries(); i++){
	    t_clone->GetEntry(i);
	    *l2_event = *l2_clone;
	    t_level2->Fill();
	    l2_event->Clear();
	    // COPY COPY COPY
	};
    };
    // ---------------
    // close the files
    // ---------------
    if(p->get2){
	if( t_clone )t_clone->Delete("all");//delete old tree from file
	if( !(p->standalone) )runinfo->Close();
	f2->Write("Tracker");
	if( t_level2 )t_level2->Delete();  //delete new tree from memory
	
    };
    
    if(f0) if(f0->IsOpen()) f0->Close();
//    if( f0_c->IsOpen() )f0_c->Close();
    
//    lh_event->Delete();
    l1_event->Delete();
    l2_event->Delete();
    l2_clone->Delete();
    
    return(p->ostatus);
}

