/* -*- C++ -*-
 *
 * src/lemon/error.h - Part of LEMON, a generic C++ optimization library
 *
 * Copyright (C) 2005 Egervary Jeno Kombinatorikus Optimalizalasi
 * Kutatocsoport (Egervary Combinatorial Optimization Research Group,
 * EGRES).
 *
 * Permission to use, modify and distribute this software is granted
 * provided that this copyright notice appears in all copies. For
 * precise terms see the accompanying LICENSE file.
 *
 * This software is provided "AS IS" with no warranty of any kind,
 * express or implied, and with no claim as to its suitability for any
 * purpose.
 *
 */

#ifndef LEMON_ERROR_H
#define LEMON_ERROR_H

//! \ingroup exceptions
//! \file
//! \brief Basic exception classes and error handling.

#include <exception>
#include <string>
#include <sstream>
#include <iostream>
#include <cstdlib>
#include <memory>

namespace lemon {

/// \addtogroup exceptions
/// @{

  /// Exception-safe convenient "error message" class.

  /// Helper class which provides a convenient ostream-like (operator <<
  /// based) interface to create a string message. Mostly useful in
  /// exception classes (therefore the name).
  class ErrorMessage {
  protected:
    ///\e 
    ///\todo The good solution is boost:shared_ptr...
    mutable
    std::auto_ptr<std::ostringstream> buf;
    
    ///\e 
    bool init() throw() {
      try {
	buf.reset(new std::ostringstream);
      }
      catch(...) {
	buf.reset();
      }
      return buf.get();
    }

  public:

    ///\e 
    ErrorMessage() throw() { init(); }

    ErrorMessage(const ErrorMessage& em) throw() : buf(em.buf) { }

    ///\e 
    ErrorMessage(const char *message) throw() {
      init();
      *this << message;
    }

    ///\e 
    ErrorMessage(const std::string &message) throw() {
      init();
      *this << message;
    }

    ///\e 
    template <typename T>
    ErrorMessage& operator<<(const T &t) throw() {
      if( ! buf.get() ) return *this;

      try {
	*buf << t;
      }
      catch(...) {
	buf.reset();
      }
    }

    ///\e 
    const char* message() throw() {
      if( ! buf.get() ) return 0;

      const char* mes = 0;
      try {
	mes = buf->str().c_str();
      }
      catch(...) {}
      return mes;
    }
    
  };

  /**
   * \brief Generic exception class.
   *
   * Base class for exceptions used in LEMON.
   */
  class Exception : public std::exception {
  public:
    ///\e 
    Exception() {}
    ///\e 
    virtual ~Exception() throw() {}

    ///\e
    virtual const char* exceptionName() const {
      return "lemon::Exception";
    }
    
    ///\e 
    virtual const char* what() const throw() {
      return exceptionName();
    }
  };

  /**
   * \brief One of the two main subclasses of \ref Exception.
   *
   * Logic errors represent problems in the internal logic of a program;
   * in theory, these are preventable, and even detectable before the
   * program runs (e.g., violations of class invariants).
   *
   * A typical example for this is \ref UninitializedParameter.
   */
  class LogicError : public Exception {
  public:
    virtual const char* exceptionName() const {
      return "lemon::LogicError";
    }
  };

  /**
   * \brief \ref Exception for uninitialized parameters.
   *
   * This error represents problems in the initialization
   * of the parameters of the algorithms.
   */
  class UninitializedParameter : public LogicError {
  public:
    virtual const char* exceptionName() const {
      return "lemon::UninitializedParameter";
    }
  };

  
  /**
   * \brief One of the two main subclasses of \ref Exception.
   *
   * Runtime errors represent problems outside the scope of a program;
   * they cannot be easily predicted and can generally only be caught as
   * the program executes.
   */
  class RuntimeError : public Exception {
  public:
    virtual const char* exceptionName() const {
      return "lemon::RuntimeError";
    }
  };

  ///\e
  class RangeError : public RuntimeError {
  public:
    virtual const char* exceptionName() const {
      return "lemon::RangeError";
    }
  };

  ///\e 
  class IOError : public RuntimeError {
  public:
    virtual const char* exceptionName() const {
      return "lemon::IOError";
    }
  };

  ///\e 
  class DataFormatError : public IOError {
  protected:
    const char *_message;
    int _line;

    ///\todo Much better solution is boost::shared_ptr
    mutable
    std::auto_ptr<std::string> _file;

  public:

    DataFormatError(const DataFormatError &dfe) : 
      IOError(dfe), _message(dfe._message), _line(dfe._line),
      _file(dfe._file) {}

    ///\e 
    explicit DataFormatError(const char *the_message)
      : _message(the_message), _line(0) {}
    ///\e 
    DataFormatError(const std::string &file_name, int line_num,
		    const char *the_message)
      : _message(the_message), _line(line_num) { file(file_name); }

    ///\e 
    void line(int line_num) { _line=line_num; }
    ///\e 
    void message(char *the_message) { _message=the_message; }
    ///\e 
    void file(const std::string &file_name) {
      try {
	_file.reset(new std::string);
	*_file = file_name;
      }
      catch(...) {
	_file.reset();
      }
    }

    ///\e
    int line() const { return _line; }
    ///\e
    const char* message() const { return _message; }

    /// \brief Returns the filename.
    ///
    /// Returns \e "(unknown)" if the filename was not specified.
    const char* file() const {
      if( _file.get() )
	return _file->c_str();
      else
	return "(unknown)";
    }

    ///\e 
    virtual const char* what() const throw() {
      const char *mes = 0;
      try {
	std::ostringstream ostr;
	ostr << _message;
	if( _file.get() || _line ) {
	  ostr << " (";
	  if( _file.get() ) ostr << "in file '" << *_file << "'";
	  if( _file.get() && _line ) ostr << " ";
	  if( _line ) ostr << "at line " << _line;
	  ostr << ")";
	}
	mes = ostr.str().c_str();
      }
      catch(...) {}
      if( mes ) return mes;
      return exceptionName();
    }

    virtual const char* exceptionName() const {
      return "lemon::DataFormatError";
    }

    virtual ~DataFormatError() throw() {}
  };


  ///\e
  class AssertionFailedError : public LogicError {
  protected:
    const char *assertion;
    const char *file;
    int line;
    const char *function;
    const char *message;
  public:
    ///\e
    AssertionFailedError(const char *_file, int _line, const char *func,
			 const char *msg, const char *_assertion = 0) :
      assertion(_assertion), file(_file), line(_line), function(func),
      message(msg) {}

    ///\e
    const char* get_assertion() const { return assertion; }
    ///\e
    const char* get_message() const { return message; }
    ///\e
    const char* get_file() const { return file; }
    ///\e
    const char* get_function() const { return function; }
    ///\e
    int get_line() const { return line; }


    virtual const char* what() const throw() {
      const char *mes = 0;
      try {
	std::ostringstream ostr;
	ostr << file << ":" << line << ": ";
	if( function )
	  ostr << function << ": ";
	ostr << message;
	if( assertion )
	  ostr << " (assertion '" << assertion << "' failed)";
	mes = ostr.str().c_str();
      }
      catch(...) {}
      if( mes ) return mes;
      return exceptionName();
    }

    virtual const char* exceptionName() const {
      return "lemon::AssertionFailedError";
    }

    virtual ~AssertionFailedError() throw() {}
  };


  /****************  Macros  ****************/


  inline
  void assert_fail(const char *file, int line, const char *func,
		   const char *message, const char *assertion = 0,
		   bool do_abort=true)
  {
    using namespace std;
    cerr << file << ":" << line << ": ";
    if( func )
      cerr << func << ": ";
    cerr << message;
    if( assertion )
      cerr << " (assertion '" << assertion << "' failed)";
    cerr << endl;
    if(do_abort)
      abort();
  }

  inline
  void assert_fail_throw(const char *file, int line, const char *func,
		   const char *message, const char *assertion = 0,
		   bool = true)
  {
    throw AssertionFailedError(file, line, func, message, assertion);
  }

/// @}

}
#endif // LEMON_ERROR_H

#undef LEMON_ASSERT
#undef LEMON_FIXME

#ifndef LEMON_ASSERT_ABORT
#  define LEMON_ASSERT_ABORT 1
#endif

#ifndef LEMON_ASSERT_HANDLER
#  ifdef LEMON_ASSERT_EXCEPTION
#    define LEMON_ASSERT_HANDLER ::lemon::assert_fail_throw
#  else
#    define LEMON_ASSERT_HANDLER ::lemon::assert_fail
#  endif
#endif

#if defined(NDEBUG) || defined(LEMON_DISABLE_ASSERTS)

#  define LEMON_ASSERT(exp, msg)  (static_cast<void> (0))

#else

/**
 * \brief Macro for assertions with customizable message
 *
 * Macro for assertions with customizable message.
 *
 * The behaviour can be customized with LEMON_ASSERT_HANDLER,
 * LEMON_ASSERT_EXCEPTION and LEMON_ASSERT_ABORT defines. Asserts can be
 * disabled by defining either NDEBUG or LEMON_DISABLE_ASSERTS macros.
 *
 * \todo We should provide some way to reset to the default behaviour,
 * shouldn't we?
 *
 * \todo This whole 'assert' business should be placed in a separate
 * include file.
 *
 * \todo __PRETTY_FUNCTION__ should be replaced by something
 * compiler-independant, like BOOST_CURRENT_FUNCTION
 */

#  define LEMON_ASSERT(exp, msg)                 \
     (static_cast<void> (!!(exp) ? 0 : (         \
       LEMON_ASSERT_HANDLER(__FILE__, __LINE__,  \
                            __PRETTY_FUNCTION__, \
			    (msg), #exp, LEMON_ASSERT_ABORT), 0)))

#endif // NDEBUG || LEMON_DISABLE_ASSERTS

/**
 * \brief Macro for mark not yet implemented features.
 *
 * \todo Is this the right place for this? It should be used only in
 * modules under development.
 *
 * \todo __PRETTY_FUNCTION__ should be replaced by something
 * compiler-independant, like BOOST_CURRENT_FUNCTION
 */

# define LEMON_FIXME(msg) \
    (LEMON_ASSERT_HANDLER(__FILE__, __LINE__, __PRETTY_FUNCTION__, \
			  "FIXME: " msg))
