/**
 * @file
 */
#ifndef UI_UTIL_EXCEPTION_HPP
#define UI_UTIL_EXCEPTION_HPP

// STDC++
#include <string>
#include <cerrno>

// C++ libraries
#include <ui-utilcpp/Text.hpp>

#define UI_SOURCEDEBUG __FILE__ + std::string(":") + UI::Util::tos(__LINE__)

/** @name Throw macro shortcuts with automatic debug info and optimized errno handling.
 *
 * @{ */
/** @brief Throw non-code exception. */
#define UI_THROW(desc)                  throw Exception(desc, UI_SOURCEDEBUG)
/** @brief Throw non-code exception w/ errno handling. */
#define UI_THROW_ERRNO(call)            { int const myErrno(errno); throw Exception(call + Exception::Errno_, UI_SOURCEDEBUG, myErrno); }
/** @brief Throw code exception. */
#define UI_THROW_CODE(code, desc)       throw Exception(code, desc, UI_SOURCEDEBUG)
/** @brief Throw code exception w/ errno handling. */
#define UI_THROW_CODE_ERRNO(code, call) { int const myErrno(errno); throw Exception(code, call + Exception::Errno_, UI_SOURCEDEBUG, myErrno); }
/** @} */

/** @brief Namespace for any Schlund+Partner C++ code. */
namespace UI {

/** @brief Generic exception class for namespace UI.
 *
 * @section Exception_HowTo Exception HOWTO
 *
 * @subsection Exception_HowTo_U User's guide (exception handlers)
 *
 * - For standard (no "code" support), just use intuitively. For
 * example, <em>UI::Exception</em> catches any S+P Exception,
 * <em>UI::Util::Exception</em> any S+P exception in namespace UI::Util,
 * <em>UI::Util::File::Exception</em> any exception of class UI::Util::File.
 *
 * - Any time you want to catch <em>code exceptions</em>, you will
 * always need a dedicated handler (knowing the code semantics)
 * anyway. For example, knowing <em>UI::Util::File::Exception</em> is
 * a code exception, catch exactly that exception and evaluate further
 * using getCode().
 *
 * @subsection Exception_HowTo_D Developer's guide (write own exception classes)
 *
 * @subsubsection Exception_HowTo_D_P Preliminaries
 *
 * - In any context (namespace or class), a class "Exception" should
 * be available, using UI::Exception or UI::CodeException constructor
 * syntax; this is needed so UI_THROW* macros can be used.
 *
 * - Note that the "interfacing" exception class is always called
 * "Exception", even if it is a code exception. The
 * <em>CodeException</em> template is only used to ease writing
 * classes w/ dedicated code number support.
 *
 * - Be sure that your exception class always inherits the next class
 * "context up". For example UI::Util::Sys::Exception must inherit
 * from UI::Util::Exception, <b>not</b> UI::Exception; else a handler
 * on UI::Util::Exception would not catch UI::Util::Sys::Exception,
 * which is against intuitive use of exceptions. The same holds true
 * for mere typedefs.
 *
 * @subsubsection Exception_HowTo_D_E Quickstart
 *
 * Let's say, you invented a new namespace "UI::Humbug".
 *
 * - In the simplest case, if you don't need support to catch down all
 * UI::Humbug's exceptions, you may just use UI_THROW*-macros w/o any
 * additional code.
 *
 * - Alternatively, if UI::Humbug needs a generic exception class so
 * we can catch it all down, just use the very same source code for
 * UI::Util::Exception in your new namespace.
 *
 * - Optionally, if you want to ease the use of code exceptions in
 * your namespace for sub-contexts, just use the very code for
 * the UI::Util::CodeException template in your namespace.
 *
 * - Alernatively, if your new namespace's generic exception class is
 * to be a code exception itself, inherit UI::Humbug::Exception from
 * UI::CodeException<HumbugCode>.
 *
 * The above three points typically go into "Exception.hpp" of your project.
 *
 * - Optionally, you can now add exception classes for sub-contexts
 * (sub-namespaces or classes) if needed.
 *
 * - To throw exceptions, always use macros UI_THROW, UI_THROW_CODE,
 * UI_THROW_ERRNO or UI_THROW_CODE_ERRNO.
 *
 */
class Exception: public std::exception
{
protected:
	/** @name These constants may be used for description default values for derived classes.
	 * @{ */
	static std::string const NoWhatGiven_;
	static std::string const NoDebugGiven_;
	/** @} */

public:
	/** @brief If this string is used in a "what" description message, it will be replaced by an errno error string. */
	static std::string const Errno_;

	/** @brief Construct with description text.
	 *
	 * @note You usually <b>do not call this constructor directly</b>, but instead
	 *       always use one of the UI_THROW_* macros above.
	 *
	 * @param what  Exception description. Errno_ text in string will be replaced with error string.
	 * @param debug Any debug text.
	 * @param errNo Dedicated "errno" error number.
	 */
	Exception(std::string const & what=NoWhatGiven_, std::string const & debug=NoDebugGiven_, int const & errNo=0);
	virtual ~Exception() throw();

	/** @name Get exception information.
	 * @{ */
	char        const * what() const throw();
	int         const & getErrno() const;
	std::string         getDebug() const;
	/** @} */

private:
	std::string const what_;
	std::string const debug_;
	int const errNo_;
};

/** @brief Adding code facility to Exception. */
template <typename Code = int>
class CodeException: public Exception
{
public:
	CodeException(Code const & code, std::string const & what=NoWhatGiven_, std::string const & debug=NoDebugGiven_, int const & errNo=0)
		:UI::Exception(what, debug, errNo)
		,code_(code)
	{};
	Code const & getCode() const { return code_; }
private:
	Code const code_;
};

/** @brief Namespace for ui-utilcpp. */
namespace Util {

/** @brief Generic exception class for namespace UI::Util. */
class Exception: public UI::Exception
{
public:
	Exception(std::string const & what=NoWhatGiven_, std::string const & debug=NoDebugGiven_, int const & errNo=0)
		:UI::Exception(what, debug, errNo) {}
	virtual ~Exception() throw() {};
};

/** @brief Adding code facility to Exception. */
template <typename Code = int>
class CodeException: public Exception
{
public:
	CodeException(Code const & code, std::string const & what=NoWhatGiven_, std::string const & debug=NoDebugGiven_, int const & errNo=0)
		:Exception(what, debug, errNo)
		,code_(code)
	{};
	Code const & getCode() const { return code_; }
private:
	Code const code_;
};

}}
#endif
