/*
 * The UDS Collection
 * Copyright (C) 1999, 2000, 2001  Julian v. Bock
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *	(See file COPYING for more information)
 */

#ifndef _uds_exception_hh
#define _uds_exception_hh


// includes

#include <uds/diagnose.hh>
#include <uds/init.hh>
#include <exception>
#include <string>

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

using namespace std;


namespace uds
{

/** UDS exception base class base ;-).
	Exceptions that are derived from this class are considered to be (almost)
	fatal.
	\author Julian v. Bock */
class Exception
{
public:
	/** \param f 'fatal error' flag
		\param d description
		\param g diagnose class */
	Exception( bool f, const string& d, const DiagBase& g );
	
	/** \param d description
		\param g diagnose class */
	Exception( const string& d, const DiagBase& g );
	
	/** copy constructor */
	Exception( const Exception& rhs );
	
	/** destructor */
	virtual
	~Exception();
	
	/** generates a string that contains the location in the current source
		file
		\param file source file name
		\param line current line
		\param fn function name
		\return message string
		\see _uds_SourceLocation */
	static string
	SourceLocMsg( const char* file, int line, const char* fn );
	
	/** returns a string that contains a short note about the type of the
		error
		\return error type description */
	virtual string
	What() const = 0;
	
	/** returns a string that might give an error diagnose
		\return additional info */
	virtual string
	Diagnose() const;
	
	/** returns a backtrace. This string is empty if the class didn't generate
		a backtrace
		\return backtrace */
	const string&
	Backtrace() const { return btrace; }
	
	/** returns a string that gives an additional description. One might want
		to place log messages here when the exception is thrown or when it is
		caught and thrown.
		\return description */
	const string&
	Info() const { return addInfo; }
	
	/** returns a string that gives an additional description. This string can
		be modified whenever the exception is caught. One might want to place
		log messages here when the exception is thrown or when it is caught
		and thrown.
		\return description */
	string&
	Info() { return addInfo; }
	
	/** generates a string that contains of the type of
		the error, the diagnose, the 'additional info' string, and a backtrace
		\return message */
	string
	Message() const;
	
	/** appends additional information to the message string. This string can
		be modified whenever the exception is caught. One might want to place
		log messages here when the exception is thrown or when it is caught
		and thrown.
		\param msg extra information. If this string is not terminated with a
			newline, a newline is appended */
	void
	AppMsg( const string& msg );
	
	/** provides read-only access to the diagnose class
		\return diagnose class */
	const DiagBase&
	Diag() const { return diag; }
	
	Exception&
	operator = ( const Exception& rhs );
	
private:
	/// additional information
	string		addInfo;
	/// backtrace
	string		btrace;
	/// diagnose class
	DiagBase&		diag;
	
	/** initialization function */
	void
	init();
	
	/** dumps the core */
	void
	dump();
};


/** uds exception class generation macro
	\param Name exception class name
	\param Base base class
	\param What_parm short error description
	\param DefDiag default diagnose class (with default constructor)
	\author Julian v. Bock */
#define _uds_ExceptionClass( Name, Base, What_parm, DefDiag )                \
class Name : public Base                                                     \
{                                                                            \
public:                                                                      \
	Name( const string& d = string(), const uds::DiagBase& g = DefDiag() ) : \
		Base( d, g ) {}                                                      \
	                                                                         \
	Name( const char* d ) :                                                  \
		Base( d, DefDiag() ) {}                                              \
	                                                                         \
	Name( bool f, const string& d = string(), const uds::DiagBase& g         \
		= DefDiag() ) : Base( f, d, g ) {}                                   \
	                                                                         \
	virtual                                                                  \
	~Name();                                                                 \
	                                                                         \
	virtual string                                                           \
	What() const { return What_parm; }                                       \
}

/** uds exception class generation macro
	\param Name exception class name
	\param Base base class
	\param What_parm short error description
	\param DefDiag default diagnose class (with default constructor)
	\param Fatal FatalError flag
	\author Julian v. Bock */
#define _uds_ExceptionClassF( Name, Base, What_parm, DefDiag, Fatal )        \
class Name : public Base                                                     \
{                                                                            \
public:                                                                      \
	Name( const string& d, const uds::DiagBase& g = DefDiag() )              \
		: Base( Fatal, d, g ) {}                                             \
                                                                             \
	Name( const char* d ) :                                                  \
		Base( Fatal, d, DefDiag() ) {}                                       \
	                                                                         \
	Name( bool f, const string& d, const uds::DiagBase& g                    \
		= DefDiag() ) : Base( f, d, g ) {}                                   \
	                                                                         \
	virtual                                                                  \
	~Name();                                                                 \
	                                                                         \
	virtual string                                                           \
	What() const { return What_parm; }                                       \
}

/** uds pure exception class generation macro
	\param Name exception class name
	\param Base base class
	\author Julian v. Bock */
#define _uds_PureExceptionClass( Name, Base )                                \
class Name : public Base                                                     \
{                                                                            \
public:                                                                      \
	Name( const string& d, const uds::DiagBase& g ) :                        \
		Base( d, g ) {}                                                      \
	                                                                         \
	Name( bool f, const string& d, const uds::DiagBase& g ) :                \
		Base( f, d, g ) {}                                                   \
	                                                                         \
	virtual                                                                  \
	~Name() = 0;                                                             \
}

/** uds pure exception class generation macro
	\param Name exception class name
	\param Base base class
	\param Fatal FatalError flag
	\author Julian v. Bock */
#define _uds_PureExceptionClassF( Name, Base, Fatal )                        \
class Name : public Base                                                     \
{                                                                            \
public:                                                                      \
	Name( const string& d, const uds::DiagBase& g ) :                        \
		Base( Fatal, d, g ) {}                                               \
	                                                                         \
	Name( bool f, const string& d, const uds::DiagBase& g ) :                \
		Base( f, d, g ) {}                                                   \
	                                                                         \
	virtual                                                                  \
	~Name() = 0;                                                             \
	                                                                         \
	virtual bool                                                             \
	FatalError() const { return Fatal; }                                     \
}

/** exception class generation macro. The exception class is derived
	from std::exception.
	\param Name class name
	\param what error description
	\author Julian v. Bock */
#define _uds_SExceptionClass( Name, What )                                   \
class Name : public exception                                                \
{                                                                            \
public:                                                                      \
	~Name();                                                                 \
                                                                             \
	virtual const char*                                                      \
	what() const { return What; }						                     \
} 

/** debug exception class macro
	\param Name exception class name
	\author Julian v. Bock */
#ifdef DEBUG
#define _uds_DebugExceptionClass( Name, Desc )                               \
_uds_ExceptionClassF( Name, uds::Exception, Desc,                            \
	uds::NoDiag, false )
#else
#define _uds_DebugExceptionClass( Name, Desc )                               \
_uds_SExceptionClass( Name, Desc )
#endif


/** This macro calls Exception::SourceLocMsg. It has to be a macro since
	we use __FILE__ and __LINE__ */
#define _uds_SourceLocation                                                  \
	uds::Exception::SourceLocMsg( __FILE__, __LINE__, __PRETTY_FUNCTION__ )


/** This macro calls Exception::SourceLocMsg if DEBUG is defined; otherwise
	an empty string is returned */
#ifdef DEBUG
#define _uds_DbgSourceLocation                                               \
	_uds_SourceLocation
#else
#define _uds_DbgSourceLocation string()
#endif


/** throws an uds exception. If main() was not entered, an error description is
	printed to stderr.
	\param e exception to be thrown
	\param s when true, the exception is only thrown if there is no uncaught
		exception. Otherwise the error message is printed immediately. */
template < class E >
void
Throw( const E& e, bool s = true )
{
	if ( s && uncaught_exception() )
	{
		cerr<<"WARNING: uncaught exception\ndropping following exception\n"
			<<e.Message();
		
		return;
	}
	
	if ( !uds::Initialized() )
		cerr<<"throwing exception\n"<<e.Message();
	
	throw e;
}

}

#endif // _uds_exception_hh
