//
#include <iostream>
#include <sstream>
#include <errno.h>
//
#include <TSystem.h>
//
#include <PamelaDBOperations.h>
#include <YodaProfilerVerl2.h>
//
using namespace std;
//
// Usage subroutine
//
void usage(){
  printf("\nUsage:\n");
  printf("\n YodaProfiler [options] -rawFile raw_filename -yodaFile yoda_filename \n");
  printf("\n -rawFile        full path to the raw file\n");
  printf("\n -yodaFile       full path to the YODA file\n");
  printf("\n Options can be: \n");
  printf("\n --version       print informations about compilation and exit\n");
  printf("\n -h | --help     print this help and exit \n");
  printf("\n -v | --verbose  be verbose [default]\n");
  printf("\n -s | --silent   print nothing on STDOUT\n");
  printf("\n -g | --debug    be very verbose [default: no]\n");
  printf("\n -boot number    CPU boot number [default = taken from VarDump]\n");
  printf("\n -tsync number   timesync (s) [default = taken from data]\n");
  printf("\n -obt0 number    obt at timesync (ms) [default = taken from data]\n");
  printf("\n -clean number   number in seconds after which the fragment table\n");
  printf("\n                 can be cleaned and runs validated [default = -1 do not clean],\n");
  printf("\n                 if 0 force cleaning immediatly, if negative do not clean\n");
//
  printf("\n -remove file    remove file and all related runs and calibrations from DB\n");
  printf("\n                 file must be the YODA filename (full path is not needed)\n");
  printf("\n                 'same' can be used if in conjuction with -yodaFile\n");
  printf("\n -validate file  validates runs between the two closest calibration to file \n");
  printf("\n                 not belonging to file itself. File must be the YODA filename\n");
  printf("\n                 'same' can be used if in conjuction with -yodaFile\n");
  printf("\n -cleanfrag file clean run fragments for file only\n");
  printf("\n                 File must be the YODA filename\n");
  printf("\n                 'same' can be used if in conjuction with -yodaFile\n");
  printf("\n -nofrag         do not leave runs in the fragment table and look for fragments\n");
  printf("\n                 in the GL_RUN table.\n");
  printf("\n -force          to be used to reprocess a file or to process a file\n");
  printf("\n                 when already validated the surroundings, it is equivalent to:\n");
  printf("\n                 -remove same -validate same -cleanfrag same -nofrag\n");
//	
  printf("\n -host           name for the host [default = $PAM_DBHOST or mysql://localhost/pamelaprod]\n");
  printf("\n -user           username for the DB [default = $PAM_DBUSER or \"anonymous\"] \n");
  printf("\n -psw            password for the DB [default = $PAM_DBPSW or \"\"]\n");
  printf("\n -tle <file>     ascii file containing TLE obtained from celestrak.org or space-track.org [default = no]\n");
  printf("\n The order of input files and options does not matter. \n");
  printf("\nExample: \n");
  printf("\n YodaProfiler -yodaFile /path/to/raw/files/000_000_00000_cln2.pam -rawFile /path/to/filesfromyoda/000_000_00000_cln2.root -v \n\n");
};
//
int main(int numinp, char *inps[]){
  //
  // Variables booking
  //
  Int_t signal = 0;
  Int_t nul = 0;
  UInt_t boot = 0;
  UInt_t tsync = 0;
  UInt_t obt0 = 0;
  Long64_t olderthan = -1LL;  
  //
  //
  TString filerawname  = "";
  TString filerootname = "";
  //
  TString host = "mysql://localhost/pamelaprod";
  TString user = "anonymous";
  TString password = "";
  //
  TString tlefilename = "";
  //
  const char *pamdbhost = gSystem->Getenv("PAM_DBHOST");
  const char *pamdbuser = gSystem->Getenv("PAM_DBUSER");
  const char *pamdbpsw = gSystem->Getenv("PAM_DBPSW");
  if ( !pamdbhost ) pamdbhost = "";
  if ( !pamdbuser ) pamdbuser = "";
  if ( !pamdbpsw ) pamdbpsw = "";
  if ( strcmp(pamdbhost,"") ) host = pamdbhost;
  if ( strcmp(pamdbuser,"") ) user = pamdbuser;
  if ( strcmp(pamdbpsw,"") ) password = pamdbpsw;
  //
  //    
  Bool_t beverbose = true;
  Bool_t debug = false;
  // 
  Bool_t remove = false;
  TString remfile = "";
  //
  Bool_t forceclean = false;
  TString fcleanfile = "";
  //
  Bool_t validate = false;
  TString validfile = "";
  //
  Bool_t nofrag = false;
  //
  Bool_t force = false;
  //
  Int_t i = 0;
  //
  if ( numinp > 1 ){
    while ( i < numinp ){
      if ( !strcmp(inps[i],"--version") ){
	YodaProfilerInfo(true);
	exit(0);
      };
      if ( !strcmp(inps[i],"-h") || !strcmp(inps[i],"--help") ){

	usage();
	exit(0);
      };
      if ( !strcmp(inps[i],"-rawFile") ) {
	if ( numinp-1 < i+1 ){
	  usage();
	  exit(1);
	};
	filerawname = (TString)inps[i+1];		
      };
      if ( !strcmp(inps[i],"-yodaFile") ) {
	if ( numinp-1 < i+1 ){
	  usage();
	  exit(1);
	};
	filerootname = (TString)inps[i+1];	
      };
      if ( !strcmp(inps[i],"-remove") ) {
	if ( numinp-1 < i+1 ){
	  usage();
	  exit(1);
	};
	remfile = (TString)inps[i+1];	
	remove = true;
      };
     if ( !strcmp(inps[i],"-cleanfrag") ) {
	if ( numinp-1 < i+1 ){
	  usage();
	  exit(1);
	};
	fcleanfile = (TString)inps[i+1];	
	forceclean = true;
      };
     if ( !strcmp(inps[i],"-validate") ) {
	if ( numinp-1 < i+1 ){
	  usage();
	  exit(1);
	};
	validfile = (TString)inps[i+1];	
	validate = true;
      };
      if ( !strcmp(inps[i],"-boot") ) {
	if ( numinp-1 < i+1 ){
	  usage();
	  exit(1);
	};
	boot = atoi(inps[i+1]);	
      };
      if ( !strcmp(inps[i],"-tsync") ) {
	if ( numinp-1 < i+1 ){
	  usage();
	  exit(1);
	};
	tsync = (UInt_t)atoll(inps[i+1]);	
      };
      if ( !strcmp(inps[i],"-obt0") ) {
	if ( numinp-1 < i+1 ){
	  usage();
	  exit(1);
	};
	obt0 = (UInt_t)atoll(inps[i+1]);	
      };
      if ( !strcmp(inps[i],"-clean") ) {
	if ( numinp-1 < i+1 ){
	  usage();
	  exit(1);
	};
	olderthan = (Long64_t)atoll(inps[i+1]);	
      };
      if ( !strcmp(inps[i],"-host") ) {
	if ( numinp-1 < i+1 ){
	  usage();
	  exit(1);
	};
	host = (TString)inps[i+1];	
      };
      if ( !strcmp(inps[i],"-user") ) {
	if ( numinp-1 < i+1 ){
	  usage();
	  exit(1);
	};
	user = (TString)inps[i+1];	
      };
      if ( !strcmp(inps[i],"-psw") ) {
	if ( numinp-1 < i+1 ){
	  usage();
	  exit(1);
	};
	password = (TString)inps[i+1];	
      };
      if ( !strcmp(inps[i],"-tle") ) {
	if ( numinp-1 < i+1 ){
	  usage();
	  exit(1);
	};
	tlefilename = (TString)inps[i+1];
      };
      //
      if ( !strcmp(inps[i],"-v") || !strcmp(inps[i],"--verbose") ) beverbose = true;
      //
      if ( !strcmp(inps[i],"-s") || !strcmp(inps[i],"--silent") ) beverbose = false;
      //
      if ( !strcmp(inps[i],"-g") || !strcmp(inps[i],"--debug") ) debug = true;
      //
      if ( !strcmp(inps[i],"-f") || !strcmp(inps[i],"-force") ) force = true;
      //
      if ( !strcmp(inps[i],"-nofrag") ) nofrag = true;
      //
      i++;
    };
  } else {
    //
    // no input parameters exit with error, we need at least the run id.
    //
    cout << "\n ERROR: NO INPUT PARAMETERS \n";
    usage();
    exit(1);
  };

  //
  // If not in verbose mode redirect to /dev/null the stdout and stderr
  //
  if ( !beverbose ){
    nul = open("/dev/null", O_CREAT | O_RDWR,S_IREAD | S_IWRITE);
    dup2(nul,1);
    dup2(nul,2);
  };
  //
  if ( !strcmp(filerootname.Data(),"") ){
    if ( (validate && !strcmp(validfile.Data(),"same")) ||
	 (remove && !strcmp(remfile.Data(),"same")) ||
	 (forceclean && !strcmp(fcleanfile.Data(),"same")) || force ){
      printf("\n ERROR IN INPUT PARAMETERS \n");
      usage();
      exit(1);  
    };
  } else {
    if ( validate && !strcmp(validfile.Data(),"same") ) validfile =  (TString)gSystem->BaseName(filerootname.Data());
    if ( remove && !strcmp(remfile.Data(),"same") ) remfile =  (TString)gSystem->BaseName(filerootname.Data());
    if ( forceclean && !strcmp(fcleanfile.Data(),"same") ) fcleanfile =  (TString)gSystem->BaseName(filerootname.Data());
  };
  //
  if ( force ){
    nofrag = true;
    forceclean = true;
    remove = true;
    validate = true;    
    fcleanfile = (TString)gSystem->BaseName(filerootname.Data());
    remfile = fcleanfile;
    validfile = fcleanfile;
  };
  //
  // Start:
  //
  TString message;
  char *version = YodaProfilerInfo(false);
  PamelaDBOperations *pamDB = 0;
  UInt_t sizeofwar = 12;
  UInt_t WAR[12];
  memset(WAR, 0, sizeofwar*sizeof(UInt_t));
  //
  printf("\n Welcome to the PAMELA YodaProfiler, version %s \n\n",version);
  try{
    //
    //-------------------------------------------------------------------------------------------
    // Create pamDB object and open SQL connection
    //-------------------------------------------------------------------------------------------
    if ( beverbose ) printf(" 1 => Initialize and open SQL connection \n");
    pamDB = new PamelaDBOperations(host,user,password,filerawname,filerootname,boot,tsync,obt0,debug,tlefilename);
    pamDB->CheckConnection();
    //-------------------------------------------------------------------------------------------
    //
    pamDB->SetNoFrag(nofrag);
    //
    if ( remove ){
      //-------------------------------------------------------------------------------------------
      // Remove a file and, on cascade, all that follows from the DB
      //-------------------------------------------------------------------------------------------
      if ( beverbose ) printf(" X => Remove file %s from DB \n",((TString)gSystem->BaseName(remfile)).Data());
      WAR[11] = pamDB->removeFile((TString)gSystem->BaseName(remfile.Data())); 
      //-------------------------------------------------------------------------------------------
    };    
    // 
    if( pamDB->InsertRaw() ){
      //-------------------------------------------------------------------------------------------
      //Insert a Raw file in GL_RAW
      //-------------------------------------------------------------------------------------------
      if ( beverbose ) printf(" 2 => Insert a RAW file in GL_RAW \n");
      WAR[0] = pamDB->insertPamelaRawFile(); 
      //-------------------------------------------------------------------------------------------
    };
    //
    if( pamDB->InsertRoot() ){
      //	  
      if( !pamDB->InsertRaw() )printf("=> RAW file not inserted --- the DB might not ( yet ) be filled correctly \n"); 
      pamDB->CheckFile();
      //-------------------------------------------------------------------------------------------   
      //Update a single GL_RAW record with its BOOT_NUMBER
      //-------------------------------------------------------------------------------------------
      if ( beverbose ) printf(" 3 => Update a single GL_RAW record with its BOOT_NUMBER \n");
      WAR[3] = pamDB->assignBOOT_NUMBER();
      //    if ( WAR[3] ) pamDB->SetNOBOOT(true);
      if ( WAR[3] && WAR[3] != 1 ) throw -9;
      //-------------------------------------------------------------------------------------------
      //
      //-------------------------------------------------------------------------------------------
      //Insert an entry in GL_TIMESYNC
      //-------------------------------------------------------------------------------------------
      if ( beverbose ) printf(" 4 => Insert an entry in GL_TIMESYNC \n");
      WAR[1] = pamDB->insertPamelaGL_TIMESYNC(); 
      //-------------------------------------------------------------------------------------------
      //
      //-------------------------------------------------------------------------------------------
      //Insert unpack ROOT file in GL_ROOT  
      //-------------------------------------------------------------------------------------------
      if ( beverbose ) printf(" 5 => Insert unpack ROOT file in GL_ROOT \n");
      WAR[2] = pamDB->insertPamelaRootFile();
      //-------------------------------------------------------------------------------------------
      //
      //-------------------------------------------------------------------------------------------      
      //Insert in GL_RUN runs information records relative to a single unpack
      //-------------------------------------------------------------------------------------------
      if ( beverbose ) printf(" 6 => Scan physics and store runs in the GL_RUN table\n");
      WAR[4] = pamDB->insertPamelaRUN();
      //-------------------------------------------------------------------------------------------    
	
      //-------------------------------------------------------------------------------------------
      //Insert in GL_CALO_CALIB calibration information records relative to a single unpack
      //-------------------------------------------------------------------------------------------
      if ( beverbose ) printf(" 7 => Insert calorimeter calibrations in the GL_CALO_CALIB table\n");
      WAR[5] = pamDB->insertCALO_CALIB();
      //-------------------------------------------------------------------------------------------    
	
      //-------------------------------------------------------------------------------------------
      //Insert in GL_TRK_CALIB calibration information records relative to a single unpack
      //-------------------------------------------------------------------------------------------
      if ( beverbose ) printf(" 8 => Insert tracker calibrations in the GL_TRK_CALIB table\n");
      WAR[6] = pamDB->insertTRK_CALIB();
      //-------------------------------------------------------------------------------------------    
	
      //-------------------------------------------------------------------------------------------
      //Insert in GL_S4_CALIB calibration information records relative to a single unpack
      //-------------------------------------------------------------------------------------------
      if ( beverbose ) printf(" 9 => Insert S4 calibrations in the GL_S4_CALIB table\n");
      WAR[7] = pamDB->insertS4_CALIB();
      //-------------------------------------------------------------------------------------------    
    };
    //
    if ( forceclean ){
      //-------------------------------------------------------------------------------------------
      //Clean the GL_RUN_FRAGMENTS for the given file
      //-------------------------------------------------------------------------------------------
      if ( beverbose ) printf(" 10 => Clean the GL_RUN_FRAGMENTS table for file %s \n",fcleanfile.Data());
      WAR[8] = pamDB->CleanGL_RUN_FRAGMENTS((TString)gSystem->BaseName(fcleanfile.Data()));
    };
    //
    //
    if ( validate ){
      //-------------------------------------------------------------------------------------------
      //Validate runs
      //-------------------------------------------------------------------------------------------
      if ( beverbose ) printf(" 11 => Validate runs table for file %s \n",validfile.Data());
      WAR[9] = pamDB->ValidateRuns((TString)gSystem->BaseName(validfile.Data()));
      //-------------------------------------------------------------------------------------------    
    };
    //
    pamDB->CheckValidate(olderthan);
    //
    if( pamDB->Validate() ){  
      //-------------------------------------------------------------------------------------------
      //Clean the GL_RUN_FRAGMENTS
      //-------------------------------------------------------------------------------------------
      if ( beverbose ) printf(" 10 => Clean the GL_RUN_FRAGMENTS table (earlier than %s) \n",pamDB->GetCleanTime() );
      WAR[8] = pamDB->CleanGL_RUN_FRAGMENTS();
      //-------------------------------------------------------------------------------------------    	
      //-------------------------------------------------------------------------------------------
      //Validate runs
      //-------------------------------------------------------------------------------------------
      if ( beverbose ) printf(" 11 => Validate runs (earlier than %s)\n",pamDB->GetCleanTime());
      WAR[9] = pamDB->ValidateRuns();
      //-------------------------------------------------------------------------------------------    
    };

    if(! tlefilename.IsNull() ) {
      //----------------------------------------------
      //Populate GL_TLE table using the file provided
      //----------------------------------------------
      if ( beverbose ) printf(" 12 => Insert TLE elements in the GL_TLE table from file %s \n",tlefilename.Data());
      WAR[10] = pamDB->populateTLE();
    }
	
  } catch (Int_t exc) {
    signal = exc;
    switch(exc){
    case -1: message += " DB connection failure"; break;
    case -2: message += " Connection failure"; break;
    case -3: message += " Cannot determine the timesync, use the -tsync and -obt0 options to override"; break;
    case -4: message += " Error querying DB"; break;
    case -5: message += " Inconsistent OBT/pkt_num"; break;
    case -6: message += " The file is not in the database"; break;
    case -7: message += " Cannot open TLE file"; break;
    case -8: message += " Event file is empty"; break;
    case -9: message += " No VarDump no BOOT number, use the -boot option to override"; break;
    case -10: message += " No results from DB"; break;
    case -11: message += " Raw file not found"; break;
    case -12: message += " Cannot open Level0 file"; break;
    case -13: message += " No consistent OBT/PKT_NUM information"; break;
    case -14: message += " Not supported yet: run with no events, no runtrailer, no runheader"; break;
    case -15: message += " Not supported yet: run with no runheader at the beginning/end of file not recognized as run fragment"; break;
    case -16: message += " No Physics tree in Level0 file"; break;
    case -17: message += " No RunHeader tree in Level0 file"; break;
    case -18: message += " No RunTrailer tree in Level0 file"; break;
    case -19: message += " No Mcmd tree in Level0 file"; break;
    case -20: message += " No VarDump tree in Level0 file"; break;
    case -21: message += " No CalibCalPed tree in Level0 file"; break;
    case -22: message += " No CalibTrk1 tree in Level0 file"; break;
    case -23: message += " No CalibTrk2 tree in Level0 file"; break;
    case -24: message += " No CalibS4 tree in Level0 file"; break;
    case -25: message += " Cannot find the run just inserted"; break;
    case -26: message += " Raw file not found looking for VarDump"; break;
    case -27: message += " Cannot determine downlink orbit, wrong filename format?"; break;
    case -28: message += " Cannot assign a RUN ID unique number!"; break;
    default: message += " Unidentified error"; break;
    };
    printf("\n");
    printf(" ERROR (%i) %s \n",exc,message.Data());
    printf("\n");
  }
  //
  // warnings handlers
  //
  Bool_t printwarning = false;
  message="";
  for (UInt_t j=0; j<sizeofwar; j++){
    if ( WAR[j] ){
      printwarning = true;
      //
      if ( j == 0 ){ // insertPamelaRaw warnings
	for (UInt_t bit=0; bit<32; bit++){
	  if ( WAR[j] & (1 << bit) ){
	    if ( bit == 0 ) message += "=> RAW file already inserted\n";
	    else message += "=> Unidentified insertPamelaRaw warning\n";
	  };
	};
      };
      //
      if ( j == 1 ){ // insertTimeSync warnings
	for (UInt_t bit=0; bit<32; bit++){
	  if ( WAR[j] & (1 << bit) ){
	    if ( bit == 0 ) message += "=> TIMESYNC already inserted\n";
	    else if ( bit == 1 ) message += "=> No mcmd timesync in the file\n";
	    else if ( bit == 2 ) message += "=> No runheaders in the file\n";
	    else if ( bit == 3 ) message += "=> No runtrailers in the file\n";
	    else if ( bit == 4 ) message += "=> No mcmd inclination in the file\n";
	    else  message += "=> Unidentified insertTimeSync warning\n";
	  };
	};
      };
      //
      if ( j == 2 ){ // insertPamelaRoot warnings
	for (UInt_t bit=0; bit<32; bit++){
	  if ( WAR[j] & (1 << bit) ){
	    if ( bit == 0 ) message += "=> ROOT file already inserted\n";
	    else message += "=> Unidentified insertPamelaRoot warning\n";
	  };
	};
      };
      //
      if ( j == 3 ){ // assignBOOTnumber warnings
	for (UInt_t bit=0; bit<32; bit++){
	  if ( WAR[j] & (1 << bit) ){
	    if ( bit == 0 ) message += "=> BOOT number already inserted\n";//
	    else if ( bit == 1 ) message += "=> VarDump event tree is empty, use the -boot option to override\n";//
	    else if ( bit == 2 ) message += "=> No BOOT number in VarDump(!), use the -boot option to override\n";//
	    else if ( bit == 4 ) message += "=> The file is not in the database looking for VarDump, use the -boot option to override\n";//
	    else message += "=> Unidentified assignBOOTnumber warning\n";
	  };
	};
      };
      //
      if ( j == 4 ){ // insertPamelaRUN
	for (UInt_t bit=0; bit<32; bit++){
	  if ( WAR[j] & (1 << bit) ){
	    if      ( bit == 0 ) message += "=> Inconsistent PKT/OBT sequence\n";
	    else if ( bit == 1 ) message += "=> No physics events in the file\n";
	    else if ( bit == 2 ) message += "=> Less than 2 physics events in the file\n";
	    else  message += "=> Unidentified insertPamelaRun warning\n";
	  };
	};
      };
      //
      if ( j == 5 ){ // insertCALO_CALIB
	for (UInt_t bit=0; bit<32; bit++){
	  if ( WAR[j] & (1 << bit) ){
	    if      ( bit == 0 ) message += "=> No calorimeter calibrations in the file\n";
	    else  message += "=> Unidentified insertCaloCalib warning\n";
	  };
	};
      };
      //
      //
      if ( j == 6 ){ // insertTRACKER_CALIB
	for (UInt_t bit=0; bit<32; bit++){
	  if ( WAR[j] & (1 << bit) ){
	    if      ( bit == 0 ) message += "=> No tracker calibrations in the file\n";
	    else  message += "=> Unidentified insertTrkCalib warning\n";
	  };
	};
      };
      //
      //
      if ( j == 7 ){ // insertS4_CALIB
	for (UInt_t bit=0; bit<32; bit++){
	  if ( WAR[j] & (1 << bit) ){
	    if      ( bit == 0 ) message += "=> No s4 calibrations in the file\n";
	    else  message += "=> Unidentified insertS4calib warning\n";
	  };
	};
      };
      //
      //
      if ( j == 8 ){ // CleanGL_RUN_FRAGMENTS
	for (UInt_t bit=0; bit<32; bit++){
	  if ( WAR[j] & (1 << bit) ){
	    if      ( bit == 0 ) message += "=> Skip the GL_RUN_FRAGMENTS table cleaning\n";
	    if      ( bit == 1 ) message += "=> Problems retrieving ROOT ID from DB\n";
	    else  message += "=> Unidentified CleanGL_RUN_FRAGMENTS warning\n";
	  };
	};
      };
      //
      if ( j == 9 ){ // ValidateRuns
	for (UInt_t bit=0; bit<32; bit++){
	  if ( WAR[j] & (1 << bit) ){
	    if      ( bit == 0 ) message += "=> Skip the run validation \n";
	    if      ( bit == 1 ) message += "=> Problems retrieving ROOT ID from DB\n";
	    else  message += "=> Unidentified ValidateRuns warning\n";
	  };
	};
      };
      //
      if ( j == 10 ){ // populateTLE
	for (UInt_t bit=0; bit<32; bit++){
	  if ( WAR[j] & (1 << bit) ){
	    if      ( bit == 0 ) message += "=> TLE insertion failed\n";
	    if      ( bit == 1 ) message += "=> No new TLE available\n";
	    else  message += "=> Unidentified populateTle warning\n";
	  };
	};
      };
      //
      if ( j == 11 ){ // removeFile
	for (UInt_t bit=0; bit<32; bit++){
	  if ( WAR[j] & (1 << bit) ){
	    if      ( bit == 0 ) message += "=> No file to delete from DB \n";
	    else  message += "=> Unidentified populateTle warning\n";
	  };
	};
      };
    };
  };
  //
  if ( printwarning ){
    printf("\n");
    printf(" WARNING(s):\n%s\n",message.Data());
    printf("\n");
    if ( !signal ) signal = 1;
  };
  //
  //---------------------------------------------------------------------------------------
  // Close and exit
  //---------------------------------------------------------------------------------------
  if ( beverbose ) printf(" 13 => Free objects and close SQL connection \n");
  pamDB->Close();
  //
  printf("\n");
  printf(" Finished, exiting...\n");
  printf("\n");
  //
  // Close redirection if the case.
  //
  if ( !beverbose ) close(nul);
  //
  exit(signal);
  //
}
