/* xoreos-tools - Tools to help with xoreos development
 *
 * xoreos-tools is the legal property of its developers, whose names
 * can be found in the AUTHORS file distributed with this source
 * distribution.
 *
 * xoreos-tools 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.
 *
 * xoreos-tools 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 xoreos-tools. If not, see <http://www.gnu.org/licenses/>.
 */

/** @file
 *  Basic exceptions to throw.
 */

#include <cstdarg>
#include <cstdio>
#include <cstdlib>

#include "src/common/error.h"
#include "src/common/util.h"

namespace Common {

StackException::StackException() {
}

StackException::StackException(const char *s, ...) {
	char buf[STRINGBUFLEN];
	va_list va;

	va_start(va, s);
	vsnprintf(buf, STRINGBUFLEN, s, va);
	va_end(va);

	_stack.push(buf);
}

StackException::StackException(const StackException &e) : _stack(e._stack) {
}

StackException::StackException(const std::exception &e) {
	add(e);
}

StackException::~StackException() throw() {
}

void StackException::add(const char *s, ...) {
	char buf[STRINGBUFLEN];
	va_list va;

	va_start(va, s);
	vsnprintf(buf, STRINGBUFLEN, s, va);
	va_end(va);

	_stack.push(buf);
}


void StackException::add(const std::exception &e) {
	add("%s", e.what());
}

const char *StackException::what() const throw() {
	if (_stack.empty())
		return "";

	return _stack.top().c_str();
}

bool StackException::empty() const {
	return _stack.empty();
}

StackException::Stack &StackException::getStack() {
	return _stack;
}


const Exception kOpenError("Can't open file");
const Exception kReadError("Read error");
const Exception kSeekError("Seek error");
const Exception kWriteError("Write error");


void printException(Exception &e, const UString &prefix) {
	try {
		Exception::Stack &stack = e.getStack();

		if (stack.empty()) {
			status("FATAL ERROR");
			return;
		}

		status("%s%s", prefix.c_str(), stack.top().c_str());

		stack.pop();

		while (!stack.empty()) {
			status("    Because: %s", stack.top().c_str());
			stack.pop();
		}
	} catch (...) {
		status("FATAL ERROR: Exception while printing exception stack");
		std::exit(1);
	}
}

void exceptionDispatcherError(const UString &reason) {
	try {
		try {
			throw;
		} catch (Exception &e) {
			if (!reason.empty())
				e.add("%s", reason.c_str());

			printException(e);
			std::exit(1);
		} catch (std::exception &e) {
			Exception se(e);
			if (!reason.empty())
				se.add("%s", reason.c_str());

			printException(se);
			std::exit(1);
		} catch (...) {
			Exception se("Unknown exception caught");
			if (!reason.empty())
				se.add("%s", reason.c_str());

			printException(se);
			std::exit(1);
		}
	} catch (...) {
		std::exit(2);
	}
}

void exceptionDispatcherWarnAndIgnore(const UString &reason) {
	try {
		try {
			throw;
		} catch (Exception &e) {
			if (!reason.empty())
				e.add("%s", reason.c_str());

			printException(e, "WARNING: ");
		} catch (std::exception &e) {
			Exception se(e);
			if (!reason.empty())
				se.add("%s", reason.c_str());

			printException(se, "WARNING: ");
		} catch (...) {
			if (!reason.empty()) {
				Exception se("%s", reason.c_str());
				printException(se, "WARNING: ");
			}
		}
	} catch (...) {
	}
}

} // End of namespace Common
