// @(#)root/tmva $Id$
// Author: Attila Krasznahorkay, Andreas Hoecker, Joerg Stelzer, Eckhard von Toerne

/**********************************************************************************
 * Project: TMVA - a Root-integrated toolkit for multivariate data analysis       *
 * Package: TMVA                                                                  *
 * Class  : MsgLogger                                                             *
 * Web    : http://tmva.sourceforge.net                                           *
 *                                                                                *
 * Description:                                                                   *
 *      Implementation (see header for description)                               *
 *                                                                                *
 * Author:                                                                        *
 *      Attila Krasznahorkay <Attila.Krasznahorkay@cern.ch> - CERN, Switzerland   *
 *      Andreas Hoecker       <Andreas.Hocker@cern.ch> - CERN, Switzerland        *
 *      Joerg Stelzer         <stelzer@cern.ch>        - DESY, Germany            *
 *      Eckhard v. Toerne     <evt@uni-bonn.de>        - U of Bonn, Germany       *
 *                                                                                *
 * Copyright (c) 2005-2011:                                                       *
 *      CERN, Switzerland                                                         *
 *      U. of Victoria, Canada                                                    *
 *      MPI-K Heidelberg, Germany                                                 *
 *      U. of Bonn, Germany                                                       *
 *                                                                                *
 * Redistribution and use in source and binary forms, with or without             *
 * modification, are permitted according to the terms listed in LICENSE           *
 * (http://tmva.sourceforge.net/LICENSE)                                          *
 **********************************************************************************/

// Local include(s):
#include "TMVA/MsgLogger.h"
#include "TMVA/Config.h"

#include "Riostream.h"

// STL include(s):
#include <iomanip>

#include <cstdlib>

#include <assert.h>

#include <memory>

// ROOT include(s):

ClassImp(TMVA::MsgLogger)

// declaration of global variables
// this is the hard-coded maximum length of the source names
const UInt_t                           TMVA::MsgLogger::fgMaxSourceSize = 25;

const std::string                      TMVA::MsgLogger::fgPrefix = "--- ";
const std::string                      TMVA::MsgLogger::fgSuffix = ": ";
#if __cplusplus > 199711L
std::atomic<Bool_t>                                       TMVA::MsgLogger::fgInhibitOutput{kFALSE};
std::atomic<const std::map<TMVA::EMsgType, std::string>*> TMVA::MsgLogger::fgTypeMap{0};
std::atomic<const std::map<TMVA::EMsgType, std::string>*> TMVA::MsgLogger::fgColorMap{0};
#else
Bool_t                                       TMVA::MsgLogger::fgInhibitOutput = kFALSE;
const std::map<TMVA::EMsgType, std::string>* TMVA::MsgLogger::fgTypeMap  = 0;
const std::map<TMVA::EMsgType, std::string>* TMVA::MsgLogger::fgColorMap = 0;
#endif
static std::auto_ptr<const std::map<TMVA::EMsgType, std::string> > gOwnTypeMap;
static std::auto_ptr<const std::map<TMVA::EMsgType, std::string> > gOwnColorMap;
 

void   TMVA::MsgLogger::InhibitOutput() { fgInhibitOutput = kTRUE;  }
void   TMVA::MsgLogger::EnableOutput()  { fgInhibitOutput = kFALSE; }
//_______________________________________________________________________
TMVA::MsgLogger::MsgLogger( const TObject* source, EMsgType minType )
   : fObjSource ( source ),
     fStrSource ( "" ),
     fActiveType( kINFO ),
     fMinType   ( minType )
{
   // constructor
   InitMaps();   
}

//_______________________________________________________________________
TMVA::MsgLogger::MsgLogger( const std::string& source, EMsgType minType )
   : fObjSource ( 0 ),
     fStrSource ( source ),
     fActiveType( kINFO ),
     fMinType   ( minType )
{
   // constructor
   InitMaps();
}

//_______________________________________________________________________
TMVA::MsgLogger::MsgLogger( EMsgType minType )
   : fObjSource ( 0 ),
     fStrSource ( "Unknown" ),
     fActiveType( kINFO ),
     fMinType   ( minType )
{
   // constructor
   InitMaps();
}

//_______________________________________________________________________
TMVA::MsgLogger::MsgLogger( const MsgLogger& parent )
   : std::basic_ios<MsgLogger::char_type, MsgLogger::traits_type>(),
     std::ostringstream(),
     TObject(),
     fObjSource(0)
{
   // copy constructor
   InitMaps();
   *this = parent;
}

//_______________________________________________________________________
TMVA::MsgLogger::~MsgLogger()
{
   // destructor
}

//_______________________________________________________________________
TMVA::MsgLogger& TMVA::MsgLogger::operator= ( const MsgLogger& parent )
{
   // assingment operator
   if (&parent != this) {
      fObjSource  = parent.fObjSource;
      fStrSource  = parent.fStrSource;
      fActiveType = parent.fActiveType;
      fMinType    = parent.fMinType;
   }

   return *this;
}

//_______________________________________________________________________
std::string TMVA::MsgLogger::GetFormattedSource() const
{
   // make sure the source name is no longer than fgMaxSourceSize:
   std::string source_name;
   if (fObjSource) source_name = fObjSource->GetName();
   else            source_name = fStrSource;

   if (source_name.size() > fgMaxSourceSize) {
      source_name = source_name.substr( 0, fgMaxSourceSize - 3 );
      source_name += "...";
   }

   return source_name;
}

//_______________________________________________________________________
std::string TMVA::MsgLogger::GetPrintedSource() const
{
   // the full logger prefix
   std::string source_name = GetFormattedSource();
   if (source_name.size() < fgMaxSourceSize)
      for (std::string::size_type i=source_name.size(); i<fgMaxSourceSize; i++) source_name.push_back( ' ' );

   return fgPrefix + source_name + fgSuffix;
}

//_______________________________________________________________________
void TMVA::MsgLogger::Send()
{
   // activates the logger writer

   // make sure the source name is no longer than fgMaxSourceSize:
   std::string source_name = GetFormattedSource();

   std::string message = this->str();
   std::string::size_type previous_pos = 0, current_pos = 0;

   // slice the message into lines:
   while (kTRUE) {
      current_pos = message.find( '\n', previous_pos );
      std::string line = message.substr( previous_pos, current_pos - previous_pos );

      std::ostringstream message_to_send;
      // must call the modifiers like this, otherwise g++ get's confused with the operators...
      message_to_send.setf( std::ios::adjustfield, std::ios::left );
      message_to_send.width( fgMaxSourceSize );
      message_to_send << source_name << fgSuffix << line;
      this->WriteMsg( fActiveType, message_to_send.str() );

      if (current_pos == message.npos) break;
      previous_pos = current_pos + 1;
   }

   // reset the stream buffer:
   this->str( "" );
   fActiveType = kINFO; // To always print messages that have no level specified...
   return;
}

//_______________________________________________________________________
void TMVA::MsgLogger::WriteMsg( EMsgType type, const std::string& line ) const
{
   // putting the output string, the message type, and the color
   // switcher together into a single string

   if ( (type < fMinType || fgInhibitOutput) && type!=kFATAL ) return; // no output

   std::map<EMsgType, std::string>::const_iterator stype;

   if ((stype = fgTypeMap.load()->find( type )) != fgTypeMap.load()->end()) {
      if (!gConfig().IsSilent() || type==kFATAL) {
         if (gConfig().UseColor()) {
            // no text for INFO or VERBOSE
            if (type == kINFO || type == kVERBOSE)
               std::cout << fgPrefix << line << std::endl; // no color for info
            else
 	       std::cout << fgColorMap.load()->find( type )->second << fgPrefix << "<"
                         << stype->second << "> " << line  << "\033[0m" << std::endl;
         }
         else {
            if (type == kINFO) std::cout << fgPrefix << line << std::endl;
            else               std::cout << fgPrefix << "<" << stype->second << "> " << line << std::endl;
         }
      }
   }

   // take decision to stop if fatal error
   if (type == kFATAL) {
      std::cout << "***> abort program execution" << std::endl;
      std::exit(1);
      assert(false);
   }
}

//_______________________________________________________________________
TMVA::MsgLogger& TMVA::MsgLogger::Endmsg( MsgLogger& logger )
{
   // end line
   logger.Send();
   return logger;
}

//_______________________________________________________________________
void TMVA::MsgLogger::InitMaps()
{
   // Create the message type and color maps

   if(!fgTypeMap) {
     std::map<TMVA::EMsgType, std::string>*tmp  = new std::map<TMVA::EMsgType, std::string>();
   
     (*tmp)[kVERBOSE]  = std::string("VERBOSE");
     (*tmp)[kDEBUG]    = std::string("DEBUG");
     (*tmp)[kINFO]     = std::string("INFO");
     (*tmp)[kWARNING]  = std::string("WARNING");
     (*tmp)[kERROR]    = std::string("ERROR");
     (*tmp)[kFATAL]    = std::string("FATAL");
     (*tmp)[kSILENT]   = std::string("SILENT");
     const std::map<TMVA::EMsgType, std::string>* expected=0;
     if(fgTypeMap.compare_exchange_strong(expected,tmp)) {
       //Have the global own this
       gOwnTypeMap.reset(tmp);
     } else {
       //Another thread beat us in creating the instance
       delete tmp;
     }
   }

   if(!fgColorMap) {
     std::map<TMVA::EMsgType, std::string>*tmp  = new std::map<TMVA::EMsgType, std::string>();

     (*tmp)[kVERBOSE] = std::string("");
     (*tmp)[kDEBUG]   = std::string("\033[34m");
     (*tmp)[kINFO]    = std::string("");
     (*tmp)[kWARNING] = std::string("\033[1;31m");
     (*tmp)[kERROR]   = std::string("\033[31m");
     (*tmp)[kFATAL]   = std::string("\033[37;41;1m");
     (*tmp)[kSILENT]  = std::string("\033[30m");

     const std::map<TMVA::EMsgType, std::string>* expected=0;
     if(fgColorMap.compare_exchange_strong(expected,tmp)) {
       //Have the global own this
       gOwnColorMap.reset(tmp);
     } else {
       //Another thread beat us in creating the instance
       delete tmp;
     }
   }
}
 MsgLogger.cxx:1
 MsgLogger.cxx:2
 MsgLogger.cxx:3
 MsgLogger.cxx:4
 MsgLogger.cxx:5
 MsgLogger.cxx:6
 MsgLogger.cxx:7
 MsgLogger.cxx:8
 MsgLogger.cxx:9
 MsgLogger.cxx:10
 MsgLogger.cxx:11
 MsgLogger.cxx:12
 MsgLogger.cxx:13
 MsgLogger.cxx:14
 MsgLogger.cxx:15
 MsgLogger.cxx:16
 MsgLogger.cxx:17
 MsgLogger.cxx:18
 MsgLogger.cxx:19
 MsgLogger.cxx:20
 MsgLogger.cxx:21
 MsgLogger.cxx:22
 MsgLogger.cxx:23
 MsgLogger.cxx:24
 MsgLogger.cxx:25
 MsgLogger.cxx:26
 MsgLogger.cxx:27
 MsgLogger.cxx:28
 MsgLogger.cxx:29
 MsgLogger.cxx:30
 MsgLogger.cxx:31
 MsgLogger.cxx:32
 MsgLogger.cxx:33
 MsgLogger.cxx:34
 MsgLogger.cxx:35
 MsgLogger.cxx:36
 MsgLogger.cxx:37
 MsgLogger.cxx:38
 MsgLogger.cxx:39
 MsgLogger.cxx:40
 MsgLogger.cxx:41
 MsgLogger.cxx:42
 MsgLogger.cxx:43
 MsgLogger.cxx:44
 MsgLogger.cxx:45
 MsgLogger.cxx:46
 MsgLogger.cxx:47
 MsgLogger.cxx:48
 MsgLogger.cxx:49
 MsgLogger.cxx:50
 MsgLogger.cxx:51
 MsgLogger.cxx:52
 MsgLogger.cxx:53
 MsgLogger.cxx:54
 MsgLogger.cxx:55
 MsgLogger.cxx:56
 MsgLogger.cxx:57
 MsgLogger.cxx:58
 MsgLogger.cxx:59
 MsgLogger.cxx:60
 MsgLogger.cxx:61
 MsgLogger.cxx:62
 MsgLogger.cxx:63
 MsgLogger.cxx:64
 MsgLogger.cxx:65
 MsgLogger.cxx:66
 MsgLogger.cxx:67
 MsgLogger.cxx:68
 MsgLogger.cxx:69
 MsgLogger.cxx:70
 MsgLogger.cxx:71
 MsgLogger.cxx:72
 MsgLogger.cxx:73
 MsgLogger.cxx:74
 MsgLogger.cxx:75
 MsgLogger.cxx:76
 MsgLogger.cxx:77
 MsgLogger.cxx:78
 MsgLogger.cxx:79
 MsgLogger.cxx:80
 MsgLogger.cxx:81
 MsgLogger.cxx:82
 MsgLogger.cxx:83
 MsgLogger.cxx:84
 MsgLogger.cxx:85
 MsgLogger.cxx:86
 MsgLogger.cxx:87
 MsgLogger.cxx:88
 MsgLogger.cxx:89
 MsgLogger.cxx:90
 MsgLogger.cxx:91
 MsgLogger.cxx:92
 MsgLogger.cxx:93
 MsgLogger.cxx:94
 MsgLogger.cxx:95
 MsgLogger.cxx:96
 MsgLogger.cxx:97
 MsgLogger.cxx:98
 MsgLogger.cxx:99
 MsgLogger.cxx:100
 MsgLogger.cxx:101
 MsgLogger.cxx:102
 MsgLogger.cxx:103
 MsgLogger.cxx:104
 MsgLogger.cxx:105
 MsgLogger.cxx:106
 MsgLogger.cxx:107
 MsgLogger.cxx:108
 MsgLogger.cxx:109
 MsgLogger.cxx:110
 MsgLogger.cxx:111
 MsgLogger.cxx:112
 MsgLogger.cxx:113
 MsgLogger.cxx:114
 MsgLogger.cxx:115
 MsgLogger.cxx:116
 MsgLogger.cxx:117
 MsgLogger.cxx:118
 MsgLogger.cxx:119
 MsgLogger.cxx:120
 MsgLogger.cxx:121
 MsgLogger.cxx:122
 MsgLogger.cxx:123
 MsgLogger.cxx:124
 MsgLogger.cxx:125
 MsgLogger.cxx:126
 MsgLogger.cxx:127
 MsgLogger.cxx:128
 MsgLogger.cxx:129
 MsgLogger.cxx:130
 MsgLogger.cxx:131
 MsgLogger.cxx:132
 MsgLogger.cxx:133
 MsgLogger.cxx:134
 MsgLogger.cxx:135
 MsgLogger.cxx:136
 MsgLogger.cxx:137
 MsgLogger.cxx:138
 MsgLogger.cxx:139
 MsgLogger.cxx:140
 MsgLogger.cxx:141
 MsgLogger.cxx:142
 MsgLogger.cxx:143
 MsgLogger.cxx:144
 MsgLogger.cxx:145
 MsgLogger.cxx:146
 MsgLogger.cxx:147
 MsgLogger.cxx:148
 MsgLogger.cxx:149
 MsgLogger.cxx:150
 MsgLogger.cxx:151
 MsgLogger.cxx:152
 MsgLogger.cxx:153
 MsgLogger.cxx:154
 MsgLogger.cxx:155
 MsgLogger.cxx:156
 MsgLogger.cxx:157
 MsgLogger.cxx:158
 MsgLogger.cxx:159
 MsgLogger.cxx:160
 MsgLogger.cxx:161
 MsgLogger.cxx:162
 MsgLogger.cxx:163
 MsgLogger.cxx:164
 MsgLogger.cxx:165
 MsgLogger.cxx:166
 MsgLogger.cxx:167
 MsgLogger.cxx:168
 MsgLogger.cxx:169
 MsgLogger.cxx:170
 MsgLogger.cxx:171
 MsgLogger.cxx:172
 MsgLogger.cxx:173
 MsgLogger.cxx:174
 MsgLogger.cxx:175
 MsgLogger.cxx:176
 MsgLogger.cxx:177
 MsgLogger.cxx:178
 MsgLogger.cxx:179
 MsgLogger.cxx:180
 MsgLogger.cxx:181
 MsgLogger.cxx:182
 MsgLogger.cxx:183
 MsgLogger.cxx:184
 MsgLogger.cxx:185
 MsgLogger.cxx:186
 MsgLogger.cxx:187
 MsgLogger.cxx:188
 MsgLogger.cxx:189
 MsgLogger.cxx:190
 MsgLogger.cxx:191
 MsgLogger.cxx:192
 MsgLogger.cxx:193
 MsgLogger.cxx:194
 MsgLogger.cxx:195
 MsgLogger.cxx:196
 MsgLogger.cxx:197
 MsgLogger.cxx:198
 MsgLogger.cxx:199
 MsgLogger.cxx:200
 MsgLogger.cxx:201
 MsgLogger.cxx:202
 MsgLogger.cxx:203
 MsgLogger.cxx:204
 MsgLogger.cxx:205
 MsgLogger.cxx:206
 MsgLogger.cxx:207
 MsgLogger.cxx:208
 MsgLogger.cxx:209
 MsgLogger.cxx:210
 MsgLogger.cxx:211
 MsgLogger.cxx:212
 MsgLogger.cxx:213
 MsgLogger.cxx:214
 MsgLogger.cxx:215
 MsgLogger.cxx:216
 MsgLogger.cxx:217
 MsgLogger.cxx:218
 MsgLogger.cxx:219
 MsgLogger.cxx:220
 MsgLogger.cxx:221
 MsgLogger.cxx:222
 MsgLogger.cxx:223
 MsgLogger.cxx:224
 MsgLogger.cxx:225
 MsgLogger.cxx:226
 MsgLogger.cxx:227
 MsgLogger.cxx:228
 MsgLogger.cxx:229
 MsgLogger.cxx:230
 MsgLogger.cxx:231
 MsgLogger.cxx:232
 MsgLogger.cxx:233
 MsgLogger.cxx:234
 MsgLogger.cxx:235
 MsgLogger.cxx:236
 MsgLogger.cxx:237
 MsgLogger.cxx:238
 MsgLogger.cxx:239
 MsgLogger.cxx:240
 MsgLogger.cxx:241
 MsgLogger.cxx:242
 MsgLogger.cxx:243
 MsgLogger.cxx:244
 MsgLogger.cxx:245
 MsgLogger.cxx:246
 MsgLogger.cxx:247
 MsgLogger.cxx:248
 MsgLogger.cxx:249
 MsgLogger.cxx:250
 MsgLogger.cxx:251
 MsgLogger.cxx:252
 MsgLogger.cxx:253
 MsgLogger.cxx:254
 MsgLogger.cxx:255
 MsgLogger.cxx:256
 MsgLogger.cxx:257
 MsgLogger.cxx:258
 MsgLogger.cxx:259
 MsgLogger.cxx:260
 MsgLogger.cxx:261
 MsgLogger.cxx:262
 MsgLogger.cxx:263
 MsgLogger.cxx:264
 MsgLogger.cxx:265
 MsgLogger.cxx:266
 MsgLogger.cxx:267
 MsgLogger.cxx:268
 MsgLogger.cxx:269
 MsgLogger.cxx:270
 MsgLogger.cxx:271
 MsgLogger.cxx:272
 MsgLogger.cxx:273
 MsgLogger.cxx:274
 MsgLogger.cxx:275
 MsgLogger.cxx:276
 MsgLogger.cxx:277
 MsgLogger.cxx:278
 MsgLogger.cxx:279
 MsgLogger.cxx:280
 MsgLogger.cxx:281
 MsgLogger.cxx:282
 MsgLogger.cxx:283