#include <iomanip>
#include "ExceptionHandler.hpp"

namespace alib {

ExceptionHandler::NestedException::NestedException ( std::string type_, std::string desc_ ) : type ( std::move ( type_ ) ), desc ( std::move ( desc_ ) ) { }

ExceptionHandler::NestedException::NestedException ( std::string type_ ) : type ( std::move ( type_ ) ) { }

ExceptionHandler::NestedException::operator std::string ( ) const {
	std::ostringstream oss;
	if ( desc.has_value ( ) ) {
		oss << "[" << type << "]: " << desc.value ( );
	} else {
		oss << type;
	}

	return oss.str ( );
}

std::vector < std::function < int ( alib::ExceptionHandler::NestedExceptionContainer & ) > > & ExceptionHandler::handlers ( ) {
	static std::vector < std::function < int ( NestedExceptionContainer & ) > > res = {
		[ ] ( NestedExceptionContainer & output ) {
			try {
				throw;
			} catch ( ... ) {
				output.push_back ( "Unexpected exception" );
				return 127;
			}
		}, [ ] ( NestedExceptionContainer & output ) {
			try {
				throw;
			} catch ( const std::exception & e ) {
				output.push_back ( "Standard exception", e.what ( ) );
				return rethrow_if_nested ( output, e, 3 );
			}
		}
	};
	return res;
}

int ExceptionHandler::rethrow_if_nested ( NestedExceptionContainer & output, const std::exception & e, int result ) {
	try {
		std::rethrow_if_nested ( e );
		return result;
	} catch ( ... ) {
		return handlerRec ( output, handlers ( ).rbegin ( ) );
	}
}

int ExceptionHandler::handlerRec ( NestedExceptionContainer & output, const std::vector < std::function < int ( NestedExceptionContainer & ) > >::const_reverse_iterator & iter ) {
	if ( iter == handlers ( ).rend ( ) ) {
		output.push_back ( "Unhandled exception" );
		return 127;
	}

	try {
		return ( * iter ) ( output );
	} catch ( ... ) {
		return handlerRec ( output, std::next ( iter ) );
	}
}

int ExceptionHandler::handle ( NestedExceptionContainer & output ) {
	return handlerRec ( output, handlers ( ).rbegin ( ) );
}

int ExceptionHandler::handle ( std::ostream & output ) {
	NestedExceptionContainer exceptions;
	int ret = ExceptionHandler::handle ( exceptions );

	for ( size_t i = 0; i < exceptions.getExceptions ( ).size ( ); i++ ) {
		output << i << " " << std::string ( exceptions.getExceptions ( ) [ i ] ) << std::endl;
	}

	return ret;
}

} /* namespace alib */
