#pragma once

#ifdef PSEMEK_STACKTRACE
	#include <boost/stacktrace.hpp>
#endif

#include <exception>
#include <string>
#include <iostream>

namespace psemek::util
{

#ifdef PSEMEK_STACKTRACE
	using stacktrace = boost::stacktrace::stacktrace;
#else
	struct stacktrace
	{
		friend std::ostream & operator << (std::ostream & stream, stacktrace)
		{
			stream << "(stacktrace disabled)\n";
			return stream;
		}
	};
#endif

	struct exception
		: std::exception
	{
		exception(std::string message, stacktrace stacktrace = {})
			: message_(std::move(message))
			, stacktrace_(std::move(stacktrace))
		{}

		char const * what() const noexcept override
		{
			return message_.data();
		}

		std::string const & message() const noexcept
		{
			return message_;
		}

		auto const & stacktrace() const noexcept
		{
			return stacktrace_;
		}

	private:
		std::string message_;
		util::stacktrace stacktrace_;
	};

	inline std::ostream & operator << (std::ostream & os, exception const & e)
	{
		os << e.what() << '\n' << e.stacktrace();
		return os;
	}

}
