
// -*- C++ -*-
//
// This file is part of the Coriolis Software.
// Copyright (c) UPMC/LIP6 2008-2010, All Rights Reserved
//
// ===================================================================
//
// $Id$
//
// x-----------------------------------------------------------------x 
// |                                                                 |
// |                   C O R I O L I S                               |
// |          Alliance / Hurricane  Interface                        |
// |                                                                 |
// |  Author      :                    Jean-Paul CHAPUT              |
// |  E-mail      :       Jean-Paul.Chaput@asim.lip6.fr              |
// | =============================================================== |
// |  C++ Module  :       "./Utilities.cpp"                          |
// | *************************************************************** |
// |  U p d a t e s                                                  |
// |                                                                 |
// x-----------------------------------------------------------------x



#include  <csignal>
#include  <cstdlib>
#include  <cstring>
#include  <iomanip>

#include  <boost/program_options.hpp>
namespace boptions = boost::program_options;

#include  "vlsisapd/configuration/Configuration.h"
#include  "hurricane/Warning.h"
#include  "hurricane/isobar/Script.h"
#include  "crlcore/Utilities.h"


namespace {

  using namespace Hurricane;
  using namespace CRL;


  void  verboseLevel1Changed ( Cfg::Parameter* p )
  {
    if ( p->asBool() ) mstream::enable  ( mstream::Verbose1 );
    else               mstream::disable ( mstream::Verbose1 );

  //cerr << "Verbose Level 1: " << boolalpha << p->asBool() << endl;
  }


  void  verboseLevel2Changed ( Cfg::Parameter* p )
  {
    if ( p->asBool() ) mstream::enable  ( mstream::Verbose2 );
    else               mstream::disable ( mstream::Verbose2 );
  }


  void  infoChanged ( Cfg::Parameter* p )
  {
    if ( p->asBool() ) mstream::enable  ( mstream::Info );
    else               mstream::disable ( mstream::Info );
  }


  void  catchCoreChanged ( Cfg::Parameter* p )
  {
    System::setCatchCore ( p->asBool() );
  }


  void  logModeChanged ( Cfg::Parameter* p )
  {
    if ( p->asBool() ) tty::enable  ();
    else               tty::disable ();
  }


  void  traceLevelChanged ( Cfg::Parameter* p )
  {
    ltracelevel ( p->asInt() );
  }


  std::string  environmentMapper ( std::string environmentName )
  {
    if ( environmentName == "CORIOLIS_TOP" ) return "coriolis_top";
    return "";
  }


}  // End of anonymous namespace.


int           tty::_width          = 80;
bool          tty::_enabled        = true;
unsigned int  mstream::_activeMask = mstream::Verbose0;

mstream  cmess0 ( mstream::Verbose0, std::cout );
mstream  cmess1 ( mstream::Verbose1, std::cout );
mstream  cmess2 ( mstream::Verbose2, std::cout );
mstream  cinfo  ( mstream::Info    , std::cout );


// -------------------------------------------------------------------
// Class  :  "::Dots".


Dots::Dots ( const std::string& left, const std::string& right ) : _left(left), _right(right) { }


Dots  Dots::asPercentage ( const std::string& left, float value )
{
  std::ostringstream right;
  right << std::setprecision(3) << (value*100.0) << "%";
  return Dots(left,right.str());
}


Dots  Dots::asBool ( const std::string& left, bool value )
{ std::ostringstream right; right << std::boolalpha << value; return Dots(left,right.str()); }


Dots  Dots::asUInt ( const std::string& left, unsigned int value )
{ std::ostringstream right; right << value; return Dots(left,right.str()); }


Dots  Dots::asInt ( const std::string& left, int value )
{ std::ostringstream right; right << value; return Dots(left,right.str()); }


Dots  Dots::asULong ( const std::string& left, unsigned long value )
{ std::ostringstream right; right << value; return Dots(left,right.str()); }


Dots  Dots::asSizet ( const std::string& left, size_t value )
{ std::ostringstream right; right << value; return Dots(left,right.str()); }


Dots  Dots::asDouble ( const std::string& left, double value )
{ std::ostringstream right; right << value; return Dots(left,right.str()); }


Dots  Dots::asLambda ( const std::string& left, Hurricane::DbU::Unit value )
{ std::ostringstream right; right << Hurricane::DbU::getValueString(value); return Dots(left,right.str()); }


Dots  Dots::asLambda ( const std::string& left, double value )
{ std::ostringstream right; right << Hurricane::DbU::getValueString(value); return Dots(left,right.str()); }


Dots  Dots::asIdentifier ( const std::string& left, const std::string& value )
{ std::ostringstream right; right << "<" << value << ">"; return Dots(left,right.str()); }


Dots  Dots::asString ( const std::string& left, const std::string& value )
{ return Dots(left,value); }


std::ostream& operator<< ( std::ostream& out, const Dots& dots )
{
  int count = tty::getWidth() - 2 - dots._left.length() - dots._right.length();
  out << dots._left << " "; while ( count-- > 0 ) out << "."; out << " " << dots._right;

  return out;
}



namespace CRL {


  using std::cout;
  using std::cerr;
  using std::endl;
  using std::string;
  using std::ostringstream;


# define        SIGTFLT        1


  // Error messages.
  const char* DupSystem          = "\n    Attempt to re-create Alliance System.";
  const char* BadAllocProperty   = "%s::create():\n    Property allocation failed.\n";
  const char* BadCreate          = "%s::create():\n    Memory allocation failed.\n";
  const char* NullDataBase       = "%s:\n\n    The Hurricane DataBase have not been created yet.\n";
  const char* NullTechnology     = "%s:\n\n    The Hurricane DataBase do not contain any technology.\n";
  const char* NullLibrary        = "%s:\n\n    NULL Library given as argument.\n";
  const char* NullCell           = "%s:\n\n    NULL Cell given as argument.\n";
  const char* BadFopen           = "%s:\n\n    Unable to open %s file : \"%s\".\n";
  const char* BadColorValue      = "%s() :\n\n"
                                        "    Invalid color value for color \"%s\" : \"%s\".\n";

// -------------------------------------------------------------------
// Class  :  "CRL::System".


  System* System::_singleton = NULL; //System::get();


  System::System ()
    : _catchCore(true)
  {
  // Immediate setup to avoid some tiresome looping...
    _singleton = this;

  // Set the trap function for the SIGINT signal (CTRL-C).
  //if ( signal(SIGINT,System::TrapSig) == SIG_ERR )
  //  System::TrapSig ( SIGTFLT );

  // Set the trap function for SIGFPE, SIGBUS, SIGABRT and SIGSEGV signals.
    if (   ( signal(SIGFPE , System::_trapSig) == SIG_ERR )
        || ( signal(SIGBUS , System::_trapSig) == SIG_ERR )
        || ( signal(SIGABRT, System::_trapSig) == SIG_ERR )
        || ( signal(SIGPIPE, System::_trapSig) == SIG_ERR )
        || ( signal(SIGSEGV, System::_trapSig) == SIG_ERR ) )
      System::_trapSig ( SIGTFLT );

  // Environment variables reading.
    boptions::options_description options ("Environment Variables");
    options.add_options()
      ( "coriolis_top", boptions::value<string>()->default_value(CORIOLIS_TOP)
                      , "The root directory of the Coriolis installation tree." );

    boptions::variables_map arguments;
    boptions::store  ( boptions::parse_environment(options,environmentMapper), arguments );
    boptions::notify ( arguments );

    if ( bfs::path::default_name_check_writable() )
      bfs::path::default_name_check ( bfs::portable_posix_name );

    bfs::path sysConfDir ( SYS_CONF_DIR );
    if ( not sysConfDir.has_root_path() ) {
      if ( arguments.count("coriolis_top") ) {
        sysConfDir = arguments["coriolis_top"].as<string>() / sysConfDir;
      } else {
        cerr << Error("Environment variable CORIOLIS_TOP not set,"
                      " may be unable to read configuration...") << endl;
      }
    }
    sysConfDir /= "coriolis2";
    _pathes.insert ( make_pair("etc",sysConfDir) );

  // Default configuration loading.
    Cfg::Configuration* conf = Cfg::Configuration::get ();

    Cfg::getParamBool("misc.catchCore"    ,true )->registerCb ( catchCoreChanged );
    Cfg::getParamBool("misc.verboseLevel1",true )->registerCb ( verboseLevel1Changed );
    Cfg::getParamBool("misc.verboseLevel2",true )->registerCb ( verboseLevel2Changed );
    Cfg::getParamBool("misc.info"         ,false)->registerCb ( infoChanged );
    Cfg::getParamBool("misc.logMode"      ,true )->registerCb ( logModeChanged );
    Cfg::getParamInt ("misc.traceLevel"   ,1000 )->registerCb ( traceLevelChanged );

  // Immediate update from the configuration.
    catchCoreChanged     ( Cfg::getParamBool("misc.catchCore"    ) );
    verboseLevel1Changed ( Cfg::getParamBool("misc.verboseLevel1") );
    verboseLevel2Changed ( Cfg::getParamBool("misc.verboseLevel2") );
    infoChanged          ( Cfg::getParamBool("misc.info"         ) );
    logModeChanged       ( Cfg::getParamBool("misc.logMode"      ) );
    traceLevelChanged    ( Cfg::getParamInt ("misc.traceLevel"   ) );

    bool      systemConfFound = false;
    bfs::path systemConfFile  = sysConfDir / "tools.configuration.xml";
    if ( bfs::exists(systemConfFile) ) {
      systemConfFound = true;
      conf->readFromFile ( systemConfFile.string() );
    } else {
      cerr << Warning("System configuration file:\n  <%s> not found."
                     ,systemConfFile.string().c_str()) << endl;
    }

    bool      dotConfFound = false;
    bfs::path dotConfFile  = "./.coriolis2.configuration.xml";
    if ( bfs::exists(dotConfFile) ) {
      dotConfFound = true;
      conf->readFromFile ( dotConfFile.string() );
    }

    bfs::path pythonSitePackages = PYTHON_SITE_PACKAGES;
    pythonSitePackages = arguments["coriolis_top"].as<string>() / pythonSitePackages;
    bfs::path stratusDir = pythonSitePackages / "stratus";
    bfs::path cumulusDir = pythonSitePackages / "cumulus";

    Isobar::Script::addPath ( pythonSitePackages.string() );
    Isobar::Script::addPath ( stratusDir.string() );
    Isobar::Script::addPath ( cumulusDir.string() );

  // Delayed printing, as we known only now whether VerboseLevel1 is requested.
    if ( cmess1.enabled() ) {
      cmess1 << "  o  Reading Configuration. " << endl;
      if (systemConfFound) cmess1 << "     - <" << systemConfFile.string() << ">." << endl;
      if (dotConfFound)    cmess1 << "     - <" << dotConfFile.string() << ">." << endl;
    }
  }


  System *System::get ()
  {
    if ( _singleton == NULL ) {
      _singleton = new System ();
    }

    return _singleton;
  }


  void  System::_trapSig ( int sig )
  {
    cerr << "\n\n[CRL ERROR] System::_trapSig():\n" << endl;

    switch ( sig ) {
      case SIGINT:
      // User interrupt with CTRL-C.
      //emergency ();
        break;

      case SIGTERM:
      case SIGFPE:
      case SIGBUS:
      case SIGSEGV:
      case SIGABRT:
      case SIGPIPE:
      //emergency ();

      // Ouch!! This may result from a program bug.
        cerr << "  An program internal bug have occur ";

        if (sig == SIGFPE ) cerr << "(SIGFPE).";
        if (sig == SIGBUS ) cerr << "(SIGBUS).";
        if (sig == SIGSEGV) cerr << "(SIGSEGV).";
        if (sig == SIGPIPE) cerr << "(SIGPIPE).";

        cerr << "\n  Please e-mail to <alliance-users@asim.lip6.fr>.\n"
             << "\n  program terminated ";

        if ( getCatchCore() ) {
          cerr << "(core not dumped).\n";
          exit ( 1 );
        } else {
          cerr << "(core will be dumped).\n";
          if (   ( signal(SIGFPE , SIG_DFL) == SIG_ERR )
              || ( signal(SIGBUS , SIG_DFL) == SIG_ERR )
              || ( signal(SIGABRT, SIG_DFL) == SIG_ERR )
              || ( signal(SIGSEGV, SIG_DFL) == SIG_ERR )
              || ( signal(SIGPIPE, SIG_DFL) == SIG_ERR ) )
            exit ( 1 );
          else {
            kill ( getpid(), /*sig*/ SIGSEGV );
            return;
          }
        }
        break;

      default:
      /* Unexpected signal. */
        cerr << "\n  Unexpected signal \'" << sig << "\' in trap function.\n";
        break;
    }

    exit ( 1 );
  }


  const bfs::path& System::_getPath ( const string& key )
  {
    static bfs::path nullPath ("no_path");

    cerr << "Looking up: " << key << endl;

    map<const string,bfs::path>::const_iterator ipath = _pathes.find ( key );
    if ( ipath == _pathes.end() ) return nullPath;

    cerr << "Successfull lookup: "; cerr.flush();
    cerr << (*ipath).second.string() << endl;

    return (*ipath).second;
  }


// -------------------------------------------------------------------
// Class  :  "CRL::IoFile".


  bool  IoFile::open ( const string& mode )
  {
    if ( isOpen() )
      throw Error ( "IoFile::Open():\n  Attempt to reopen file %s\n", _path.c_str() );

    _mode       = mode;
    _file       = fopen ( _path.c_str(), mode.c_str() );
    _lineNumber = 0;
    _eof        = false;

    return _file;
  }


  void  IoFile::close ()
  {
    if ( isOpen() ) fclose ( _file );
    _file       = NULL;
    _lineNumber = 0;
    _eof        = false;
  }


  char* IoFile::readLine ( char* buffer, size_t length )
  {
    assert ( buffer != NULL );

    if ( eof() ) {
      buffer[0] = '\0';
    } else {
      char* result = fgets ( buffer, length-1, _file );
      if ( !result || feof(_file) ) {
        _eof = true;
        buffer[0] = '\0';
      } else {
        _lineNumber++;
        size_t readLength = strlen ( buffer );
        if ( buffer[readLength-1] == '\n' )
          buffer[readLength-1] = '\0';
      }
    }

    return buffer;
  }


  string  IoFile::_getString () const
  {
    ostringstream s;

    s << "<IoFile " << _path << ">";

    return s.str();
  }


  Record *IoFile::_getRecord () const
  {
    Record* record = new Record ( "<IoFile>" );
    record->add ( getSlot ( "_path", &_path ) );
    return record;
  }


}  // End of CRL namespace.


// -------------------------------------------------------------------
// Class  :  "mstream".


void  mstream::enable  ( unsigned int mask ) { _activeMask |=  mask; }
void  mstream::disable ( unsigned int mask ) { _activeMask &= ~mask; }
