// =====================================================================================================================
//  fennec, a free and open source game engine
//  Copyright © 2025 - 2026  Medusa Slockbower
//
//  This program 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.
//
//  This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================

///
/// \file fennec/lang/assert.h
/// \brief \ref fennec_lang_assert
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///

#ifndef FENNEC_LANG_ASSERT_H
#define FENNEC_LANG_ASSERT_H

#include <fennec/lang/types.h>

///
/// \page fennec_lang_assert Assertions
///
/// \code #include <fennec/lang/assert.h> \endcode
///
/// This header contains macros for making assertions about code behaviour.
///
/// fennec defines the following assert implementations:
///
/// <table width="100%" class="fieldtable" id="table_fennec_lang_bits">
/// <tr><th style="vertical-align: top">Syntax
///     <th style="vertical-align: top">Description
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         assert(expr, desc)
///     <td width="50%" style="vertical-align: top">
///         Make an assertion with expression \emph{expr} and provide a description \emph{desc}. Only halts in debug mode.
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         assertf(expr, desc)
///     <td width="50%" style="vertical-align: top">
///         Make an assertion with expression \emph{expr} and provide a description \emph{desc}. Always halts.
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         assertd(expr, desc)
///     <td width="50%" style="vertical-align: top">
///         Make an assertion, ***only in debug mode***, with expression \emph{expr} and provide a description \emph{desc}.
///         This should be used when the branching caused by \emph{assert} would hinder performance in release mode.
///
/// </table>
///
///
///

#if FENNEC_COMPILER_MSVC
#define __PRETTY_FUNCTION__ __FUNCSIG__
#endif

#ifndef FENNEC_DOXYGEN
void _assert_impl(const char* expr, size_t expr_l,
	              const char* file, size_t file_l, int line,
	              const char* func, size_t func_l,
	              const char* desc, bool halt);

template<size_t ExprL, size_t FileL, size_t FuncL>
void _assert(const char (&expr)[ExprL],
	         const char (&file)[FileL], int line,
	         const char (&func)[FuncL],
	         const char* desc,
	         bool halt) {
	::_assert_impl(expr, ExprL, file, FileL, line, func, FuncL, desc, halt);
}
#endif

///
/// \brief base assert function, halts only in debug mode
/// \param expression the expression to validate
/// \param description the description of the assertion
#define assert(expression, description) \
	if(not(expression)) [[unlikely]] { \
		_assert(#expression, __FILE__, __LINE__, __PRETTY_FUNCTION__, description, FENNEC_DEBUG); \
	}

///
/// \brief fail assert function, always halts
/// \param expression the expression to validate
/// \param description the description of the assertion
#define assertf(expression, description) \
	if(not(expression)) [[unlikely]] { \
		_assert(#expression, __FILE__, __LINE__, __PRETTY_FUNCTION__, description, true); \
	}

///
/// \brief debug assert function, only defined in debug mode
/// \param expression the expression to validate
/// \param description the description of the assertion
#if FENNEC_RELEASE
#define assertd(expression, description)
#else
#define assertd(expression, description) assert(expression, description)
#endif

#endif // FENNEC_LANG_ASSERT_H
