//
#include <stdlib.h>
#include <iostream>
//
#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 <PamLevel2.h>
//
using namespace std;

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

PamLevel2 *event = NULL;
TFile     *outfh = NULL;


//====================================
// Functions to be provided externally
//====================================
bool Select(PamLevel2*);
void CreateHistos(PamLevel2*, TString);
void FindAverage(PamLevel2*,int);
void FindMatrix(PamLevel2*,int);
void SaveHistos();
void CalculateAverage();

//==========================================
//000000000000000000000000000000000000000000
//==========================================
bool Begin(TString outfile){
	
  //------------------------------
  //  create output histos/trees
  //------------------------------
  CreateHistos(event,outfile);
  cout << "\nBegin() - done\n\n";
  return 1;
	
}
//==========================================
//000000000000000000000000000000000000000000
//==========================================

bool Process1(int iev){

    
  if( !Select(event) )return false;

  if ( MATRIX ){
    FindMatrix(event,iev);    
  } else {
    FindAverage(event,iev);
  };
  return true;

}

//==========================================
//000000000000000000000000000000000000000000
//==========================================

bool Finish(){
    
  SaveHistos();
  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){

  //  -------------------
  //  create output files
  //  -------------------
  TTree::SetMaxTreeSize(1000*Long64_t(2000000000)); //m 070308 + dimensione oltre la quale divide il file di uscita: 2TB

  //  --------------------
  //  read input file/list
  //  --------------------
  event = new PamLevel2(ddir,list,ioptions);
  event->SetSortingMethod("CALS1S2");
  if ( SIMU ) event->SetSortingMethod("+GP");
  //  tree->SetCacheSize(0);

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

  //  -----------------
  //  loop over events 
  //  -----------------
  ULong64_t nevents = event->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;
    
  TString current_file = "";

  for(ULong64_t iev=nmin; iev<nmin+nmax; iev++){
    //
    event->Clear();
    if( event->GetEntry(iev) ){
      Process1(iev);
    }else{
      cout << "Chain entry "<<iev<<" -- ERROR --"<<endl;
    };

  };

  if ( !MATRIX ) CalculateAverage();

  event->Clear();
  //  --------------
  //  close and exit
  //  --------------
  Finish();

  return 0;
    
}

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

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 << "-processFile file    -  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 << "------------------------------------------------------------"<<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
    // -----------------------  
    SIMU = false;
    CRIG = false;
    SRIG = false;
    DEBUG   = false;
    MATRIX  = false;
    FULL = false;
    DIR     = gSystem->WorkingDirectory();
    LIST    = "";
    OUTFILE = "";
    I_OPTIONS = "+AUTO ";
    O_OPTIONS = "fillHistos";
    MAXEV   = 0;	
    MINEV   = 0;	
		

    for (int i = 1; i < argc; i++){		
      // -----------------------------------------------------//
      if (!strcmp(argv[i], "-processFile")){
	if (++i >= argc) throw -1;
	LIST = argv[i];
	cout << "processFile "<<LIST<<endl;
	continue;
      }  
      // -----------------------------------------------------//
      else if (!strcmp(argv[i], "-processDir")){
	if (++i >= argc) throw -1;
	DIR = argv[i];
	cout << "processDir "<<DIR<<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], "-matrix")){
	MATRIX = true;
	continue;	
      }
      // -----------------------------------------------------//
      //
      else if (!strcmp(argv[i], "-simu")){
	SIMU = true;
	continue;	
      }
      // -----------------------------------------------------//
      //
      else if (!strcmp(argv[i], "-calorig")){
	CRIG = true;
	continue;	
      }
      // -----------------------------------------------------//
      //
      else if (!strcmp(argv[i], "-simurig")){
	SRIG = true;
	continue;	
      }
      // -----------------------------------------------------//
      else if (!strcmp(argv[i], "-full")){
	FULL = 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);
	
  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;
	
};

