#if !defined(__CINT__) || defined(__MAKECINT__)

#include <PamLevel2.h>

#include <TTree.h>
#include <TChain.h>
#include <TFile.h>
#include <TList.h>
#include <TROOT.h>
#include <TString.h>
#include <TBenchmark.h>
#include <TFriendElement.h>
#include <TObjectTable.h>

#include <stdlib.h>
#include <iostream>
using namespace std;

#endif

//===================================
// global variables
//===================================
Bool_t    DEBUG;
TString   DIR;
TString   LIST;
TString   I_OPTIONS;
TString   O_OPTIONS;
ULong64_t MINEV; 
ULong64_t MAXEV;
TString   OUTFILE;
Bool_t    SIMU;

PamLevel2 *event = NULL;
TChain    *tree  = NULL;
TChain    *runt  = NULL;
TFile     *outfh = NULL;
TFile     *outft = NULL;
TTree     *otree = NULL;
GL_RUN    *run   = NULL;

bool fillTree   = false;
bool fillHistos = false;

//====================================
// Functions to be provided externally
//====================================
bool Select(PamLevel2*);
//void CreateHistos(PamLevel2*, TFile *);
void CreateHistos(PamLevel2*, TFile *);
void FillHistos(PamLevel2*);
void SaveHistos(TFile *);

//==========================================
//000000000000000000000000000000000000000000
//==========================================
bool Begin(){
	
    //------------------------------
    //  create output histos/trees
    //------------------------------
    //if(fillHistos) CreateHistos(event,outfh);
    if(fillHistos) CreateHistos(event, outfh);
    if(fillTree)event->CreateCloneTrees(outft);

    cout << "\nBegin() - done\n\n";
    return 1;
	
}
//==========================================
//000000000000000000000000000000000000000000
//==========================================

bool Process(int iev){

    
    if( !Select(event) )return false;
    if(fillHistos)FillHistos(event);
    if(fillTree)event->FillCloneTrees();//must be after fillhisto

    return true;

}
//==========================================
//000000000000000000000000000000000000000000
//==========================================

bool Finish(){
    
    if(fillHistos){
	SaveHistos(outfh);
	outfh->Close();
    }
    if(fillTree){
	outft->cd();
	event->WriteCloneTrees();
	outft->Close();
    }
    
    cout << "Finish() - done\n";
    
    return 1;
}

//==========================================
//000000000000000000000000000000000000000000
//==========================================
Int_t Loop(TString ddir,TString list, ULong64_t nmax,  ULong64_t nmin, TString ioptions, TString ooptions, TString outfile){

//    gObjectTable->Print();
	
//  ------------------------
//  check output options (I)
//  ------------------------
    if(ooptions.Contains("fillTree"))fillTree=true;
    if(ooptions.Contains("fillHisto"))fillHistos=true;
    
//  -------------------
//  create output files
//  -------------------
    TTree::SetMaxTreeSize(1000*Long64_t(2000000000)); //m 070308 + dimensione oltre la quale divide il file di uscita: 2TB

    TString outfile_t =0;
    TString outfile_h =0;
    if( outfile.IsNull() ){
	if(!list.IsNull())outfile = list(0,list.Last('.'));	    
	else outfile = "output";	    
    }else{	
	if(outfile.Contains(".root"))outfile = outfile(0,outfile.Last('.'));
    }
    if(fillTree){
	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 1;
	};
	cout << "Created output file: "<<outft->GetName()<<endl;
    }
    if(fillHistos){
	outfile_h = outfile;
	outfile_h.Append("-histo.root");
	outfh = (TFile*)gROOT->FindObject(outfile_h); if (outfh) outfh->Close();
	outfh = new TFile(outfile_h,"RECREATE");
	if(outfh->IsZombie()){
	    cout << "Output file could not be created\n";
	    return 1;
	};
	cout << "Created output file: "<<outfh->GetName()<<endl;
    }
    
//  --------------------
//  read input file/list
//  --------------------
    event = new PamLevel2();
    event->SetGP(SIMU);//new
    if(SIMU)cout << " >> SIMULATION << "<<event->IsGP()<<endl;
    event->SetDBConnection();
    if(DEBUG)gObjectTable->Print();
    if(list.Contains(".root")){
	TString tempfile = gSystem->BaseName(list.Data());
//	tempfile.Replace(tempfile.Index(".root",5),5,".txt",4);
	tempfile.Append(".txt");
	cout << "Creating temporary file: "<<tempfile<<endl;
	stringstream command;
	command.str("");
	command << " echo "<<list<<" > "<<tempfile;
	cout << command.str().c_str() << endl;
	gSystem->Exec(command.str().c_str());
	tree  = event->GetPamTree(ddir,tempfile.Data(),ioptions);
	runt  = event->GetRunTree(ddir,tempfile.Data());
	command.str("");
	command << "rm -f "<<tempfile;
	gSystem->Exec(command.str().c_str());
    }else{	 
	tree  = event->GetPamTree(ddir,list,ioptions);
	runt  = event->GetRunTree(ddir,list);
    };

   
//    gObjectTable->Print();
    tree->SetCacheSize(0);

//  -------------------------
//  check output options (II)
//  -------------------------
    if(fillTree)event->SetWhichTrees(ooptions); 

//  ---------------
//  initializations
//  ---------------
    if( !Begin() )return 0;;

//  -----------------
//  loop over events
//  -----------------
    ULong64_t nevents = tree->GetEntries();	
    if(!nmax)nmax = numeric_limits<ULong64_t>::max();	
    if(nevents < nmax)nmax=nevents;
    nmax=nmax-nmin;
    
    cout << endl<<" Start loop over events:  "<< nmax <<endl;
    Int_t ntrk = 0;
    Int_t ncls = 0;
    Int_t sel  = 0;
    Int_t get  = 0;
    TBenchmark *benchmark = new TBenchmark();
    benchmark->Start("event-loop");
    
    TString current_file = "";
    if(DEBUG)gObjectTable->Print();
    for(ULong64_t iev=nmin; iev<nmin+nmax; iev++){
	
	event->Clear();
//	if( tree->GetEntry(iev) ){
	if( event->GetEntry(iev) ){ //<<< new feature
	    get++;
	    if( Process(iev) ){
//		cout << "%%%% "<<iev << endl;
		sel++;
	    }
	    if(current_file.CompareTo(tree->GetFile()->GetName())){
		current_file=tree->GetFile()->GetName();
		cout <<endl<<"@ entry "<< iev<< " -> "<< current_file << endl;
	    };
	}else{
	    cout << "Chain entry "<<iev<<" -- ERROR --"<<endl;
	};
	if( !(iev%5000) && DEBUG)gObjectTable->Print();
	
	if( !(iev%1000) )cout <<"|";

    };
    cout <<endl;
    benchmark->Show("event-loop");
    cout << sel <<" selected events over "<<get;
    if(get)cout<<" ("<< 100*sel/get<<"%)"<<endl;
    if(DEBUG)gObjectTable->Print();
    event->Clear();
//  --------------
//  close and exit
//  --------------
    Finish();

    return 0;
    
}

/////////////////////////////////////////////////////////////////

#if !defined(__CINT__) 

// // input parameters
// Bool_t    DEBUG;
// TString   DIR;
// TString   LIST;
// TString   OPTIONS;
// ULong64_t MAXEV;
// TString   OUTFILE;

void usage(){

    cout << "------------------------------------------------------------"<<endl;
    cout << "Loop over events (on one or more Level2-files), applying some selection cuts, \n";
    cout << "creating output histograms and/or trees with selected events. \n \n ";
    cout << "USAGE:"<<endl;
    cout << "-processDir  DIR     -  Level2 data directory \n";
    cout << "-processList LIST    -  list  of files (.txt) or single file (.root) to be analysed \n";
    cout << "-outputFile  PATH    -  name of the output file \n";
    cout << "-NumEvents   XXX     -  number of events to be analysed \n";
    cout << "-FirstEvent  XXX     -  first event to be analysed \n";
    cout << "--debug, -g          -  debug mode \n";
    cout << "--help, -h           -  print this help \n";
    cout << "-input-options [ options ] -  options: \n";
    cout << "                        +(-)ALL    --> inlcude(exclude) all default level1/level2 trees and branches \n "   ;
    cout << "                        +(-)TRK1 +(-)TRK2 +(-)CAL1 +(-)CAL2 +(-)TOF +(-)TRG +(-)ND +(-)S4 +(-)ORB --> inlcude(exclude) level1/level2 trees and branches \n"  ;
    cout << "                        +TRK0 +CAL0 +TOF0 --> include level0 branches \n"  ;
    cout << "-output-options [ options ] -  options: \n";
    cout << "                        fillHistos --> create an output file with histograms  \n";
    cout << "                        fillTree   --> create an output file with trees storing the selected events \n ";
    cout << "                        == NB == "<<endl;
    cout << "                        output level1/level2 tree is created with the same trees/branches as read from input file."<<endl;
    cout << "                        in order to modify the output structure use the following options:"<<endl;
    cout << "                        +(-)ALL    --> inlcude(exclude) all trees and branches \n "   ;
    cout << "                        +(-)TRK1 +(-)TRK2 +(-)CAL1 +(-)CAL2 +(-)TOF +(-)TRG +(-)ND +(-)S4 +(-)ORB --> inlcude(exclude) trees and branches \n"  ;
    cout << "--simulation, -s     -  simulation file \n";    
    cout << "------------------------------------------------------------"<<endl;
}
//
int HandleInputPar(int argc, char **argv){

    if(argc>1){
		
	if(!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help") ){
	    usage();
	    return(1);
	};
// -----------------------
// Read input parameters
// -----------------------  
	DEBUG   = false;
	DIR     = gSystem->WorkingDirectory();
	LIST    = "";
	OUTFILE = "";
	I_OPTIONS = "+ALL ";
	O_OPTIONS = "fillTree";
	MAXEV   = 0;	
	MINEV   = 0;	
	SIMU    = false;	
		

	for (int i = 1; i < argc; i++){		
	    // -----------------------------------------------------//
	    if (!strcmp(argv[i], "-processDir")){
		if (++i >= argc) throw -1;
		DIR = argv[i];
		cout << "processDir "<<DIR<<endl;
		continue;
	    }
	    // -----------------------------------------------------//
	    else if (!strcmp(argv[i], "-processList")){
		if (++i >= argc) throw -1;
		LIST = argv[i];
		cout << "processList "<<LIST<<endl;
		continue;
	    }  
	    // -----------------------------------------------------//
	    else if (!strcmp(argv[i], "-outputFile")){
		if (++i >= argc) throw -1;
		OUTFILE = argv[i];
		cout << "outputFile "<<OUTFILE<<endl;
		continue;
	    }  
	    // -----------------------------------------------------// 
	    else if (!strcmp(argv[i], "-FirstEvent")){                 
		if (++i >= argc) throw -1;                             
		MINEV = atoi(argv[i]);                                 
		cout << "FirstEvent "<<MINEV<<endl;                    
		continue;	                                       
	    }                                                          
	    // -----------------------------------------------------//
	    else if (!strcmp(argv[i], "-NumEvents")){
		if (++i >= argc) throw -1;
		MAXEV = atoi(argv[i]);
		cout << "NumEvents "<<MAXEV<<endl;
		continue;	
	    }
	    // -----------------------------------------------------//
	    else if (!strcmp(argv[i], "-input-options")){
		if (++i >= argc) throw -1;
		I_OPTIONS = argv[i];
		if( I_OPTIONS.Contains("[") ){
		    do{
			if (++i >= argc) throw -1;
			I_OPTIONS.Append(argv[i]);
		    }while(!I_OPTIONS.Contains("]"));
		}else cout << "wrong option format --> ignoring " << endl;
	    }
	    // -----------------------------------------------------//
	    else if (!strcmp(argv[i], "-output-options")){
		if (++i >= argc) throw -1;
		O_OPTIONS = argv[i];
		if( O_OPTIONS.Contains("[") ){
		    do{
			if (++i >= argc) throw -1;
			O_OPTIONS.Append(argv[i]);
		    }while(!O_OPTIONS.Contains("]"));
		}else cout << "wrong option format --> ignoring " << endl;
	    }
	    // -----------------------------------------------------//
	    else if (!strcmp(argv[i], "--debug") || !strcmp(argv[i], "-g")){
		DEBUG = true;
		continue;	
	    }
	    // -----------------------------------------------------//
	    else if (!strcmp(argv[i], "--simulation") || !strcmp(argv[i], "-s")){
		SIMU = true;
		continue;	
	    }
	    // -----------------------------------------------------//
	    else{
		cout << "Unidentified input parameter. Ignored."<< endl;
	    };
	};
    }else{
	usage();
	return(1);
    };
// -----------------------
// Check input parameters
// -----------------------  
	
		
    return(0);
	
};
// 

int main(int argc, char **argv)
{
	
    if( HandleInputPar(argc,argv) )return(1);
	
//    Loop(DIR,LIST,MAXEV,"-ALL+TRK1+TRK2+CAL1+CAL2+TOF+AC",OUTFILE);
    cout << "INPUT OPTIONS "<<I_OPTIONS<<endl;
    cout << "OUTPUT OPTIONS "<<O_OPTIONS<<endl;
    Loop(DIR,LIST,MAXEV,MINEV,I_OPTIONS,O_OPTIONS,OUTFILE);
    
    cout << "Back to main - end"<<endl;
    
    return 0;
	
};

#endif
