#ifndef ERRLOG_HH
#define ERRLOG_HH

//--------------------------------------------------------------------------
// Description:
//	Class ErrLogger:
//		Interface description for the abstract logging interface
//
// BaBar Usage:
//
//	BaBar policy for using ErrLogger is to interact with it only
//	through the ErrMsg macro.
//
//	ErrMsg(severity) << "the message" << endmsg;
//
//	The error message must be terminated by endmsg as above.
//
//	The ErrMsg macro returns an ostream& so that it is possible
//	to include any constructs that can be sent to cout or cerr.
//	For example, the string classes and user-defined operator<<
//	are available to users while using ErrMsg.
//
// General Usage:
//
//	Available constructs are a logging call:
//
//		ErrLog::msg(severity, facility, code) << "Foo" << endmsg;
//
//	and a test to see if a particular logging call will do anything:
//
//		if (ErrLog::logging(severity, facility, code)) ...
//
//	All error messages should be completed with "endmsg".  For multiple-
//	line messages use '\n' for line breaks in preference to endl.  The 
//      endmsg command will supply a final "endl" for error loggers for which
//      it's appropriate (e.g., if writing to the console).  Users should in 
//      particular NOT terminate messages with "some text" << endl << endmsg;.
//
//
//	"severity" is a member of an enum; see the definition below
//	"facility" names the source of the message; in the ErrMsg() form,
//                 this is set to be the filename and line number
//	"code"     an integer distinguishing the individual errors possible;
//	           no meanings are predefined at this level
//
//
// Environment:
//	Software developed for the BaBar Detector at the SLAC B-Factory.
//
// Author List:
//      Bob Jacobsen	(original author)
//	Scott Metzler	(addition of ErrStream)
//	Gregory Dubois-Felsmann   (ErrOpt; conversion of "code" to int)
//      $Id: ErrLog.hh,v 1.1.1.1 2005/03/29 17:04:19 steinke Exp $
//
// Copyright Information:
//	Copyright (C) 1998  Lawrence Berkeley National Laboratory
//	Copyright (C) 1998-2004  California Institute of Technology
//
//------------------------------------------------------------------------

//-----------------
// C/C++ Headers --
//-----------------
#include <assert.h>
#include <iostream>

//------------------------------------
// Collaborating Class Declarations --
//------------------------------------
class ErrStream;
class ErrOptParser;
template <class T> class ErrOptRef;


// Use the ErrMsg and ErrLogging macros for interacting with the error logger
// in a syntactically economical way.

// The next two macros allow the substitution of the value of the 
// __LINE__ macro as a quoted character string instead of an integer.
// As odd as this may seem, it's a well-established ISO C trick.

// 1) replaced by its argument, treated as a quoted string, without any
//    nested expansions applied (they are suppressed by the "#" operator).
#define ERRLINE_HACK_1(line)   #line

// 2) allows its argument (which will be "__LINE__") to have macro expansion
//    performed on it.  Hence HACK_2(__LINE__) becomes, e.g., HACK_1(76).
#define ERRLINE_HACK_2(line)   ERRLINE_HACK_1(line)


#ifdef ErrMsg
#undef ErrMsg
#endif
#define ErrMsg(sev) ErrLog::msg( ErrLog::sev, __FILE__ "(" ERRLINE_HACK_2(__LINE__) ")", 0 )

#ifdef ErrLogging
#undef ErrLogging
#endif
#define ErrLogging(sev) ErrLog::logging( ErrLog::sev, __FILE__ "(" ERRLINE_HACK_2(__LINE__) ")", 0 )

// [June 2002 note]  The old versions of these macros expanded to 
// ( ErrLog::sev, __FILE__, __LINE__ ) and so invoked the (..., int) forms
// of msg() and logging().  But at run time these forms were immediately
// vectored, by converting the code to a string, into the (..., const char*)
// forms, in which then in practice in all relevant ErrLog implementations the
// "code" string, containing the line number, was appended to the facility.
//
// The new forms of these macros preserve this behavior -- the line number
// is concatenated to the file name -- on the theory that the line number
// makes a silly code number and that the file+line combination is 
// reasonable to treat as a unit.


// The actual class

class ErrLog {
public:

  /**
   *  Message severity levels.
   */
  enum Severity { debugging=-1, 
		  trace=0, 
		  routine, 
		  warning, 
		  error,
		  severe,
		  fatal, 
		  MinSeverity=debugging,
		  MaxSeverity=fatal };

  //	fatal:		A condition exists that casts doubt on the integrity
  //                    of processing and the reliability of any results.
  //                    No recovery strategy can be attempted, because the
  //                    state of the program may be indeterminate.
  //
  //                    It is intended that "fatal" errors be reported
  //                    primarily when it is likely that the condition was
  //                    caused by incorrect code, or when the root cause cannot
  //                    be determined.  Dealing with the error is likely to
  //                    require debugging or other expert investigation.
  // 
  //                    In most implementations, this will be configured to
  //                    result in application termination with ::abort() 
  //                    immediately after the error message is completed with
  //                    "endmsg", producing a core dump usable to obtain a
  //                    traceback.
  //
  //			Programmers should generally not call ::abort() or
  //                    ::exit() themselves following the message.

  //    severe:         A condition exists such that the general ability of
  //                    the program to continue work is compromised: the
  //                    present computation and most or all subsequent ones
  //                    are likely to fail or produce erroneous results.
  //
  //                    It is intended that "severe" errors be reported 
  //                    primarily when external conditions (command line
  //                    arguments, control files, data files, the contents of
  //                    databases) are identified to be incompatible with
  //                    proceeding.  The programmer should ensure that the
  //                    message written is as informative as possible.
  //
  //                    In most implementations, this will be configured to
  //                    result in application termination immediately after
  //                    the error message is completed with "endmsg" and
  //                    delivered, with a non-zero exit status.  The ErrLog
  //                    implementation is free to determine the exit status
  //                    to use, from the range 1-126.  It may take the "code"
  //                    value from the ErrLog::msg( , , code ) into account.
  //
  //			Programmers should generally not call ::abort() or
  //                    ::exit() themselves following the message.

  //	error:		A condition exists such that requested result or
  //			action can not be produced.  However, future 
  //                    processing may be able to succeed.

  //	warning:	The result is produced, but may not be
  //			what's desired due to an unexpected condition

  //	routine:	Nothing known to be wrong with the result;
  //			messages that are always produced in normal
  //			operation

  //	trace:		Messages about the flow of program control
  //			and which optional operations took place.
  //			(This is the default if nothing is defined)
  
  //	debugging:	Information in addition to the above


  /**
   *  This constant is the exit code to be used when a message severity
   *  is such that an exit(!=0) -- as opposed to an abort() -- is merited,
   *  but no message-specific exit code can be determined from the 
   *  message parameters.
   */
  enum { DefaultExitCode=125 };

//-----------------------------------------------------------------------
// Static public member functions ---------------------------------------
//-----------------------------------------------------------------------

public:

  // These member functions are the public interface to error logging.

  /**
   *  Returns a stream to which to write a message, with the parameters
   *  given.  The message must be terminated later by performing the
   *  "<< endmsg" operation on the resulting stream.  Actual output may
   *  not occur until the endmsg is executed.  The meaning of the "facility"
   *  and "code" parameters is not defined by the ErrLog base class, nor is
   *  is guaranteed that their values will be reflected in any output
   *  generated for the message.
   *
   *  The "facility" pointer must be guaranteed by the caller to remain
   *  valid until the message is completed with "endmsg".  (I.e., this
   *  function is not guaranteed to make a copy of the string.)
   */
  static std::ostream& msg( ErrLog::Severity severity, 
		       const char* facility, 
		       int code );

  /** 
   *  DEPRECATED
   *  Backward-compatibility function for an old API.
   *  Calls through to msg( severity, facilty + code, 0 )
   *  Do not use in new code.
   */
  static std::ostream& msg( ErrLog::Severity severity, 
			    const char* facility, 
			    const char* code );

  /**
   *  Tests whether actual logging output is enabled for a specific set
   *  of message parameters.  This function should be used before sending
   *  complex output to the result of ErrMsg() or ErrLog::msg(), to avoid
   *  the cost of executing the stream insertion operations (binary-to-
   *  string conversions) for the message, only to discard their output.
   */
  inline
  static bool logging( ErrLog::Severity severity, 
		       const char* facility, 
		       int code );

  /** 
   *  DEPRECATED
   *  Backward-compatibility function for an old API.
   *  Calls through to logging( severity, facilty + code, 0 )
   *  Do not use in new code.
   */
  static bool logging( ErrLog::Severity severity, 
		       const char* facility, 
		       const char* code );


  // The following are options controlling the global configuration of
  // the static "front end" to error logging.

  /**
   *  Disables the automatic warning message generated when a new ErrLog::msg()
   *  call is made before the previous one has been terminated with "endmsg".
   *  The use of this option is *strongly* discouraged; the correct response
   *  to such warnings is to fix the nested messages.
   */
  static void turnOffWarnings();


  // The following are utility functions that are probably of interest only
  // implementations (i.e., could be protected) but expose no internals and
  // are harmless, and maybe useful, to make public.

  /**
   *  Returns a static string corresponding to the enum member name of a
   *  severity level.  I.e., severityName(ErrLog::error) == "error".
   */
  static const char* severityName( ErrLog::Severity );

  /**
   *  Returns a single uppercase letter corresponding to the enum member name
   *  of a severity level.  I.e., severityName(ErrLog::error) == 'E'.
   */
  static char        severityLetter( ErrLog::Severity );



//-----------------------------------------------------------------------
// Instance public member functions -------------------------------------
//-----------------------------------------------------------------------

public:

  /**
   *  Provides parsing of a comma-delimited list of error logger
   *  behavior control options, in getsubopt() format.  The effects of
   *  the parsing depend on the specific ErrLog implementation and are
   *  established by it at constructor time using the _errOptParser()
   *  and _configureOptions() methods.
   */ 
  bool parseOpts( const char* optarg );


//-----------------------------------------------------------------------
// Friendship declarations ----------------------------------------------
//-----------------------------------------------------------------------

  friend class ErrStream;


//-----------------------------------------------------------------------
// Instance protected member functions ----------------------------------
//-----------------------------------------------------------------------

protected:

  // These members provide a basic set of functions for dealing with a
  // specific instance of ErrLog or a subclass.  They are really intended
  // only for use in configuring the active instance just after it's been
  // created.

  /**
   *  Protected constructor, for use in initializing subclasses.
   *
   *  @param minSeverity Minimum severity of messages that will be logged
   *                     by the default implementation.
   */
  ErrLog( ErrLog::Severity minSeverity = ErrLog::debugging );

  /**
   *  A virtual destructor is provided, but note that the quasi-singleton
   *  design of this class cannot guarantee that the active ErrLog instance
   *  will be deleted at application termination.
   */
  virtual ~ErrLog();

  // Functions used with ErrOptParser.

  virtual bool _configureOptions();
  ErrOptParser& _errOptParser() { return *_itsErrOptParser; }


//-----------------------------------------------------------------------
// Instance protected virtual member functions --------------------------
//-----------------------------------------------------------------------

protected:

  // These members provide the polymorphic interface for customizing the
  // behavior of an ErrLogger implementation.

  /** 
   *  Establishes an std::ostream that is to be used for logging message text,
   *  to be terminated with "endmsg".  Pure virtual.
   */
  virtual ErrStream& doMsg( ErrLog::Severity severity, 
			    const char* facility, 
			    int code ) = 0;

  /**
   *  Callback from ErrStream when a message has been completed, allowing
   *  the logger to decide what to do.
   *
   *  @param text   The full formatted message text, if the ErrStream is a
   *                "buffering" stream, or 0 (null pointer) if it is a
   *                "wrapper" stream, with output actually appearing as 
   *                each stream-output (<<) operation happens.
   *  @param stream The ErrStream to which the message had been directed.
   *                It's provided so that end-of-message output (std::endl or
   *                something more) can be written if appropriate.
   */
  virtual void doEndmsg( const char* text, ErrStream& stream ) = 0;

  /**
   *  Tests whether a message will actually be logged for a particular
   *  set of message parameters.  A default implementation is provided,
   *  based only on a test of the severity w.r.t. a minimum level, a
   *  static member of the ErrLog base class.
   *
   *  The sort of thing a subclass might do here is apply a "verbosity"
   *  control in addition to the severity test, perhaps suppressing
   *  repeated messages with the same parameters.
   */
  virtual bool doLogging( ErrLog::Severity severity, 
			  const char* facility, 
			  int code );


//-----------------------------------------------------------------------
// Static protected member functions ------------------------------------
//-----------------------------------------------------------------------

protected:

  // These members provide tools for ErrLog implementations to use.

  /** 
   *  ErrLog is a form of singleton.  Polymorphism of its static methods is
   *  arranged by calling implementation instances through this pointer.
   */
  inline static ErrLog* _implementation() { return _implInstance; }

  /** 
   *  ErrLog is a form of singleton.  The constructor of any subclass must
   *  register itself as the active instance by calling this function.
   */
  inline static void _setImplementation( ErrLog* implementation );

  /**
   *  Get the appropriate default stream for a given severity level.
   *  The facility and code arguments are not used.
   */
  static ErrStream* _getDefaultStream( ErrLog::Severity, const char*, int );

  /**
   *  Apply the logging test for a given severity level.
   *  The facility and code arguments are not used.
   */
  inline static bool _getDefaultLogging( ErrLog::Severity, const char*, int );

  /**
   *  Write a set of message parameters through into an ErrStream.
   *  This function exists so that subclasses of ErrLog can benefit
   *  from its friendship relationship with ErrStream.
   */
  static void _setParameters( ErrStream&, 
			      ErrLog::Severity, const char*, int );

  /**
   *  Evaluated and executes the default termination strategy for errors:
   *  abort() on fatal, exit(!=0) on severe, with the exit code derived from
   *  the message's "code" value.
   */
  static void _defaultTermination( ErrLog::Severity severity, 
				   int code );

  /**
   *  Tests the anticipated behavior of the default termination strategy.
   *  Available for use by implementations that need to know whether a
   *  termination will result before deciding on the disposition of a
   *  message.
   *
   *  @return 0 if termination will not occur, non-zero if it will.
   *          Negative if by signal (SIGABRT), positive if by exit(!=0).
   *
   *  @see QtrErrLog
   */
  static int _getDefaultTermination( ErrLog::Severity severity, 
				     int code );

//-----------------------------------------------------------------------
// Static private member functions --------------------------------------
//-----------------------------------------------------------------------

private:

  // These functions provide implementation details.

  static void _executeDefaultTermination( int value );


//-----------------------------------------------------------------------
// Static protected member data -----------------------------------------
//-----------------------------------------------------------------------

protected:

  // data members for the static front end and default implementation
  // These appear to be used in implementations.  A future cleanup pass
  // may be able to convert them all to private.
  static ErrStream* _myStreamCout;
  static ErrStream* _myStreamCerr;
  static ErrStream* _myDevnull;
  static Severity _minSeverity;


//-----------------------------------------------------------------------
// Static private member data -------------------------------------------
//-----------------------------------------------------------------------

private:

  // Singleton polymorphic implementation instance.
  static ErrLog* _implInstance;

  // data members for the static front end and default implementation
  static bool  _nestedWarnings;

private:

  bool _useStdio;

  // Contained by reference to avoid introducing header file dependencies:
  ErrOptRef<bool>* _useStdioOpt;
  ErrOptParser* _itsErrOptParser;

  // dis-allow copy construction and assignment
  ErrLog( const ErrLog& );
  ErrLog& operator=( const ErrLog& );

};


// Friends of ErrStream that are needed in implementing the "endmsg" feature.

void endmsg(ErrStream& es); // declare
std::ostream& operator<<(std::ostream& os, void (*fp)(ErrStream & es) );


// --------------------------------------------------------------------------
// Inline implementations ---------------------------------------------------
// --------------------------------------------------------------------------

inline bool 
ErrLog::logging( ErrLog::Severity severity, 
		 const char* facility, 
		 int code )
{
  return ( _implementation() != 0
	   ? _implementation()->doLogging( severity, facility, code )
	   : ErrLog::_getDefaultLogging( severity, facility, code )   );
}


inline void
ErrLog::_setImplementation( ErrLog* implementation )
{
  // "There can only be one" (or zero -- we can reset to the default).
  assert( implementation == 0 || _implementation() == 0 );

  // If the assertion is compiled away, this call is simply ignored.
  if ( implementation == 0 || _implementation() == 0 ) {
    _implInstance = implementation;
  }
}


inline bool 
ErrLog::_getDefaultLogging( ErrLog::Severity severity,
			    const char*,    // unused
			    int )           // unused
{
  return ( severity >= _minSeverity );
}

#endif // ERRLOG_HH