/*
 * Copyright 2015 - 2025 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 https://mozilla.org/MPL/2.0/.
 */

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

#if __has_include(<execinfo.h>)
#include <execinfo.h>
#include <dlfcn.h>
#endif

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

#include <ox/std/def.hpp>
#include <ox/std/defines.hpp>
#include <ox/std/string.hpp>
#include <ox/std/trace.hpp>
#include <ox/std/vector.hpp>

OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)

namespace ox {

#if defined(OX_USE_STDLIB) && __has_include(<unistd.h>)
[[nodiscard]] [[maybe_unused]]
static auto symbolicate([[maybe_unused]]void **frames,
                        [[maybe_unused]]std::size_t frameCnt,
                        [[maybe_unused]]const char *linePrefix) {
	using StrT = BasicString<100>;
	Vector<StrT, 30> out;
#if __has_include(<cxxabi.h>) && __has_include(<execinfo.h>)
#ifdef OX_OS_FreeBSD
	using FrameCnt_t = unsigned;
#else
	using FrameCnt_t = signed;
#endif
	const auto mangledSymbols = backtrace_symbols(frames, static_cast<FrameCnt_t>(frameCnt));
	for (auto i = 0u; i < frameCnt; ++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>("{}: {}", i, mangledSymbols[i]));
	}
	free(mangledSymbols);
#endif // __has_include(<cxxabi.h>)
	return out;
}
#endif // defined(OX_USE_STDLIB) && __has_include(<unistd.h>)

ox::String genStackTrace([[maybe_unused]]unsigned shave) noexcept {
	ox::String out;
#if defined(OX_USE_STDLIB) && __has_include(<unistd.h>) && __has_include(<execinfo.h>)
	constexpr auto FrameCnt = 100;
	Vector<void*, FrameCnt> frames(FrameCnt);
#ifdef OX_OS_FreeBSD
	using FrameCnt_t = unsigned;
#else
	using FrameCnt_t = signed;
#endif
	frames.resize(static_cast<std::size_t>(backtrace(frames.data(), static_cast<FrameCnt_t>(frames.size()))));
	if (frames.size() - shave > 2) {
		const auto symbolicatedStacktrace = symbolicate(frames.data() + shave, frames.size() - shave, "\t");
		out+= "Stacktrace:\n";
		for (const auto &s : symbolicatedStacktrace) {
			out += sfmt("\t{}\n", s);
		}
	}
#endif
	return out;
}

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

}

OX_CLANG_NOWARN_END
