/*
 * 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/.
 */

#pragma once

#include "def.hpp"
#include "defines.hpp"
#include "error.hpp"
#include "realstd.hpp"
#include "stacktrace.hpp"
#include "trace.hpp"
#include "typetraits.hpp"

namespace ox {

[[noreturn]]
void panic(
	Error const&err,
	StringViewCR panicMsg,
	std::source_location const &src = std::source_location::current()) noexcept;

[[noreturn]]
inline void panic(
	StringViewCR panicMsg,
	std::source_location const &src = std::source_location::current()) noexcept {
	panic(Error{1}, panicMsg, src);
}

[[noreturn]]
constexpr void constexprPanic(
	StringViewCR panicMsg,
	Error const &err = {},
	std::source_location const &src = std::source_location::current()) noexcept {
	if (!std::is_constant_evaluated()) {
		panic(err, panicMsg, src);
	} else {
		while (true);
	}
}

void assertFailFuncRuntime(
	StringViewCR assertTxt,
	StringViewCR msg,
	std::source_location const &src = std::source_location::current()) noexcept;

void assertFailFuncRuntime(
	Error const &err,
	StringViewCR,
	StringViewCR assertMsg,
	std::source_location const &src = std::source_location::current()) noexcept;

constexpr void assertFunc(
	bool const pass,
	[[maybe_unused]]StringViewCR assertTxt,
	[[maybe_unused]]StringViewCR msg,
	std::source_location const &src = std::source_location::current()) noexcept {
	if (!pass) {
		if (!std::is_constant_evaluated()) {
			assertFailFuncRuntime(assertTxt, msg, src);
		} else {
			while (true);
		}
	}
}

constexpr void assertFunc(
	Error const &err,
	StringViewCR,
	StringViewCR assertMsg,
	std::source_location const &src = std::source_location::current()) noexcept {
	if (err) {
		if (!std::is_constant_evaluated()) {
			assertFailFuncRuntime(err, {}, assertMsg, src);
		} else {
			while (true);
		}
	}
}

constexpr void expect(
	auto const &actual,
	auto const &expected,
	std::source_location const &src = std::source_location::current()) noexcept {
	if (actual != expected) {
		 if (!std::is_constant_evaluated()) {
#if defined(OX_USE_STDLIB)
			oxErrf(
				"\n\033[31;1;1mASSERT FAILURE:\033[0m [{}:{}]: {}\n",
				src.file_name(),
				src.line(),
				"Value incorrect");
			oxErrf(
				"expected: {}\nactual:   {}\n",
				detail::toStringView<true>(expected),
				detail::toStringView<true>(actual));
			printStackTrace(2);
			oxTracef(
				"assert.expect", "Failed assert: {} == {} [{}:{}]",
				detail::toStringView<true>(actual),
				detail::toStringView<true>(expected),
				src.file_name(),
				src.line());
			std::abort();
#else
			constexprPanic("Comparison failed", {}, src);
#endif
		} else {
			while (true);
		}
	}
}

}
