////////////////////////////////////////////////////////////////////
/// \file rat.cc
///
/// \brief  Main
///
/// \author Stan Seibert <volsung@physics.utexas.edu>
///
/// REVISION HISTORY:\n
///     29 Mar 2010 : Gabriel Orebi Gann - add ``UnPackEvents'' producer
///     19 Dec 2011 : A Mastbaum - add InDispatchProducer
///     28 Sep 2012 : A Mastbaum - remove InDispatchProducer
///     25 Sep 2013 : A Mastbaum - remove InPackedProducer
///     13 May 2015 : W. Heintzelman - add global pointer to the RAT RunManager
///     22 Aug 2015 : N. Barros - Changed default option for -N to -1
///     22 Nov 2016 : N. Barros - Added run mc mode.
///     04 Dec 2016 : N. Barros - Added list of run-level tables to not use default plane
///     26 Jan 2017 : N. Barros - Changed logging logic to have separate log file level
///     22 Mar 2017 : N. Barros - Added support for default plane lockout and cleared DB initialization
///     26 Oct 2017 : T Kaptanoglu - Add flag to allow working in complete offline mode when processing data
///     11 Jan 2018 : E Leming - Added option to load ratdb server adderss from an environment variable
///     02 Mar 2018 : B. Land - Add flag to set database tag
///     11 Nov 2018 : J. Caravaca - Removed 'cout << cerr' that prevented compilation
///
/// \details
///
////////////////////////////////////////////////////////////////////////

#include <sys/types.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <stdlib.h>
#include <iostream>
#include <string>

#include <G4UImanager.hh>
#include <G4UIterminal.hh>
#include <G4UItcsh.hh>
#include <Randomize.hh>
#include <globals.hh>

// time.h must come after Randomize.hh on RedHat 7.3
#include <time.h>

#include <RAT/Log.hh>
#include <RAT/RunManager.hh>
#include <RAT/ProcBlock.hh>
#include <RAT/ProcBlockManager.hh>
#include <RAT/PythonProc.hh>
#include <RAT/SignalHandler.hh>
#include <RAT/Version.hh>
#include <RAT/DB.hh>
#include <RAT/DBExceptions.hh>
#include <RAT/DBMessenger.hh>
#include <RAT/OutROOTProc.hh>
#include <RAT/G4Stream.hh>
#include <RAT/Random.hh>
#include <TRandom.h>
#include <TStopwatch.h>
#include <sys/utsname.h> //For uname()

// event producers
#include <RAT/InZDABProducer.hh>
#ifdef COMPILE_DISPATCH
#include <RAT/InDispatchProducer.hh>
#endif
#include <RAT/InROOTProducer.hh>
#include <RAT/InNetProducer.hh>
#include <RAT/InSOCProducer.hh>
#include <RAT/Gsim.hh>

using std::string;
using std::vector;
using std::cout;
using std::endl;
using std::cerr;
using std::ifstream;

using namespace RAT;

// Global variable  -- pointer to the RAT RunManager:
RAT::RunManager* gTheRatRunManager = NULL;

class CmdOptions {
public:
  CmdOptions() : seed(-1),save_macro(true), fNumEvents(-1),
                 output_filename(""),run_number(-1), subrun_number(-1), dbserver(""),
                 run_mc_mode(false),no_remote_db(false), default_plane_lock(true) { };
  long seed;
  bool save_macro;
  long fNumEvents;
  std::vector<std::string> input_filename;
  std::string output_filename;
  std::vector<std::string> python_processors;
  std::string run_duration;
  long run_number;
  long subrun_number;
  std::string dbserver;
  std::string ratdb_tag;
  bool run_mc_mode;
  bool no_remote_db;
  bool default_plane_lock;
};

CmdOptions parse_command_line(int argc, char** argv);
void help();
string get_short_hostname();
string get_long_hostname();

void parse_run(std::string str, int &run, int &subrun) {
  cout << BBLUE << "Overriding run number with " << str << CLR << endl;
  // First tokenize the string
  int tmp_num;
  size_t sep_pos = str.find(':');
  std::string tmpstr;
  if ( sep_pos == std::string::npos) {
    // There is no subrun
    if (str.size() > 0) {
      tmp_num = atol(str.c_str());
      if (tmp_num < 1) {
        cout << BRED << "ERROR:Invalid run number (" << str << ")" << CLR << endl;
        exit(0);
      } else {
        run = tmp_num;
      }

    } else {
      cout << BRED << "ERROR:Input string does not seem to be a valid number (" << str << ")" << CLR << endl;
      exit(0);
    }
  } else {
    // there is a subrun
    tmpstr = str.substr(0,sep_pos);
    if (tmpstr.size() > 0) {
      tmp_num = atol(tmpstr.c_str());
      if (tmp_num < 1) {
        cout << BRED << "ERROR:Invalid run number (" << tmpstr << ")" << CLR << endl;
        exit(0);
      } else {
        run = tmp_num;
      }
    } else {
      cout << BRED << "ERROR:Input string does not seem to be a valid number (" << str << ")" << CLR << endl;
      exit(0);
    }
    // Reassign for the subrun
    tmpstr = str.substr(sep_pos+1);
    if (tmpstr.size() > 0) {
      tmp_num = atol(tmpstr.c_str());
      if (tmp_num < 0) {
        cout << BRED << "ERROR:Invalid subrun number (" << tmpstr << ")" << CLR << endl;
        exit(0);
      } else {
        subrun = tmp_num;
      }
    } else {
      cout << BRED << "ERROR:Input string does not seem to be a valid number (" << str << ")" << CLR << endl;
      exit(0);
    }
  }
}

int main (int argc, char** argv)
{
    gRandom = new RAT::Random();
    TStopwatch runTime;
    runTime.Start();

    CmdOptions options = parse_command_line(argc, argv);

    info << RAT::RED << "This is SNO+ RAT " << RAT::GetRATVersion()
         << " (" << RAT::GetRATRevision() << ")" << RAT::CLR << newline;

    //Hostname and machine probing.
    struct utsname nameinfo;
    int uname_result = uname(&nameinfo);

    //uname() returns a non-negative integer on success.
    if ( uname_result >= 0 )
    {
      detail << "Hostname detected: " << nameinfo.nodename << newline;
      detail << "Hardware detected: " << nameinfo.machine << newline;
    }

    //Otherwise notify the user that there was an issue.
    else
    {
      info << "There was a problem detecting machine information.  Error strings will"
           << " be written to the data structure!" << newline;
    }

    info << "Status messages enabled: info ";
    detail << "detail ";
    debug << "debug ";
    info << "\n";

    // Latch G4 output into RAT::Log streams
    SetG4coutStream(G4Stream::DETAIL);
    SetG4cerrStream(G4Stream::WARN);

    // Seed random number generator using current time and process id
    // (Use both just in case multiple processes started at same time)
    time_t start_time = time(NULL);
    pid_t pid = getpid();
    info << "Hostname: " << get_long_hostname() << " PID: " << pid << newline;
    if (options.seed == -1) // No override by command switch
      options.seed = start_time ^ (pid << 16);

    info << BBLUE << "Seeding random number generator: " << CLR << options.seed << newline;
    CLHEP::HepRandom::setTheSeed(options.seed);

    // initialize RATDB and load all .ratdb files in $GLG4DATA,
    // usually set to $RATROOT/data
    DB *rdb = NULL;
    DBMessenger *rdb_messenger = NULL;
  try{
    rdb = DB::Get();
    if (options.no_remote_db) {
      info << BCYAN << "Enabling 'airplane mode' (no remote DB access)." << CLR << newline;
      rdb->SetAirplaneModeStatus(true);
    }
    if (!options.default_plane_lock) {
      info << BCYAN << "Disabling default table lock entirely (BE CAREFUL)." << CLR << newline;
      rdb->SetDefaultPlaneLockStatus(false);
    }

    if (options.run_number != -1) {
      // -- If a run number is specified, one should block default plane tables
      rdb->SetRunID(options.run_number);
    }

    // If the user explicitly passed a server address, use that
    if (options.dbserver.length() != 0) {
      info << BBLUE << "Loading database tables from remote RATDB..." << CLR << newline;
      rdb->SetServer(options.dbserver,false);
    }

    info << BBLUE << "Loading default database tables..." << CLR << newline;
    rdb->LoadDefaults();

    if (options.ratdb_tag.length() != 0) {
      info << BBLUE << "Using RATDB tag " << options.ratdb_tag << CLR << newline;
      rdb->SetDbTag(options.ratdb_tag);
    }

    if (options.run_number != -1) {
      rdb->LoadDefaultPlaneLocks();
    }
    rdb_messenger = new DBMessenger();

    // Set default input and output files
    if (options.input_filename.size() != 0) {
        // This is always executed before the processing of the macros
        // So it is worth to simply replace
        rdb->SetSArray("IO", "", "default_input_filename", options.input_filename);
        info << "Setting default input files to :" << newline;
        for (size_t i = 0; i < options.input_filename.size(); ++i) {
          info << " :: " << options.input_filename.at(i) << "\n";
        }
    }
    if (options.output_filename != "") {
        rdb->SetS("IO", "", "default_output_filename", options.output_filename);
        info << "Setting default output file to " << options.output_filename << "\n";
    }
    if( options.fNumEvents != -1 ) {
        rdb->SetI( "IO", "", "default_num_events", options.fNumEvents );
        info << "Setting default number of events to " << options.fNumEvents << "\n";
    }
    if(options.run_duration.size()) {
      if (options.fNumEvents != -1) {
          Log::Die("Can't specify both duration and number of events. Choose one.",1);
      }
      // Replace the ':' by a space
      std::replace(options.run_duration.begin(),options.run_duration.end(),':',' ');
      info << "Setting run to be time controlled with a duration of ["
           << options.run_duration << "]." << newline;
      rdb->SetI("IO","","timed_run",1);
      rdb->SetS("IO","timed_duration",options.run_duration);
    }

  }
  catch (DBNotFoundError &e) {
    cerr << "DB: Field " + e.table + "[" + e.index + "]." + e.field
        + " lookup failure.  Does not exist or has wrong type.\n";
    if (rdb->GetTblNoDefaultPlaneStatus(e.table,e.index)) {
      cerr << "rat:: Table " << e.table << "[" << e.index << "] matches a default plane lock entry." << newline;
      cerr << "rat:: Make sure that the table exists for this run in the remote database." << newline;
    }
    return 1;
  }
  catch (ParseError& e)
  {
    cerr << e.GetFull() << newline;
    return 1;
  }
  catch (int &code) {
    cerr << "DB: Failed to load the database. Exception caught with code " << code << newline;
    return code;
  }
  catch(std::exception &e) {
    cerr << "Caught STD exception initializing RATDB : " << e.what() << newline;
    return 1;
  }
  catch(std::string &msg) {
    cerr << "Caught string exception initializing RATDB : " << msg << newline;
    return 1;
  }
  catch(...) {
    cerr << "DB: Unknown exception caught." << newline;
    return 1;
  }


  try { // Catch database errors

      // Main analysis block -- will contain user-constructed analysis sequence
      info << BBLUE << "Initializing Processor block..." << CLR << newline;
      ProcBlock *mainBlock = new ProcBlock;
      // Process block manager -- Supplies user commands to construct analysis
      // sequence and actually does the processor creation
      ProcBlockManager *blockManager = new ProcBlockManager(mainBlock);

      // Build event producers
      info << BBLUE << "Initializing run manager..." << CLR << newline;
      RunManager* runManager = new RunManager(mainBlock);

      if (options.run_number != -1  || options.subrun_number != -1) {
        warn << BGREEN << "Overriding the (sub)run number to be simulated." << CLR << newline;
        runManager->GetGsim()->SetRunNumberOverride(options.run_number);
        runManager->GetGsim()->SetSubrunNumberOverride(options.subrun_number);
      }

      if (options.run_mc_mode) {
        // Run duration and number of events cannot be set when in run_mc mode
        if (options.run_duration.size()) {
          Log::Die("Can't set run_mc mode and set a run duration.",1);
        }
        if (options.fNumEvents != -1) {
          Log::Die("Can't set run_mc mode and a number of events.",1);
        }
        // the run number has to be specified in run MC mode
        if (options.run_number == -1) {
          Log::Die("Run mc mode enabled, but run number not specified.",1);
        }
        warn << BGREEN << "** Running in run simulation mode (run_mc) **" << CLR << newline;
        runManager->GetGsim()->SetRunSimMode(true);
      }

      gTheRatRunManager = runManager;
      info << BBLUE << "Initializing event producers..." << CLR << newline;
      InROOTProducer* inroot = new InROOTProducer(mainBlock);
      InZDABProducer* inzdab = new InZDABProducer(mainBlock);
#ifdef COMPILE_DISPATCH
      InDispatchProducer* indispatch = new InDispatchProducer(mainBlock);
#endif
      InNetProducer* innet = new InNetProducer(mainBlock);
      InSOCProducer* insoc = new InSOCProducer(mainBlock);

      // Setup signal handler to intercept Ctrl-C and quit event loop
      // nicely (closing files and all that).
      SignalHandler::Init();

      // Initialize the user interface
      info << BBLUE << "Initializing user interface..." << CLR << newline;
      G4UImanager* theUI = G4UImanager::GetUIpointer();
      // Commands can only be removed once without Geant4 segfaulting, so it's best to do so here
      theUI->RemoveCommand( theUI->GetTree()->FindPath( "/run/beamOn" ) ); // Replaced with /rat/run/start
      theUI->RemoveCommand( theUI->GetTree()->FindPath( "/tracking/storeTrajectory" ) ); // Replaced with /rat/tracking/store full
      if (options.run_mc_mode) {
        // disallow the duration macro command
        theUI->RemoveCommand( theUI->GetTree()->FindPath( "/rat/run/duration" ) );
      }
      // Add any python processors specified on the command line
      for (unsigned i=0; i < options.python_processors.size(); i++) {
          Processor *p = new PythonProc;
          p->SetS("class", options.python_processors[i]);
          mainBlock->DeferAddProcessorCommand(p);
      }

      // interactive or batch according to command-line args
      if (optind - argc == 0) {
          // Interactive mode
          // G4UIterminal is a (dumb) terminal.
          // ..but it can be made smart by adding a "shell" to it
          info << BBLUE << "Starting interactive session..." << CLR << newline;
          SetG4coutStream(G4Stream::DEFAULT);
          G4UIsession* theSession = new G4UIterminal(new G4UItcsh);
          theSession -> SessionStart();
          delete theSession;
      }
      else {
          // Batch mode, with optional user interaction
          G4String command = "/control/execute ";
          for (int iarg=optind; iarg<argc; iarg++) {
              // process list of macro files; "-" means interactive user session
              G4String fileName = argv[iarg];
              if ( fileName == "-" ) {
                  // interactive session requested
                  info << BBLUE << "Starting interactive session..." << CLR << newline;
                  SetG4coutStream(G4Stream::DEFAULT);
                  G4UIsession* theSession = new G4UIterminal(new G4UItcsh);
                  theSession -> SessionStart();
                  delete theSession;
              }
              else {
                  if (options.save_macro) {
                      // Check that file exists, then
                      // Read file contents and log them
                      ifstream macro(fileName);
                      string macroline;
                      if ( ! macro.is_open()){
                          Log::Die("File " + fileName + " does not exist.");
                          }
                      while (!macro.eof()) {
                          getline(macro, macroline);
                          Log::AddMacro(macroline+"\n");
                      }
                  }

                  // execute given file
                  info << BBLUE << "Running macro..." << CLR << newline;
                  theUI -> ApplyCommand(command+fileName);
              }
          }
      }
      // User exit or macros finished, clean up

      // Hack to close GLG4sim output file if used
      theUI->ApplyCommand("/event/output_file");

      // Report on CPU usage
      struct rusage usage;
      getrusage(RUSAGE_SELF, &usage);
      float usertime = usage.ru_utime.tv_sec + usage.ru_utime.tv_usec / 1e6;
      float systime = usage.ru_stime.tv_sec + usage.ru_stime.tv_usec / 1e6;

      info << dformat("\nRun time:  %8.2f sec\n", runTime.RealTime());
      info << dformat("CPU usage: user %8.2f sec\tsys %6.2f sec\n",
                       usertime, systime);

      // Check whether the DB fields the user set were actually used
      rdb->CheckSetFieldUsage();


      delete blockManager;
      delete mainBlock; // implicitly deletes all processor instances the user
                        // built, and closes up associated files
      delete runManager;
      delete inroot;
      delete inzdab;
#ifdef COMPILE_DISPATCH
      delete indispatch;
#endif
      delete innet;
      delete insoc;

      delete rdb_messenger;

  }
  catch (DBNotFoundError &e) {
      cerr << "DB: Field " + e.table + "[" + e.index + "]." + e.field
        + " lookup failure.  Does not exist or has wrong type.\n";
      return 1;
  }
  catch (DS::DataNotFound &e) {
    cerr << "RAT: RAT::DS::DataNotFound Exception caught accessing a DS variable.\n";
    cerr << "Class  : " << e.fClassName << endl;
    cerr << "Field  : " << e.fFieldName << endl;
    cerr << "Detail : " << e.what() << endl;
    return 1;
  }
  catch (ParseError& e)
    {
      cerr << e.GetFull() << newline;
      return 1;
    }
  catch (int &code) {
      cerr << "rat: Caught a return code." << endl;
      return code;
  }
  /** catch logic error exceptions.
   * This includes invalid_argument, domain_error, length_error, out_of_range
   */
  catch (std::logic_error &e) {
    cerr << "rat: Logic error caught" << endl;
    cerr << "details: " << e.what() << endl;
    return 1;
  }
  /**
   * Catch run time errors: range_error,overflow,underflow, etc
   */
  catch (std::runtime_error &e) {
    cerr << "rat: Runtime error caught" << endl;
    cerr << "details: " << e.what() << endl;
    return 1;
  }
  /**
   * bad_alloc
   */
  catch (std::bad_alloc &e) {
    cerr << "rat: Bad allocation error caught" << endl;
    cerr << "details: " << e.what() << endl;
    return 1;
  }
  /**
   * Generic exception
   */
  catch (std::exception &e) {
    cerr << "rat: STL exception caught." << endl;
    cerr << e.what() << endl;
    return 1;
  }
  catch(...) {
    cerr << "rat: Unidentified exception caught " << std::endl;
    return 1;
  }

  return 0;
}

#define OPT_NO_SAVE_MACRO 1000

CmdOptions parse_command_line(int argc, char** argv) {
  static struct option opts[] = { {"quiet", 0, NULL, 'q'},
                                  {"help", 0, NULL, 'h'},
                                  {"verbose", 0, NULL, 'v'},
                                  {"debug", 0, NULL, 'd'},
                                  {"version", 0, NULL, 'V'},
                                  {"logfile", 1, NULL, 'l'},
                                  {"seed", 1, NULL, 's'},
                                  {"input", 1, NULL, 'i'},
                                  {"output", 1, NULL, 'o'},
                                  {"python", 1, NULL, 'p'},
                                  {"no-save-macro", 0, NULL, OPT_NO_SAVE_MACRO},
                                  {"database", 1, NULL, 'b'},
                                  {"run_num", 1, NULL, 'n'},
                                  {"num-events", 1, NULL, 'N'},
                                  {"duration",1,NULL,'T'},
                                  {"run_mc",1,NULL,'r'},
                                  {"log-level",1,NULL,'L'},
                                  {"airplane-mode",0,NULL,'P'},
                                  {"allowdefault",0,NULL,'X'},
                                  {"ratdb_tag",1,NULL,'d'},
                                  {0, 0, 0, 0} };


  CmdOptions options;
  int display_level = Log::INFO;
  int log_level = Log::DETAIL;
  std::string logfilename = std::string("rat.")+get_short_hostname()+"."+::to_string(getpid())+".log";
  std::string strrun;
  int run = -1, subrun = -1;
  int option_index = 0;
  int c = getopt_long(argc, argv, "qhvVPXl:n:r:s:b:d:o:i:p:N:T:L:", opts, &option_index);
  while (c != -1) {
        switch (c) {

          case 'q':
              if (display_level > Log::WARN)
                  display_level--;
              break;
          case 'v':
              if (display_level < Log::DEBUG)
                  display_level++;
              break;
          case 'h': help(); exit(0); break;
          case 'V':
              cout << "SNO+ RAT " << RAT::GetRATVersion() << " (" << RAT::GetRATRevision() << ")" << std::endl;
              exit(0);
              break;
          case 'n':
            strrun = optarg;
            parse_run(strrun,run,subrun);
            options.run_number = run;
            options.subrun_number = subrun;
            break;
          case 'l': logfilename = optarg; break;
          case 'L': log_level = atol(optarg); break;
          case 's': options.seed = atol(optarg); break;
          case 'b': options.dbserver = optarg; break;
          case 'd': options.ratdb_tag = optarg; break;
          case 'i': options.input_filename.push_back(optarg); break;
          case 'o': options.output_filename = optarg; break;
          case 'p': options.python_processors.push_back(optarg); break;
          case 'N': options.fNumEvents = atol(optarg); break;
          case 'T': options.run_duration = optarg; break;
          case 'r':
            options.run_mc_mode = true;
            strrun = optarg;
            parse_run(strrun,run,subrun);
            options.run_number = run;
            options.subrun_number = subrun;
            break;
          case 'P': options.no_remote_db = true; break;
          case 'X': options.default_plane_lock = false; break;
          case OPT_NO_SAVE_MACRO: options.save_macro = false; break;
          default: exit(1);
        }
        c = getopt_long(argc, argv, "qhvVPXl:n:r:s:b:d:o:i:p:N:T:L:", opts, &option_index);
  }

  // Setup logging
  if (log_level > 3) {
    cout << "Warning: Invalid file log level [" << log_level << "]. Setting it to 'debug' (3)" << endl;
    log_level = 3;
  } else if (log_level < 0) {
    cout << "Warning: Invalid file log level [" << log_level << "]. Setting it to 'warn' (0)" << endl;
    log_level = 0;
  }
  Log::Init(logfilename, Log::Level(display_level), Log::Level(log_level));

  return options;
}

void help(){
  // Logging not yet initialized so use cout
  cout << "SNO+ RAT " << RAT::GetRATVersion() << " (" << RAT::GetRATRevision()
       << ")" << std::endl << std::endl;
  cout << "Usage: rat [flags] macro1.mac macro2.mac ...\n";
  cout << " -q, --quiet          Quiet mode, only show warnings\n";
  cout << " -v, --verbose        Verbose mode, show more information.\n"
          "                      Multiple -v options increase verbosity. "
                                 "The maximum is 2\n";
  cout << " -L, --log-level      Level of file logging (0:warn,1:info,2:detail[default],3:debug)\n";
  cout << " -V, --version        Show program version and exit\n";
  cout << " -h, --help           Display this help message and exit\n";
  cout << " -l, --log            Set log filename. "
                                 "Defaults to rat.[host].[pid].log\n";
  cout << " -n, --run_num        Set the run number. Defaults to MC RATDB entry (0).\n";
  cout << " -s, --seed           Set random number seed.\n"
          "                      If no -s option is present, default is to seed with\n"
          "                      XOR of clock time and process id.\n";
  cout << " -i, --input          Set default input filename. \n"
          "                      (Does not override filename in macro!)\n";
  cout << " -o, --output         Set default output filename. \n"
          "                      (Does not override filename in macro!)\n";
  cout << " -p, --python=class   Append python processor to event loop\n";
  cout << " -N, --num-events     Set the number of events to simulate\n"
          "                      or the total number of events processed for data\n";
  cout << " -T, --duration       Set the time span of the simulation. \n"
          "                      (Exclusive with number of events.)\n";
  cout << " -b, --database       URL to PgSQL database\n";
  cout << " -d, --ratdb_tag      RATDB database tag to use; live view if unspecified.\n";
  cout << " -r, --run_mc         Set the execution to be in run simulation mode.\n";
  cout << "                      In this mode, RAT will use the run number (passed as argument) and the information\n";
  cout << "                      loaded from the RUN ratdb table to set the duration of the simulation.\n";
  cout << "                      In this mode, one cannot set neither the duration nor the number of events.\n";
  cout << " --no-save-macro      Do not save macro into output ROOT file.\n";
  cout << " -P, --airplane-mode  Activate airplane mode, i.e., do not connect to the remote database.\n";
  cout << " -X, --allowdefault   Allow default plane access when processing data. Use with -P for complete offline mode.\n"
          "                      WARNING: Use extreme care working in complete offline mode.\n";
}

std::string get_short_hostname()
{
  char c_hostname[256];
  gethostname(c_hostname, 256);
  std::string hostname(c_hostname);
  std::vector<std::string> parts = split(hostname, ".");

  if (parts.size() > 0)
    return parts[0];
  else
    return std::string("unknown");
}

std::string get_long_hostname()
{
  char c_hostname[256];
  gethostname(c_hostname, 256);
  return std::string(c_hostname);
}