/* $Id: CoinError.hpp 1372 2011-01-03 23:31:00Z lou $ */
// Copyright (C) 2000, International Business Machines
// Corporation and others.  All Rights Reserved.
// This code is licensed under the terms of the Eclipse Public License (EPL).

#ifndef CoinError_H
#define CoinError_H

#include <string>
#include <iostream>
#include <cassert>
#include <cstring>

#include "CoinUtilsConfig.h"
#include "CoinPragma.hpp"

/** A function to block the popup windows that windows creates when the code
    crashes */
void WindowsErrorPopupBlocker();

//-------------------------------------------------------------------
//
// Error class used to throw exceptions
//
// Errors contain:
//
//-------------------------------------------------------------------

/** Error Class thrown by an exception

This class is used when exceptions are thrown.
It contains:
  <ul>
  <li>message text
  <li>name of method throwing exception
  <li>name of class throwing exception or hint
  <li>name of file if assert
  <li>line number
  </ul>
  For asserts class=> optional hint
*/
class CoinError  {
    friend void CoinErrorUnitTest();

private:
    CoinError()
      :
      message_(),
      method_(),
      class_(),
      file_(),
      lineNumber_()
    {
      // nothing to do here
    }

public:
    
  //-------------------------------------------------------------------
  // Get methods
  //-------------------------------------------------------------------   
  /**@name Get error attributes */
  //@{
    /// get message text
    inline const std::string & message() const 
    { return message_; }
    /// get name of method instantiating error
    inline const std::string & methodName() const 
    { return method_;  }
    /// get name of class instantiating error (or hint for assert)
    inline const std::string & className() const 
    { return class_;   }
    /// get name of file for assert
    inline const std::string & fileName() const 
    { return file_;  }
    /// get line number of assert (-1 if not assert)
    inline int lineNumber() const 
    { return lineNumber_;   }
    /// Just print (for asserts)
    inline void print(bool doPrint = true) const
    {
      if (! doPrint)
        return;
      if (lineNumber_<0) {
        std::cout<<message_<<" in "<<class_<<"::"<<method_<<std::endl;
      } else {
        std::cout<<file_<<":"<<lineNumber_<<" method "<<method_
                 <<" : assertion \'"<<message_<<"\' failed."<<std::endl;
        if(class_!="")
          std::cout<<"Possible reason: "<<class_<<std::endl;
      }
    }
  //@}
  
    
  /**@name Constructors and destructors */
  //@{
    /// Alternate Constructor 
    CoinError ( 
      std::string message__, 
      std::string methodName__, 
      std::string className__,
      std::string fileName_ = std::string(),
      int line = -1)
      :
      message_(message__),
      method_(methodName__),
      class_(className__),
      file_(fileName_),
      lineNumber_(line)
    {
      print(printErrors_);
    }

    /// Copy constructor 
    CoinError (const CoinError & source)
      :
      message_(source.message_),
      method_(source.method_),
      class_(source.class_),
      file_(source.file_),
      lineNumber_(source.lineNumber_)
    {
      // nothing to do here
    }

    /// Assignment operator 
    CoinError & operator=(const CoinError& rhs)
    {
      if (this != &rhs) {
	message_=rhs.message_;
	method_=rhs.method_;
	class_=rhs.class_;
	file_=rhs.file_;
	lineNumber_ = rhs.lineNumber_;
      }
      return *this;
    }

    /// Destructor 
    virtual ~CoinError ()
    {
      // nothing to do here
    }
  //@}
    
private:
    
  /**@name Private member data */
  //@{
    /// message test
    std::string message_;
    /// method name
    std::string method_;
    /// class name or hint
    std::string class_;
    /// file name
    std::string file_;
    /// Line number
    int lineNumber_;
  //@}

public:
  /// Whether to print every error
  static bool printErrors_;
};

#ifndef __STRING
#define __STRING(x)	#x
#endif

#ifndef __GNUC_PREREQ
# define __GNUC_PREREQ(maj, min) (0)
#endif 

#ifndef COIN_ASSERT
#   define CoinAssertDebug(expression) assert(expression)
#   define CoinAssertDebugHint(expression,hint) assert(expression)
#   define CoinAssert(expression) assert(expression)
#   define CoinAssertHint(expression,hint) assert(expression)
#else
#   ifdef NDEBUG
#      define CoinAssertDebug(expression)		{}
#      define CoinAssertDebugHint(expression,hint)	{}
#   else
#      if defined(__GNUC__) && __GNUC_PREREQ(2, 6)
#         define CoinAssertDebug(expression) { 				   \
             if (!(expression)) {					   \
                throw CoinError(__STRING(expression), __PRETTY_FUNCTION__, \
                                "", __FILE__, __LINE__);		   \
             }								   \
          }
#         define CoinAssertDebugHint(expression,hint) {			   \
             if (!(expression)) {					   \
                throw CoinError(__STRING(expression), __PRETTY_FUNCTION__, \
                                hint, __FILE__,__LINE__);		   \
             }								   \
          }
#      else
#         define CoinAssertDebug(expression) {				   \
             if (!(expression)) {					   \
                throw CoinError(__STRING(expression), "",		   \
                                "", __FILE__,__LINE__);			   \
             }								   \
          }
#         define CoinAssertDebugHint(expression,hint) {			   \
             if (!(expression)) {					   \
                throw CoinError(__STRING(expression), "",		   \
                                hint, __FILE__,__LINE__);		   \
             }								   \
          }
#      endif
#   endif
#   if defined(__GNUC__) && __GNUC_PREREQ(2, 6)
#      define CoinAssert(expression) { 					\
          if (!(expression)) {						\
             throw CoinError(__STRING(expression), __PRETTY_FUNCTION__, \
                             "", __FILE__, __LINE__);			\
          }								\
       }
#      define CoinAssertHint(expression,hint) {				\
          if (!(expression)) {						\
             throw CoinError(__STRING(expression), __PRETTY_FUNCTION__, \
                             hint, __FILE__,__LINE__);			\
          }								\
       }
#   else
#      define CoinAssert(expression) {					\
          if (!(expression)) {						\
             throw CoinError(__STRING(expression), "",			\
                             "", __FILE__,__LINE__);			\
          }								\
       }
#      define CoinAssertHint(expression,hint) {				\
          if (!(expression)) {						\
             throw CoinError(__STRING(expression), "",			\
                             hint, __FILE__,__LINE__);			\
          }								\
       }
#   endif
#endif


//#############################################################################
/** A function that tests the methods in the CoinError class. The
    only reason for it not to be a member method is that this way it doesn't
    have to be compiled into the library. And that's a gain, because the
    library should be compiled with optimization on, but this method should be
    compiled with debugging. */
void
CoinErrorUnitTest();

#ifdef __LINE__
#define CoinErrorFL(x, y, z) CoinError((x), (y), (z), __FILE__, __LINE__)
#endif

#endif