/*
 * Copyright 2015 - 2022 gary@drinkingtea.net
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#if defined(OX_USE_STDLIB) && __has_include(<unistd.h>)
#include <iostream>

#include <execinfo.h>
#include <dlfcn.h>

#if __has_include(<cxxabi.h>)
#include <cxxabi.h>
#endif
#endif

#include "string.hpp"
#include "trace.hpp"
#include "vector.hpp"

namespace ox {

#if defined(OX_USE_STDLIB) && __has_include(<unistd.h>)
[[nodiscard]]
static auto symbolicate([[maybe_unused]]char **frames,
                        [[maybe_unused]]std::size_t symbolsLen,
                        [[maybe_unused]]const char *linePrefix) {
	using StrT = BasicString<100>;
	Vector<StrT, 30> out;
#if __has_include(<cxxabi.h>)
	for (auto i = 0u; i < symbolsLen; ++i) {
		Dl_info info;
		if (dladdr(frames[i], &info) && info.dli_sname) {
			int status = -1;
			const auto name = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status);
			if (status == 0) {
				out.emplace_back(sfmt<StrT>("{}: {}", i, name));
				continue;
			}
		}
		out.emplace_back(sfmt<StrT>("{}", frames[i]));
	}
#endif // __has_include(<cxxabi.h>)
	return out;
}
#endif // defined(OX_USE_STDLIB) && __has_include(<unistd.h>)

void printStackTrace([[maybe_unused]]unsigned shave) noexcept {
#if defined(OX_USE_STDLIB) && __has_include(<unistd.h>)
	constexpr auto FrameCnt = 100;
	Vector<void*, FrameCnt> frames(FrameCnt);
	frames.resize(static_cast<std::size_t>(backtrace(frames.data(), static_cast<int>(frames.size()))));
	if (frames.size() - shave > 2) {
		const auto symbols = backtrace_symbols(frames.data() + shave, static_cast<int>(frames.size() - shave));
		const auto symbolicatedStacktrace = symbolicate(reinterpret_cast<char**>(frames.data() + shave), frames.size() - shave, "\t");
		free(symbols);
		oxErr("Stacktrace:\n");
		for (const auto &s : symbolicatedStacktrace) {
			oxErrf("\t{}\n", s);
		}
	}
#endif
}

}
