// ------------------------------------------------------------
// |                                                          |
// |  AC LEVEL2 Software - P.Hofverberg, E.Mocchiutti, S.Orsi |
// |                                                          |
// |  ACEXE.cra      version 1.00 (2006-01-19)                |
// |                                                          |
// ------------------------------------------------------------
//
//   Based on:
//   - template TEMPLATEEXE.cra (v2.00 2006-01-13) by E. Mocchiutti
//   - routine  AcLEVEL1.c      (v1.01 2005-05-17) by P.Hofverberg
//
//
//   Changelog:
//
//
//   Comments (TEMPLATEEXE.cra
//   (2006-01-13): linked to latest external packages (root2paw and calocommon), uses ROOT classes to connect to DB, assume you always want to create
//   a rootple (no ntuple in intermediate level data) and that you will force processing.
//
// Include ROOT header files for macro compilation.
//
#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>
//
// other general header files
//
#include <fstream>
#include <TSQLServer.h>
#include <TSQLRow.h>
#include <TSQLResult.h>
//
// files in the inc directory
//
#include <acl2class.h>
//
// these files are the YODA header files needed here (you could need to add more of them)
//
#include <event/PamelaRun.h>
#include <event/RegistryEvent.h>
#include <event/physics/anticounter/AnticounterEvent.h>
#include <event/CalibCalPedEvent.h>
//
// this file comes from the calorimeter COMMON package (must be installed)
//
#include <CaloFunctions.h>
//
//
//  MAIN ROUTINE
//
//  Input variables:
//
//  - run : the run ID we want to process.
//
//  - dbc : the pointer to a DB connection to be able to query the DB.
//
//
short int ACEXE(Int_t run, TSQLServer *dbc){
  //
  // x Silvio: se non ti piace ACEXE e cambi nome devi poi cambiarlo anche in ACL2.cc
  //
  // swcode contain the version of this software. It is an integer of this type: subsubversion*10000+subversion*100+version
  // For example, version 3.45.99 gives swcode =  994503
  //
  Int_t swcode = 2;
  //
  // when set debug = 1 you will have a verbose printout on the screen.
  //
  int debug = 0;
  //
  // filename is the name of the file that contains the run we are going to process; filename will be recovered from the DB.
  //
  TString filename;
  //
  // where to store results: for the moment we will leave here the starting directory
  //
  TString outDir ="./"; 
  //
  // SQL variables
  //
  TSQLResult *pResult;
  TSQLRow *Row;
  int t;
  int r;
  int procev = 0;
  stringstream myquery;
  //
  // Here we query the DB to obtain the informations needed to process the run number "run"
  //
  printf("\n Querying DB for data files informations...\n");
  //
  // 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;
  //
  // first query: which is the ID of the file which contains the run number "run"?
  // and at what absolute time the run header and run trailer time were recorded?
  //
  myquery.str("");
  myquery << "select ID_REG_RUNHEADER,RUNHEADER_TIME,RUNTRAILER_TIME from GL_RUN where ID=" << run;
  myquery << ";";
  //  
  if ( debug ) printf("The query is:\n              \"%s\" \n",myquery.str().c_str());
  //
  // query the db and store results in pResult
  //
  pResult = dbc->Query(myquery.str().c_str());
  //
  Int_t id_reg_runheader=-1;
  //
  for( r=0; r < 1000; r++){ 
    Row = pResult->Next();
    if( Row == NULL ) break;
    for( t = 0; t < pResult->GetFieldCount(); t++){
      char *pEnd;
      //
      // store in id_reg_runheader the answer about the file ID
      //
      if ( t == 0 ) id_reg_runheader=atoi(Row->GetField(t));
      //
      // store in runheadertime the run header time
      //
      if ( t == 1 ) runheadtime = strtoull(Row->GetField(t),&pEnd,0);
      //
      // store in runtrailertime the run trailer time
      //
      if ( t == 2 ) runtrailtime = strtoull(Row->GetField(t),&pEnd,0);
      //
      if ( debug ) printf(" Row %i value = %s \n",t,Row->GetField(t));
    };
  };
  delete pResult;
  //
  if ( id_reg_runheader == -1 ){
    printf("\n ERROR: no run with ID_RUN = %i \n\n Exiting... \n\n",run);
    return(102);
  };
  //
  // where can I find the file with the id I have just recovered?
  //
  myquery.str("");
  myquery << "select PATH from GL_ROOT where ID=" << id_reg_runheader;
  myquery << ";";
  //  
  if ( debug ) printf("The query is:\n              \"%s\" \n",myquery.str().c_str());
  //
  pResult = dbc->Query(myquery.str().c_str());
  stringstream fpath;
  fpath.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
      //
      fpath << Row->GetField(t);
      if ( debug ) printf(" Row %i value = %s \n",t,Row->GetField(t));
      };
    };
  delete pResult;
  //
  // this path is of the form /home/pamela/filesfromyoda/dw_YYMMDD_NNNMM/RunHeader
  // while we want something of the form of /home/pamela/filesfromyoda/dw_YYMMDD_NNNMM/
  // here the fpath string is manipulated and the output is put in filename
  //
  const string ufpath = fpath.str().c_str();  
  Int_t runheaderpos = ufpath.find("RunHeader");
  TString runhpath=(fpath.str().c_str());
  stringcopy(filename,runhpath,0,runheaderpos);
  //
  // print out informations
  //
  printf("\n LEVEL0 data directory: %s \n",filename.Data());
  printf(" RUN HEADER absolute time is:  %llu \n",runheadtime);
  printf(" RUN TRAILER absolute time is: %llu \n\n",runtrailtime);
  //
  // finished the first set of queries
  //
  printf(" ...finished querying DB!\n");
  //
  //
  // define pointers to the root file we want to open
  //
  // registry file
  TFile *regFile;
  // header file
  TFile *headerFile;
  // trigger file
  TFile *acFile;
  //
  TString filety;
  stringstream file2;
  stringstream file3;
  const char *file4;
  stringstream qy;
  qy.str("");
  //
  // store the information about the directory where the program has been started
  //
  gDirectory->GetList()->Delete();
  const char* startingdir = gSystem->WorkingDirectory();
  TString path;
  stringcopy(path,startingdir);
  //
  // Open files:
  //
  // the registry file
  //
  regFile = emigetFile(filename, "Physics", "Registry");
  if ( !regFile ){
      printf("\n\n ERROR: no registry file, exiting...\n\n");
      return(103);
  };
  TTree *regTree = (TTree*)regFile->Get("Registry");
  //
  // the header file
  //
  headerFile = emigetFile(filename, "Physics", "Header");
  if ( !headerFile ){
      printf("\n\n ERROR: no header file, exiting...\n\n");
      return(1);
  };
  TTree *otr = (TTree*)headerFile->Get("Pscu");
  //
  // the trigger file
  //
  acFile = emigetFile(filename, "Anticounter");
  if ( !acFile ){
      printf("\n\n ERROR: No Anticounter file! \n");
      return(1);
  } else {
      otr->AddFriend("Anticounter",acFile);
  };
  //
  // x Silvio: controlla che i vari branch si chiamino proprio Anticounter, non ho controllato...
  //
  // Define some basic variables
  // 
  // determine the number of entries in the rootples
  //
  Long64_t nevents    = otr->GetEntries();
  //
  // check if the file contains any event
  //
  if ( nevents < 1 ) {
      printf(" ERROR: the file is empty!\n\n");
      return(7);
  };
  //
  // from the YODA filename determine the YODA processed level and check if we have a DW or dw unpacked file
  //
  const string myfil = (const char *)filename;
  Int_t myposiz = myfil.find("dw_");    
  Int_t DW = 0;
  if ( myposiz == -1 ) {
      myposiz = myfil.find("DW_");
      DW = 1;
  };
  if ( myposiz == -1 ) return(6);
  TString basepath;
  TString yodalev;
  stringcopy(basepath,filename,0,myposiz);
  stringcopy(yodalev,filename,myposiz+13,myposiz+15);
  //
  // since we are going to create an intermediate data output we create a rootple which is smaller respect to ntuple
  //
  filety = "root";
  //
  gSystem->ChangeDirectory(path);
  //
  // if no output is given put the output in the YODA directory structure under /Physics/Level2
  //
  if ( outDir == "" ){
      file4 = filename;
      file3.str("");
      file3 << file4 << "/Physics/Level2";
  } else {
      file4 = outDir;
      file3.str("");
      file3 << file4;
  }
  //
  file2.str("");
  file2 << file3.str().c_str() << "/";
  //
  // the filename must contain the run ID number and the detector name. In the future it will be perhaps necessary to add also the software version which generated the file (?).
  //
  file2 << "run_id-" << run;
  file2 << "_template."<< filety;
  //
  printf("\n Filename will be: \n %s \n\n",file2.str().c_str());
  //
  // check if Level2 directory exists, if not create it.
  //    
  Int_t ERR = gSystem->MakeDirectory(file3.str().c_str());    
  //
  if ( !ERR ) {
    printf(" LEVEL2 directory doesn't exist! Creating LEVEL2 directory \n\n");
  };
  //
  // Open file to write
  //
  // 
  // In any case we must create a tree
  //
  TTree *tree = 0;
  //
  gSystem->ChangeDirectory(path);
  stringstream name;
  TString tmpfile2;
  stringcopy(tmpfile2,file2.str().c_str());
  TFile *hfile = 0;
  //
  // To pass data from C to FORTRAN we need a structure, while to save data into a rootple
  // the best is to have a class. Here the definition of both of them
  // NOTICE that the name of the struct MUST be the same of the name of the fortran COMMON
  // plus the underscore at the end.
  //
  AcLevel2 *acl2 = new AcLevel2();
  //
  // x Silvio: AcLevel2 e` il nome della classe, quello lo setti in ../inc/acl2class.h
  // x Silvio: cambia tutti i ctempl2 in acl2 da qui in poi (OK!)
  //

  //
  // here we open the rootple file
  //
  char *type;
  type = "RECREATE";
  hfile = new TFile(file2.str().c_str(),type,"AC data");
  //
  // and we book the rootple
  //
  tree = new TTree("AcLevel2","AC Level2 data");
  //
  // cambia tutte le variabili !! (Silvio)
  //
  tree->Branch("status",acl2->status,"status[2]/s");
  tree->Branch("hitmap",acl2->hitmap,"hitmap[2]/s");
  tree->Branch("hitstatus",acl2->hitstatus,"hitstatus[2]/s");
  tree->Branch("trigger",acl2->trigger,"trigger[2]/s");
  tree->Branch("OBT",&acl2->OBT,"OBT/l");
  tree->Branch("pro_num",&acl2->pro_num,"pro_num/I");
  tree->Branch("pkt_num",&acl2->pkt_num,"pkt_num/l");
  //
  // x Silvio qui ci metti le variabili della tua classe. In teoria puoi puntare direttamente alla classe senza elencare tutte le variabili ma il riempimento e` molto piu` lento
  //
  //
  // here we have the software code branch, filled once...
  //
  TTree *software = 0;
  software = new TTree("Software","Software used to generate data");
  software->Branch("swcode",&swcode,"swcode/I");
  //
  // ...here:
  //
  software->Fill();
  //
  // run over all the events
  //
  printf("\n Processed events: \n\n");
  //
  // define pointers to the data branches
  //
  pamela::PscuHeader *ph = 0;
  pamela::EventHeader *eh = 0;
  pamela::RegistryEvent *reg=0;
  pamela::anticounter::AnticounterEvent *ac = 0;
  //
  // x Silvio:  controlla che sia giusta questa riga sopra e da qui in poi sostituisci tutti i trig con ac (OK, level 0)
  //
  regTree->SetBranchAddress("Registry", &reg);
  otr->SetBranchAddress("Header", &eh);
  otr->SetBranchAddress("Anticounter.Event", &ac);
  //
  // x Silvio: come sopra (OK)
  //
  // here we define the number of entry of the registry file
  // notice that this number can be different from the number of entries of the physics files
  // due to the possibility of multiple records of the same event in that files
  //
  Long64_t regevents = regTree->GetEntries();    
  //
  // definition of the first event minus one, this is done this way for synchronization purpose
  //
  Int_t i = -1;    
  //
  // some variable definition
  //
  Int_t procerr = 0;
  Int_t retval = 0;
  Int_t pktnum = 0;
  Int_t obt = 0;
  ULong64_t atime = 0ULL;
  //
  //
  // start looping on events cointained in the registry file
  //
  for (Int_t re=0; re<regevents; re++){
    //
    if ( re%1000 == 0 && re > 0 ) printf(" %iK \n",re/1000);	
    //
    // get the first registry entry
    //
    regTree->GetEntry(re);
    //
    // read the event absolute time
    //
    atime = reg->absTime;
    //
    // introduce here declaration to avoid cross-inizialitation (see commented declaration ~35 lines below)
    UShort_t cnt = 1;
    //
    // if this event does not belong to the processed run jump to the next event (goto ev_jump)
    //
    if ( atime > runtrailtime || atime < runheadtime  ) {
      goto ev_jump;
    };
    //
    // ok, this event belongs to the processed run
    //
    i = reg->event;
    //
    // get data
    //
    otr->GetEntry(i);
    //
    // retrieve obt and packet number for this entry
    //
    ph = eh->GetPscuHeader(); 
    pktnum = ph->GetCounter();
    obt = ph->GetOrbitalTime();
    //
    procev++;
    //
    // ok, here we must do our data processing
    //
    //
    // x Silvio: qui ci metti il codice in c++ scritto da Petter per il software a terra
    // ti riferisci alle variabili che vuoi salvare sempre con acl2->
    //
    // --------------------------------------------------------------
    //
    // declare and initialize variables ...
    //
    //UShort_t cnt = 1; // move to line 428 before goto and it works
    //
    UShort_t CRCcheck[2];
    UShort_t Status[2];
    //UShort_t Reg[2][6];
    //UShort_t Header[2][2];
    UShort_t Hitmap[2];
    UShort_t Hitstatus[2];
    UShort_t Trigger[2];
    UShort_t Trigg_old[2];
    //UShort_t Temp[2][4];
    
    //UShort_t timeLSB[2];
    //UShort_t timeMSB[2];
    //Double_t time[2];
    
    UShort_t Counter[2][16];
    UShort_t Counter_old[2][16];
    
    UShort_t Shift[2][16];
    Int_t vec[2][16][16];
    //UShort_t pack[2][16];
    Int_t tmp;
    //
    for(Int_t gg = 0; gg < 2; gg++)
      {
	Hitstatus[gg] = 0;
      }
    //
    //fetch data
    //
    for(Int_t j = 0; j < 2; j++)
      {
	if(re>0)
	  {
	    Trigg_old[j] = Trigger[j];
	    for(Int_t jj = 0; jj < 16; jj++)
	      Counter_old[j][jj] = Counter[j][jj];
	  }

	for(Int_t jj = 0; jj < 16; jj++)
	  {
	    Counter[j][jj] = ac->counters[j][jj];
	    Shift[j][jj] = ac->shift[j][jj];
	  }
	Status[j] = ac->status[j];
	Hitmap[j] = ac->hitmap[j];
	Trigger[j] = ac->trigg[j];
	CRCcheck[j] = ac->CRCcheck[j];
      }
   
    /***********************************************/


    //process data
    /***********************************************/
    //shiftregisters
    cnt=1;
    for(Int_t b = 0; b < 2; b++){ //card
      for(Int_t k = 0; k < 16; k++){ //shift register
	for(Int_t l = 0; l < 16; l++){ //bin
	  tmp = ((Shift[b][k] & cnt) > 0 ? 1 : 0);
	  vec[b][k][l]=tmp;
	  cnt=cnt<<1;
	}
	cnt=1;
      }
    }

    //fill Level1 file
    /************************************************/
    for(Int_t s = 0; s < 2; s++)
      {
	acl2->hitmap[s] = Hitmap[s];
	acl2->trigger[s] = Trigger[s];
	cnt=1;
	for(Int_t k = 0; k < 16; k++)
	  {
	    for(Int_t bin = 5; bin < 9; bin++) //acceptance window
	      {
		if(vec[s][bin][k]==1)
		  Hitstatus[s] = Hitstatus[s] | cnt;
	      }
	    cnt=cnt<<1;
	  }
	acl2->hitstatus[s] = Hitstatus[s];

	//Status
	/****************************************/
	if(s==0){
	  if(Trigger[0] != (Trigg_old[0]+1))
	    acl2->status[0] = acl2->status[0] | 0x1;
	  if(Status[0] & 0x001F < 0x001F)
	    acl2->status[0] = acl2->status[0] | 0x2;
	  if(CRCcheck[0] == 0)
	    acl2->status[0] = acl2->status[0] | 0x4;
	  for(Int_t gg = 0; gg < 16; gg++){
	    if((Counter[0][gg] == Counter_old[0][gg]) && i>0){
	      acl2->status[0] = acl2->status[0] | 0x8;
	    }
	  }
	}
	else{
	  if(Trigger[1] != (Trigg_old[1]+2))
	    acl2->status[1] = acl2->status[1] | 0x1;
	  if(Status[1] & 0x001F < 0x001F)
	    acl2->status[1] = acl2->status[1] | 0x2;
	  if(CRCcheck[1] == 0)
	    acl2->status[1] = acl2->status[1] | 0x4;
	  for(Int_t gg = 0; gg < 16; gg++){
	    if((Counter[1][gg] == Counter_old[1][gg]) && i>0)
	      acl2->status[1] = acl2->status[1] | 0x8;
	  }
	}

	/****************************************/
      }
    acl2->OBT = ph->GetOrbitalTime();
    acl2->pkt_num = re+1;
    acl2->pro_num = ph->GetCounter();
    /************************************************/


    // --------------------------------------------------------------

    //
    // fill the rootple 
    //
    tree->Fill();
    //
    // finished processing, go to the next event.
    //
  ev_jump:
    //
    // if any error occurred during processing try to save and close the files anyway.
    //
    if ( procerr ){
      if ( i > 0 ){
	printf("\nTrying to close the file anyway...\n");
	hfile->Write();
	hfile->Close();
	printf("...done!\n");
      } else {
	//
	// if no data were processed remove the file from disk
	//
	printf("\nERROR: output file is empty! \n");
	stringstream remfile;
	remfile.str("");
	remfile << "rm -f " << file2.str().c_str();
	gSystem->Exec(remfile.str().c_str());
      };
      printf("\nERROR: processed %i out of %i events\n",i,(int)nevents);
      printf("\nERROR: an error occurred during processing!\n\n Exiting...\n\n");
      goto theend;
    };
  };
  //
  // close rootple files and exit
  //
  hfile->Write();
  hfile->Close();
  printf("\n");
  printf(" Finished! Processed events: %i \n\n Exiting... \n",procev);
  printf("\n");
 theend:
  //
  // go back to the starting path and unload fortran libraries
  //
  gSystem->ChangeDirectory(path);
  return(retval);
}

