//
// 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 <MyDect2Core.h>
#include <MyDect2Level2.h>
#include <MyDect2Verl2.h>
//
//
// Declaration of the core fortran routines
//
#define mydeccom mydeccom_
extern "C" int mydeccom();
#define mydectrk mydectrk_
extern "C" int mydectrk();
//
// Tracker classes headers and definitions
//
#include <TrkLevel2.h>
#define readB readb_ 
extern "C" int readB();
//
using namespace std;
//
Bool_t existfile(TString filename){
    ifstream myfile;
    myfile.open(filename.Data());
    if ( !myfile ){
      return(false);
    };
    myfile.close();
    return(true);
}
//
//
// CORE ROUTINE
//
//
int MyDect2Core(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 *tracker = 0;
  TTree *mydect = 0;
  TTree *mydectclone = 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;
  //
  // mydetector2 level2 classes
  //
  MyDect2Level2 *mydec = new MyDect2Level2();
  MyDect2Level2 *mydecclone = new MyDect2Level2();
  //
  TrkLevel2 *trk = new TrkLevel2();
  Long64_t nevtrkl2 = 0;
  //
  // 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;
  //
  // Define some basic variables
  // 
  stringstream file2;
  stringstream file3;
  stringstream qy;
  Bool_t imtrack = false;
  //
  Int_t itr = -1;    
  Int_t totevent = 0;
  //
  ULong64_t atime = 0ULL;
  //
  Int_t ei = 0; 
  UInt_t re = 0;
  //
  TString mydectversion;
  RunInfo *runinfo = 0;
  TArrayL *runlist = 0;
  //
  Int_t ntrkentry = 0;
  stringstream Bpath;
  stringstream Bname;
  extern struct cParamPath parampath_; 
  ULong64_t fttrkpar1 = 0ULL;
  ULong64_t tttrkpar1 = 0ULL;
  Bool_t trkpar1 = true;
  //
  // declaring external output and input structures
  //
  extern struct InputStruct cinput_;
  extern struct OutputStruct coutput_;
  //
  // 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 tree MyDetector2 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(" MYDETECTOR2 - 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());
  //
  // Check if file exists, if not exit with error (we need tracker data).
  //
  if ( !existfile(outputfile.Data()) ) {
    printf(" MYDETECTOR2 - ERROR: no Level2 file\n");
    return(-100);
  };
  // 
  // OK, file exists, open it in "update" mode. 
  //
  file = new TFile(outputfile.Data(),"UPDATE");
  if ( !file->IsOpen() ){
    printf(" MYDETECTOR2 - ERROR: cannot open file for writing\n");
    return(-101);    
  };
  //
  // Does it contain the Tracker tree?
  //
  tracker = (TTree*)file->Get("Tracker");
  if ( !tracker ) {
    printf(" MYDETECTOR2 - ERROR: no tracker tree\n");
    code = -102;
    goto closeandexit;
  };
  //
  // get tracker level2 data pointer
  //
  tracker->SetBranchAddress("TrkLevel2",&trk);  
  nevtrkl2 = tracker->GetEntries();
  //
  // update (or create) versioning information using RunInfo 
  //  
  mydectversion = MyDect2Info(false);
  sgnl = 0;
  runinfo = new RunInfo(dbc,file,run,"MYDECT2",mydectversion,sgnl);
  if ( sgnl ){
    printf(" MYDETECTOR2 - 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 MyDetector2 tree in the file, if it exists we are reprocessing data if not we are processing a new run
  //
  mydectclone = (TTree*)file->Get("MyDetector2");
  //
  // create mydetector tree mydect
  // 
  mydect = new TTree("MyDetector2","PAMELA Level2 MyDetector2 data");
  mydect->Branch("MyDect2Level2","MyDect2Level2",&mydec);
  //
  if ( !mydectclone ){
    //
    // tree does not exist, we are not reprocessing
    //
    reproc = false;
    if ( run == 0ULL ) printf(" MYDETECTOR2 - WARNING: you are reprocessing data but MyDetector2 tree does not exist!\n");
    if ( runinfo->IsReprocessing() && run != 0ULL ) printf(" MYDETECTOR2 - WARNING: it seems you are not reprocessing data but mydetector2\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 MYDETECTOR2 - WARNING: Reprocessing all runs\n");
      reprocall = true;
      mydectclone->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 MYDETECTOR2 - WARNING: Reprocessing run number %i \n",(int)run);
      //
      // access old branch in old tree
      //
      mydectclone->SetBranchAddress("MyDect2Level2",&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++){
	  //
	  mydectclone->GetEntry(j);	  
	  //
	  // copy caclone to ca
	  //
	  memcpy(&mydec,&mydecclone,sizeof(mydec));
	  //
	  // Fill entry in the new tree
	  //
	  mydect->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(" MYDETECTOR2 - 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 MYDETECTOR2 - 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(" MYDETECTOR2 - 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(" MYDETECTOR2 - ERROR: problems opening  Level0 file\n");
      code = -6;
      goto closeandexit;
    };
    l0tr = (TTree*)l0File->Get("Physics");
    if ( !l0tr ) {
      printf(" MYDETECTOR2 - ERROR: no Physics tree in Level0 file\n");
      l0File->Close();
      code = -7;
      goto closeandexit;
    };
    l0head = l0tr->GetBranch("Header");
    if ( !l0head ) {
      printf(" MYDETECTOR2 - ERROR: no Header branch in Level0 tree\n");
      l0File->Close();
      code = -8;
      goto closeandexit;    
    };
    l0registry = l0tr->GetBranch("Registry");
    if ( !l0registry ) {
      printf(" MYDETECTOR2 - ERROR: no Registry branch in Level0 tree\n");
      l0File->Close();
      code = -9;
      goto closeandexit;    
    };
    l0trig = l0tr->GetBranch("Trigger");
    if ( !l0trig ) {
      printf(" MYDETECTOR2 - 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(" MYDETECTOR2 - ERROR: Level0 file is empty\n\n");
      l0File->Close();
      code = -11;
      goto closeandexit;
    };
    //
    if ( evto > nevents-1 ) {
      printf(" MYDETECTOR2 - ERROR: too few entries in the registry tree\n");
      l0File->Close();
      code = -12;
      goto closeandexit;
    };
    //
    // Check if we have to load parameter files (or calibration associated to runs and not to events)
    //
    // for example let's assume that we could have different magnetic field maps for different runs:
    //
    if ( trkpar1 || ( tttrkpar1 != 0 && tttrkpar1 < runheadtime ) ){
      trkpar1 = false;
      //
      // Load magnetic field maps.
      //
      // =============================================================
      // Now retrieve information about parameters to process LEVEL2
      // =============================================================  
      if ( debug ) printf("\n --------------------------------------");
      if ( debug ) printf("\n Querying DB for parameter file info...");
      if ( debug ) printf("\n --------------------------------------\n");
      myquery.str("");    
      Bpath.str("");
      Bname.str("");
      myquery << "select PATH,NAME,FROM_TIME,TO_TIME from GL_PARAM where DESCR=\"field\"";  
      if ( debug ) printf("Query:\n              \"%s\" \n",myquery.str().c_str());  
      pResult = dbc->Query(myquery.str().c_str()); 
      if ( !pResult ) {
	printf(" MYDETECTOR2 - ERROR: error querying the DB [9]\n");
	code = -14;
	goto closeandexit;
      };
      for( r=0; r < 1000; r++){ 
	Row = pResult->Next();     
	if( Row == NULL ) break;
	for( t = 0; t < pResult->GetFieldCount(); t++){ 
	  if(r==0){
	    if(t==0) Bpath << Row->GetField(t);
	    if(t==1) Bname << Row->GetField(t);
	    char *pEnd;
	    if ( t == 2 ) fttrkpar1 = strtoull(Row->GetField(t),&pEnd,0);
	    if ( t == 3 ) tttrkpar1 = strtoull(Row->GetField(t),&pEnd,0);
	    if ( debug ) printf("r=%i--> Row %i value = %s \n",r,t,Row->GetField(t));
	  }
	};
      };
      delete pResult; 
      //
      Bpath << "/" << Bname.str().c_str();
      strcpy(parampath_.B_parampath ,Bpath.str().c_str());  
      parampath_.B_pathlen = Bpath.str().size();
      // ----------------------------
      // Read the magnetic field
      // ----------------------------
      printf(" Reading magnetic field maps: \n");
      readB(); 
      printf("\n");
    };
    //
    // 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(" MYDETECTOR2 - WARNING: event at time outside the run time window, skipping it\n");
	goto jumpev;
      };
      //
      // retrieve tracker informations, the LEVEL2 entry which correspond to our event will be "itr"
      //
      if ( !reprocall ){
	itr = nobefrun + (re - evfrom);
      } else {
	itr = runinfo->GetFirstFileEntry() + (re - evfrom);
      };
      if ( itr > nevtrkl2 ){  
	printf(" MYDETECTOR2 - ERROR: no tracker events with entry = %i in Level2 file\n",itr);
	l0File->Close();
	code = -113;
	goto closeandexit; 
      };
      tracker->GetEntry(itr); 
      //
      procev++;
      //
      // start processing
      //
      mydec = new MyDect2Level2();

      //
      // Here we will use some procedure to calibrate our data and put some kind of informations in the cinput structure
      //
      cinput_.myinputvar = (int)ei;

      //
      // Here we have calibrated data, ready to be passed to the FORTRAN routine which will extract common and track-related  variables.
      //
      //
      // Calculate variables common to all tracks 
      //
      mydeccom();
      //
      // and now we must copy from the output structure to the level2 class:
      //
      mydec->myvar1 = coutput_.myvar1;
      mydec->myvar2 = coutput_.myvar2;
      mydec->myvar3 = coutput_.myvar3;
      //
      // Calculate track-related variables 
      //
      //
      // Calculate variables related to tracks only if we have at least one track (from selftrigger and/or tracker)
      //
      ntrkentry = 0;
      //
      if ( trk->GetNTracks() > 0 ){
	//
	// We have at least one track
	//
	//
	// Run over tracks
	//
	for(Int_t nt=0; nt < trk->GetNTracks(); nt++){  
	  //
	  TrkTrack *pti = 0;
	  TrkTrack *ptt = trk->GetTrack(nt); 
	  //
	  imtrack = false;
	  cinput_.trkchi2 = 0;
	  coutput_.good = 1;
	  //
	  // If we have only one track, check if we have a couple of "mirrored" tracks or a unique track
	  //
	  if ( trk->GetNTracks() == 1 ){
	    if( ptt->HasImage() ){
	      imtrack = true;
	      cinput_.trkchi2 = 1;
	      pti = trk->GetTrackImage(nt);
	    };
	  };
	  //
	  // If we have a couple of tracks pass both to the fortran routine, it will decide which one is the good one
	  //
	  for (Int_t e = 0; e < 5 ; e++){
	    cinput_.al_pp[e][0] = ptt->al[e];
	    if ( imtrack ){
	      cinput_.al_pp[e][1] = pti->al[e];
	    } else {
	      cinput_.al_pp[e][1] = 0.;
	    };
	  };
	  //
	  // Get tracker related variables for this track
	  //
	  mydectrk();
	  //
	  // if we had no problem (coutput_.good = 1), fill and go on
	  //
	  if ( coutput_.good == 1 ) {
	    //
	    // Copy values in the class from the structure (we need to use a temporary class to store variables).
	    //
	    MyDect2TrkVar *t_mydec = new MyDect2TrkVar();
	    t_mydec->mytrkvar = coutput_.mytrkvar;
	    //
	    // Determine if we used the image track instead of the physics track
	    //
	    t_mydec->trkseqno = nt;
	    if ( imtrack && cinput_.trkchi2 == 2 ) t_mydec->trkseqno = nt+100; // we have image track which seems to be better than the main track, used that one.
	    if ( imtrack && cinput_.trkchi2 == -1 ) t_mydec->trkseqno = -nt-100; // we have image track but nor the main nor the image  seems to be good, processed anyway
	    //
	    
	    //
	    // create a new object for this event with track-related variables
	    //
	    TClonesArray &t = *mydec->myTrk;
	    new(t[ntrkentry]) MyDect2TrkVar(*t_mydec);
	    //
	    ntrkentry++;	
	  } else {
	    printf(" Problems with event at entry %i (track related variables not saved)\n",itr);
	  };
	}; // loop on all the tracks
      }; 
      //
      // Here you may want to clear structures used to communicate with fortran
      //
      cinput_.myinputvar = 0;
      //
      // Fill the rootple 
      //
      mydect->Fill();
      //    
      //
    jumpev:
      debug = false;
      //
    };
    //
    // Here you may want to clear some variables before processing another run  
    //
    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
	//
	mydectclone->GetEntry(j);	  
	//
	// copy caclone to ca
	//
	memcpy(&mydec,&mydecclone,sizeof(mydec));
	//
	// Fill entry in the new tree
	//
	mydect->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 ( mydecclone && !reprocall ) mydecclone->Delete("all");
    if ( l0File ) l0File->Close();
    tracker->Delete();
    runinfo->Close();
    file->Write();
    file->Close();
  } else {
    printf("\n MYDETECTOR2 - ERROR: an error occurred, exiting without saving\n");
  };
  //
  // the end
  //
  printf("\n Exiting...\n");
  return(code);
}



