//
// C/C++ headers
//
#include <fstream>
#include <string.h>
//
// ROOT headers
//
#include <TTree.h>
#include <TClassEdit.h>
#include <TObject.h>
#include <TList.h>
#include <TArrayL.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 <TClonesArray.h>
//
// YODA headers
//
#include <PamelaRun.h>
#include <RegistryEvent.h>
#include <physics/trigger/TriggerEvent.h>
//
// RunInfo header
//
#include <RunInfo.h>
//
// This program headers
//
#include <MyDect1Core.h>
#include <MyDect1Level2.h>
#include <MyDect1Verl2.h>
//
using namespace std;
//
//
// CORE ROUTINE
//
//
int MyDect1Core(ULong64_t run, TString filename, TSQLServer *dbc){
  //
  // Set these to true to have a very verbose output.
  //
  //  Bool_t debug = true;
  Bool_t debug = false;
  //
  // Output directory is the working directoy.
  //
  const char* outdir = gSystem->WorkingDirectory();
  //
  // Working filename
  //
  TString outputfile;
  stringstream name;
  name.str("");
  name << outdir << "/";
  //
  // Variables.
  //
  TFile *file = 0;
  TTree *mydect1 = 0;
  TTree *mydect1clone = 0;
  //
  Bool_t reproc = false;
  Bool_t reprocall = false;
  Long64_t nevents = 0LL;
  UInt_t nobefrun = 0;
  UInt_t noaftrun = 0;
  UInt_t numbofrun = 0;
  //
  Int_t code = 0;
  Int_t sgnl;
  //
  // mydetector1 level2 classes
  //
  MyDect1Level2 *mydec = new MyDect1Level2();
  MyDect1Level2 *mydecclone = new MyDect1Level2();
  //
  // MySQL variables
  //
  TSQLResult *pResult;
  TSQLRow *Row;
  Int_t t;
  Int_t r;
  UInt_t procev = 0;
  stringstream myquery;
  //
  // define variables where to store the absolute run header and run trailer times (unsigned long long integers, when set to a number use ULL to store the correct number).
  //
  ULong64_t runheadtime = 0ULL;
  ULong64_t runtrailtime = 0ULL;
  UInt_t evfrom = 0;
  UInt_t evto = 0;
  Long64_t totfileentries = 0ULL;
  Long64_t idRun = 0LL;
  Int_t id_reg_run=-1;
  stringstream ftmpname;
  TString fname;
  //
  // define variables for opening and reading level0 file
  //
  TFile *l0File = 0;
  TTree *l0tr = 0;
  TBranch *l0head = 0;
  TBranch *l0registry = 0;
  TBranch *l0trig = 0;
  pamela::RegistryEvent *l0reg=0;
  pamela::trigger::TriggerEvent *trig = 0;
  //
  TString mydect1version;
  ItoRunInfo *runinfo = 0;
  TArrayL *runlist = 0;
  Int_t totevent = 0;
  // 
  //
  ULong64_t atime = 0ULL;
  Int_t ei = 0; 
  Int_t S3 = 0;
  Int_t S2 = 0;
  Int_t S12 = 0;
  Int_t S11 = 0;
  UInt_t re = 0;
  Float_t tmptrigty = -1.;      
  //
  // As a first thing we must check what we have to do: if run = 0 we must process all events in the file has been passed
  // if run != 0 we must process only that run but first we have to check if the branch calorimeter already exist in the file
  // if it exists we are reprocessing data and we must delete that entries, if not we must create it.
  // 
  if ( run != 0ULL ){
    //
    // Run is not 0, we must process only one run .
    //
    //
    // If no filename is given, use the deafult one run_id.Level2.root.
    //
    if ( !strcmp(filename.Data(),"") ){
      name << run << ".Level2.root";
    } else {
      name << filename.Data();
    };
    //
  } else {
    //
    // Run is 0, we must process all entries in the given file.
    //
    //
    // OUCH! no filename has been given, run out!
    //
    if ( !strcmp(filename.Data(),"") ) {
      printf(" MYDETECTOR1 - ERROR: processing all runs but no filename is given\n");
      return(-4);
    };
    name << filename.Data();
    reproc = true;
    //
  };
  //
  // Output file is "outputfile"
  //
  outputfile = name.str().c_str(); 
  printf("\n Output filename is: \n %s \n\n",outputfile.Data());
  //
  // if file exists open it in "update" mode, if not create it (UPDATE will do it).
  //
  file = new TFile(outputfile.Data(),"UPDATE");
  if ( !file->IsOpen() ){
    printf(" MYDETECTOR1 - ERROR: cannot open file for writing\n");
    return(-101);    
  };
  //
  // Update versioning information for the run(s) we are going to process. We do it calling the RunInfo constructor.
  //
  mydect1version = MyDect1Info(false);
  sgnl = 0;
  runinfo = new ItoRunInfo(dbc,file,"mydetector1Folder",run,"MYDECT1",mydect1version,sgnl);
  runinfo->Update();
  if ( sgnl ){
    printf(" MYDETECTOR1 - ERROR: RunInfo exited with non-zero status\n");
    code = sgnl;
    goto closeandexit;
  } else {
    sgnl = 0;
  };
  //
  // number of events in the file BEFORE the first event of our run
  //
  nobefrun = runinfo->GetFirstFileEntry();
  //
  // total number of events in the file 
  //
  totfileentries = runinfo->GetFileEntries();
  //
  // first file entry AFTER the last event of our run
  //
  noaftrun = runinfo->GetLastFileEntry();
  //
  // number of run to be processed
  //
  numbofrun = runinfo->GetNoRun();
  //
  //
  // Try to access the MyDetector1 tree in the file, if it exists we are reprocessing data if not we are processing a new run
  //
  mydect1clone = (TTree*)file->Get("MyDetector1");
  //
  // create a new MyDetector1 tree called mydect1
  // 
  mydect1 = new TTree("MyDetector1","PAMELA Level2 MyDetector1 data");
  mydect1->Branch("MyDect1Level2","MyDect1Level2",&mydec);
  //
  if ( !mydect1clone ){
    //
    // tree does not exist, we are not reprocessing
    //
    reproc = false;
    if ( run == 0ULL ) printf(" MYDETECTOR1 - WARNING: you are reprocessing data but mydetector1 tree does not exist!\n");
    if ( runinfo->IsReprocessing() && run != 0ULL ) printf(" MYDETECTOR1 - WARNING: it seems you are not reprocessing data but mydetector1\n versioning information already exists in RunInfo.\n");

  } else {
    //
    // tree exists, we are reprocessing data. Are we reprocessing a single run or all the file?
    //
    reproc = true;
    //
    if ( run == 0ULL ){
      //
      // we are reprocessing all the file
      //
      //
      // if we are reprocessing everything we don't need to copy any old event and we can just work with the new tree and delete the old one immediately
      //
      printf("\n MYDETECTOR1 - WARNING: Reprocessing all runs\n");
      reprocall = true;
      mydect1clone->Delete("all");
    } else {
      reprocall = false;
      //
      // we are reprocessing a single run, we must copy to the new tree the events in the file which preceed the first event of the run
      //
      printf("\n MYDETECTOR1 - WARNING: Reprocessing run number %i \n",(int)run);
      //
      // access old branch in old tree
      //
      mydect1clone->SetBranchAddress("MyDect1Level2",&mydecclone);
      //      
      if ( nobefrun > 0 ){
	printf("\n Pre-processing: copying events from the old tree before the processed run\n");   
	printf(" Copying %i events in the file which are before the beginning of the run %i \n",(int)(nobefrun),(int)run);
	printf(" Start copying at event number 0, end copying at event number %i \n",(int)nobefrun);
	for (UInt_t j = 0; j < nobefrun; j++){
	  //
	  mydect1clone->GetEntry(j);	  
	  //
	  // copy caclone to ca
	  //
	  memcpy(&mydec,&mydecclone,sizeof(mydec));
	  //
	  // Fill entry in the new tree
	  //
	  mydect1->Fill();
	  //
	};
	printf(" Finished successful copying!\n");
      };          
    };
  };
  //
  // Get the list of run to be processed, if only one run has to be processed the list will contain one entry only.
  //
  runlist = runinfo->GetRunList();
  //
  // Loop over the run to be processed
  //
  for (UInt_t irun=0; irun < numbofrun; irun++){
    //
    // retrieve the first run ID to be processed using the RunInfo list
    //
    idRun = runlist->At(irun);
    printf("\n\n\n ####################################################################### \n");
    printf("                    PROCESSING RUN NUMBER %i \n",(int)idRun);
    printf(" ####################################################################### \n\n\n");
    //
    // store in the runinfo class the GL_RUN variables for our run
    //
    sgnl = runinfo->GetRunInfo(idRun);
    if ( sgnl ){
      printf(" MYDETECTOR1 - ERROR: RunInfo exited with non-zero status\n");
      code = sgnl;
      goto closeandexit;
    } else {
      sgnl = 0;
    };
    //
    // now you can access that variables using the RunInfo class this way
    //
    id_reg_run = runinfo->ID_REG_RUN;
    runheadtime = runinfo->RUNHEADER_TIME;
    runtrailtime = runinfo->RUNTRAILER_TIME;
    evfrom = runinfo->EV_REG_PHYS_FROM;
    evto = runinfo->EV_REG_PHYS_TO;
    //
    if ( id_reg_run == -1 ){
      printf("\n MYDETECTOR1 - ERROR: no run with ID_RUN = %i \n\n Exiting... \n\n",(int)idRun);
      code = -5;
      goto closeandexit;    
    };
    //
    // Search in the DB the path and name of the LEVEL0 file to be processed.
    //
    printf("\n Querying DB for data files informations...\n");
    //
    // where can I find the file with the id I have just recovered?
    //
    myquery.str("");
    myquery << "select PATH,NAME from GL_ROOT where ID=" << id_reg_run;
    myquery << ";";
    //  
    if ( debug ) printf("The query is:\n              \"%s\" \n",myquery.str().c_str());
    //
    pResult = dbc->Query(myquery.str().c_str());
    if ( !pResult ) {
      printf(" MYDETECTOR1 - ERROR: error querying the DB [6]\n");
      code = -14;
      goto closeandexit;
    };
    ftmpname.str("");
    //
    for( r=0; r < 1000; r++){ 
      Row = pResult->Next();
      if( Row == NULL ) break;
      for( t = 0; t < pResult->GetFieldCount(); t++){
	//
	// put in fpath the path to that file
	//
	if ( t == 0 ) ftmpname << Row->GetField(t) << "/";
	if ( t == 1 ) ftmpname << Row->GetField(t);
	if ( debug ) printf(" Row %i value = %s \n",t,Row->GetField(t));
      };
    };
    delete pResult;
    //
    fname = ftmpname.str().c_str();
    //
    // print out informations
    //
    totevent = evto - evfrom + 1;
    printf("\n LEVEL0 data file: %s \n",fname.Data());
    printf(" RUN HEADER absolute time is:  %llu \n",runheadtime);
    printf(" RUN TRAILER absolute time is: %llu \n",runtrailtime);
    printf(" %i events to be processed for run %llu: from %i to %i (reg entries)\n\n",totevent,idRun,evfrom,evto);
    //
    // Open Level0 file
    //
    l0File = new TFile(fname.Data());
    if ( !l0File ) {
      printf(" MYDETECTOR1 - ERROR: problems opening  Level0 file\n");
      code = -6;
      goto closeandexit;
    };
    l0tr = (TTree*)l0File->Get("Physics");
    if ( !l0tr ) {
      printf(" MYDETECTOR1 - ERROR: no Physics tree in Level0 file\n");
      l0File->Close();
      code = -7;
      goto closeandexit;
    };
    l0head = l0tr->GetBranch("Header");
    if ( !l0head ) {
      printf(" MYDETECTOR1 - ERROR: no Header branch in Level0 tree\n");
      l0File->Close();
      code = -8;
      goto closeandexit;    
    };
    l0registry = l0tr->GetBranch("Registry");
    if ( !l0registry ) {
      printf(" MYDETECTOR1 - ERROR: no Registry branch in Level0 tree\n");
      l0File->Close();
      code = -9;
      goto closeandexit;    
    };
    l0trig = l0tr->GetBranch("Trigger");
    if ( !l0trig ) {
      printf(" MYDETECTOR1 - ERROR: no Trigger branch in Level0 tree\n");
      l0File->Close();
      code = -104;
      goto closeandexit;
    };
    //
    l0tr->SetBranchAddress("Trigger", &trig);
    l0tr->SetBranchAddress("Registry", &l0reg);
    //
    nevents = l0registry->GetEntries();
    //
    if ( nevents < 1 ) {
      printf(" MYDETECTOR1 - ERROR: Level0 file is empty\n\n");
      l0File->Close();
      code = -11;
      goto closeandexit;
    };
    //
    if ( evto > nevents-1 ) {
      printf(" MYDETECTOR1 - ERROR: too few entries in the registry tree\n");
      l0File->Close();
      code = -12;
      goto closeandexit;
    };
    //
    // run over all the events of the run
    //
    printf("\n Ready to start! \n\n Processed events: \n\n");
    //
    for ( re = evfrom; re <= evto; re++){
      //
      if ( procev%1000 == 0 && procev > 0 ) printf(" %iK \n",procev/1000);	
      //
      l0registry->GetEntry(re);
      //
      // absolute time of this event
      //
      atime = l0reg->absTime;
      //
      // physics events is at entry number ei where
      //
      ei = l0reg->event;
      //
      if ( atime > runtrailtime || atime < runheadtime  ) {
	printf(" MYDETECTOR1 - WARNING: event at time outside the run time window, skipping it\n");
	goto jumpev;
      };
      //
      procev++;
      //
      // start processing
      //
      mydec = new MyDect1Level2();
      //
      // determine who generate the trigger for this event (TOF, S4/PULSER, CALO) 
      //
      l0trig->GetEntry(ei);
      S3 = 0;
      S2 = 0;
      S12 = 0;
      S11 = 0;
      S3 = trig->patterntrig[2];
      S2 = trig->patterntrig[3];
      S12 = trig->patterntrig[4];
      S11 = trig->patterntrig[5];
      if ( trig->patterntrig[0] ) tmptrigty = 2.;
      if ( S3 || S2 || S12 || S11 )  tmptrigty = 0.;    
      if ( trig->patterntrig[1] & (1<<0) || (!trig->patterntrig[0] && !S3 && !S2 && !S12 && !S11) ) tmptrigty = 1.;
      //
      // store informations in the level2 class
      //
      mydec->myvar1 = (int)trig->patterntrig[2];
      mydec->myvar2 = (int)trig->patterntrig[3];
      mydec->myvar3 = tmptrigty;
      //
      // Fill the rootple 
      //
      mydect1->Fill();
      //    
      //
    jumpev:
      debug = false;
      //
    };
    //
    ei = 0;
    //
  }; // process all the runs
  //
  printf("\n Finished processing data \n");
  //
 closeandexit:
  //
  // we have finished processing the run(s). If we processed a single run now we must copy all the events after our run from the old tree to the new one and delete the old tree.
  //
  if ( !reprocall && reproc && code >= 0 ){
    if ( totfileentries > noaftrun ){
      printf("\n Post-processing: copying events from the old tree after the processed run\n");   
      printf(" Copying %i events in the file which are after the end of the run %i \n",(int)(totfileentries-noaftrun),(int)run);
      printf(" Start copying at event number %i end copying at event number %i \n",(int)noaftrun,(int)totfileentries);
      for (UInt_t j = noaftrun; j < totfileentries; j++ ){
	//
	// Get entry from old tree
	//
	mydect1clone->GetEntry(j);	  
	//
	// copy caclone to ca
	//
	memcpy(&mydec,&mydecclone,sizeof(mydec));
	//
	// Fill entry in the new tree
	//
	mydect1->Fill();
      };
      printf(" Finished successful copying!\n");
    };
  };
  //
  // Case of no errors: close files, delete old tree(s), write and close level2 file 
  //


  if ( code >= 0 ){
    printf("\n Writing and closing rootple\n");
    if ( mydect1clone && !reprocall ) mydect1clone->Delete("all");
    if ( l0File ) l0File->Close();
    runinfo->Close();
    file->Write();
    file->Close();
  } else {
    printf("\n MYDETECTOR1 - ERROR: an error occurred, exiting without saving\n");
  };
  //
  // the end
  //
  printf("\n Exiting...\n");
  return(code);
}



