#ifndef _WIN32

#include <utils/DefaultSignalHandler.h>
#include <utils/Logger.h>

#include <ctype.h>
#include <cxxabi.h>
#include <execinfo.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include <QCoreApplication>

namespace DefaultSignalHandler
{
struct Signal
{
	int          number;
	const char * name;
};

const Signal ALL_SIGNALS[] = {
	{ SIGABRT, "SIGABRT" },
	{ SIGBUS,  "SIGBUS"  },
	{ SIGFPE,  "SIGFPE"  },
	{ SIGILL,  "SIGILL"  },
	{ SIGSEGV, "SIGSEGV" },
	{ SIGTERM, "SIGTERM" },
	{ SIGHUP,  "SIGHUP"  },
	{ SIGINT,  "SIGINT"  },
	{ SIGPIPE, "SIGPIPE" },
};

void write_to_stderr(const char* data, size_t size)
{
	int res = write(STDERR_FILENO, data, size);

	Q_UNUSED(res);
}

void write_to_stderr(const char* data)
{
	write_to_stderr(data, strlen(data));
}

std::string decipher_trace(const std::string &trace)
{
	std::string result;

	if(trace.empty())
	{
		result += "??\n";
		return result;
	}

	auto* begin = strchr(trace.c_str(), '(') + 1;
	auto* end = strchr(begin, '+');

	if(!end)
		end = strchr(begin, ')');

	std::string mangled_name(begin, end);

	int status;
	char * realname = abi::__cxa_demangle(mangled_name.c_str(), 0, 0, &status);
	result.insert(result.end(), trace.c_str(), begin);

	if(realname)
		result += realname;
	else
		result.insert(result.end(), begin, end);

	free(realname);
	result.insert(result.size(), end);

	return result;
}

void print_trace()
{
	const int MAX_SIZE = 50;
	void * addresses[MAX_SIZE];
	int size = backtrace(addresses, MAX_SIZE);

	if (!size)
		return;

	Logger* log = Logger::getInstance("CORE");
	char ** symbols = backtrace_symbols(addresses, size);

	/* Skip first 2 frames as they are signal
	 * handler and print_trace functions. */
	for (int i = 2; i < size; ++i)
	{
		std::string line = "\t" + decipher_trace(symbols[i]);
		Error(log, line.c_str());
	}

	free(symbols);
}

void install_default_handler(int signum)
{
	struct sigaction action{};
	sigemptyset(&action.sa_mask);
	action.sa_handler = SIG_DFL;
	(void)sigaction(signum, &action, nullptr);
}

/* Note that this signal handler is not async signal safe !
 * Ideally a signal handler should only flip a bit and defer
 * heavy work to some kind of bottom-half processing. */
void signal_handler(int signum, siginfo_t * /*info*/, void * /*context*/)
{
	const char * name = "UNKNOWN SIGNAL";

	for (const auto& s : ALL_SIGNALS) {
		if (s.number == signum) {
			name = s.name;
			break;
		}
	}

	write_to_stderr("\n");
	write_to_stderr("Hyperion caught signal :");
	write_to_stderr(name);
	write_to_stderr("\n");

	/* Anything below here is unsafe ! */

	switch(signum)
	{
	case SIGBUS:
	case SIGSEGV:
	case SIGABRT:
	case SIGFPE :
		print_trace();

		/* Don't catch our own signal */
		install_default_handler(signum);

		kill(getpid(), signum);
		return;
	case SIGINT :
	case SIGTERM:
	case SIGPIPE:
	default:
		/* If the signal_handler is hit before the event loop is started,
		 * following call will do nothing. So we queue the call. */

		// QCoreApplication::quit();
		QMetaObject::invokeMethod(qApp, "quit", Qt::QueuedConnection);

		// Reset signal handler to default (in case this handler is not capable of stopping)
		install_default_handler(signum);
	}
}

} // namespace DefaultSignalHandler
#endif // _WIN32

namespace DefaultSignalHandler
{
void install()
{
#ifndef _WIN32
	Logger* log = Logger::getInstance("CORE");

	struct sigaction action{};
	sigemptyset(&action.sa_mask);
	action.sa_sigaction = signal_handler;
	action.sa_flags |= SA_SIGINFO;

	for (const auto& s : ALL_SIGNALS)
	{
		if (sigaction(s.number, &action, nullptr)!= 0)
		{
			Error(log, "Failed to install handler for %s]\n", s.name);
		}
	}
#endif // _WIN32
}
} // namespace DefaultSignalHandler
