/* -*- mia-c++  -*-
 *
 * This file is part of MIA - a toolbox for medical image analysis
 * Copyright (c) Leipzig, Madrid 1999-2017 Gert Wollny
 *
 * MIA 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 3 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 MIA; if not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef mia_core_errormacro_hh
#define mia_core_errormacro_hh


#include <iostream>
#include <sstream>
#include <cassert>
#include <stdexcept>
#include <mia/core/defines.hh>


NS_MIA_BEGIN

#if defined(HAVE_CXXABI_H) && defined(HAVE_EXEINFO_H)
void append_stacktrace(ostream& os);
#endif

template <typename V>
void __append_message(std::ostream& os, const V& v)
{
       os << v;
}

inline void __append_message(std::ostream& MIA_PARAM_UNUSED(os))
{
}

template <typename V, typename... T>
void __append_message(std::ostream& os, const V& v, T ...t)
{
       os << v;
       ::mia::__append_message(os, t...);
}

template <typename... T>
const std::string __create_message(T ...t)
{
       std::stringstream msg;
       ::mia::__append_message(msg, t...);
#if defined(HAVE_CXXABI_H) && defined(HAVE_EXEINFO_H)
       append_stacktrace(msg);
#endif
       return msg.str();
}

/**
    \ingroup misc
    \brief  helper template to create exceptions with complex messages

    This function template creates an exception of the given type. The
    exception message is created by feeding the arguments from left to right
    into a stringstream. An abitrary number of arguments can be given, but they all
    must support the operator << for writing to an output stream

    \tparam E the exception to be created
    \tparam T... the variadic list of arguments
    \param ...t the arguments used to create the exception message
*/
template <typename E, typename... T>
E create_exception( T ...t )
{
       return E(::mia::__create_message(t...));
}

#ifdef NDEBUG

/** \brief throw if compiled in release mode but assert if compiled in debug mode
    This macro can be used to get an assertion in a debug build and an exeption
    in release mode.

    \param cond the condition that is tested in the assertion
    \param msg a varaidic list of parameters that is used to create the assertion/exception message
 */
#define DEBUG_ASSERT_RELEASE_THROW(cond, msg... )	\
	if (!(cond)) {					\
		throw ::mia::create_exception<std::logic_error>(msg);	\
	}
#else
#define DEBUG_ASSERT_RELEASE_THROW(cond, msg... )			\
	if (!(cond)) {							\
		std::cerr << "Assertion failed: \'" #cond  << "\' " <<	\
			__FILE__ << ":" << __LINE__ << " "		\
			  << ::mia::__create_message(msg) << "\n";	\
			abort();					\
	}
#endif

NS_MIA_END

#endif
