// =====================================================================================================================
//  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/intrinsics.h
/// \brief \ref fennec_lang_intrinsics
///
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///

#ifndef FENNEC_LANG_INTRINSICS_H
#define FENNEC_LANG_INTRINSICS_H

///
/// \page fennec_lang_intrinsics Intrinsics
///
/// \brief This header contains definitions for compiler intrinsics necessary for implementing functions of the
///        C++ stdlib.
///
/// \code{.cpp}#include <fennec/lang/intrinsics.h>\endcode
///
///
/// <table width="100%" class="fieldtable" id="table_fennec_lang_intrinsics">
/// <tr><th style="vertical-align: top">Syntax
///     <th style="vertical-align: top">Description
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \emph{FENNEC_HAS_BUILTIN_BIT_CAST} <br>
///         \emph{Y FENNEC_BUILTIN_BIT_CAST(X)}
///     <td width="50%" style="vertical-align: top">
///         An intrinsic for doing a bitwise cast without using \emph{reinterpret_cast}.
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \emph{FENNEC_HAS_BUILTIN_ADDRESSOF} <br>
///         \emph{Y FENNEC_BUILTIN_ADDRESSOF(X)}
///     <td width="50%" style="vertical-align: top">
///         Obtains the true address of an object in circumstances where \emph{operator&} is overloaded.
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \emph{FENNEC_HAS_BUILTIN_IS_CONVERTIBLE} <br>
///         \emph{B FENNEC_BUILTIN_IS_CONVERTIBLE(X, Y)}
///     <td width="50%" style="vertical-align: top">
///         Checks if type \emph{X} can be converted to type \emph{Y}.
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \emph{FENNEC_HAS_BUILTIN_IS_EMPTY} <br>
///         \emph{B FENNEC_BUILTIN_IS_EMPTY(X)}
///     <td width="50%" style="vertical-align: top">
///         Checks if type \emph{X} stores no data.
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \emph{FENNEC_HAS_BUILTIN_IS_POLYMORPHIC} <br>
///         \emph{B FENNEC_BUILTIN_IS_POLYMORPHIC(X)}
///     <td width="50%" style="vertical-align: top">
///         Checks if type \emph{X} is polymorphic, this is for classes only thus checks only for subtyping
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \emph{FENNEC_HAS_BUILTIN_IS_FINAL} <br>
///         \emph{B FENNEC_BUILTIN_IS_FINAL(X)}
///     <td width="50%" style="vertical-align: top">
///         Checks if type \emph{X} is final, meaning a function or class cannot be derived from.
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \emph{FENNEC_HAS_BUILTIN_IS_ABSTRACT} <br>
///         \emph{B FENNEC_BUILTIN_IS_ABSTRACT(X)}
///     <td width="50%" style="vertical-align: top">
///         Opposite of \emph{FENNEC_BUILTIN_IS_FINAL}, checks if abstract, meaning \emph{X} has at least one pure virtual function.
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \emph{FENNEC_HAS_BUILTIN_IS_STANDARD_LAYOUT} <br>
///         \emph{B FENNEC_BUILTIN_IS_STANDARD_LAYOUT(X)}
///     <td width="50%" style="vertical-align: top">
///         Checks if \emph{X} has a standard layout, here is [full criteria](https://www.cppreference.com/w/cpp/language/classes.html#Standard-layout_class)
///         for this trait
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \emph{FENNEC_HAS_BUILTIN_IS_CONSTRUCTIBLE} <br>
///         \emph{B FENNEC_BUILTIN_IS_CONSTRUCTIBLE(X, ...)}
///     <td width="50%" style="vertical-align: top">
///         Checks if type \emph{X} is constructible with args \emph{\ldots}, such that \emph{X::X(...)} exists.
///
/// </table>
///
///


// Most major compilers support __has_builtin, notably GCC, MINGW, and CLANG
#if defined(__has_builtin)


// UTILITIES ===========================================================================================================

// addressof is very difficult to implement without intrinsics.
#if __has_builtin(__builtin_addressof)
# define FENNEC_HAS_BUILTIN_ADDRESSOF 1
# define FENNEC_BUILTIN_ADDRESSOF(arg) __builtin_addressof(arg)
#else
# define FENNEC_HAS_BUILTIN_ADDRESSOF 0
#endif

// bitcast is slightly more efficient for build times than using memcpy
#if __has_builtin(__builtin_bit_cast)
# define FENNEC_HAS_BUILTIN_BIT_CAST 1
# define FENNEC_BUILTIN_BIT_CAST(type, arg) __builtin_bit_cast(type, arg)
#else
# define FENNEC_HAS_BUILTIN_BIT_CAST 0
#endif

#if __has_builtin(__builtin_LINE)
# define FENNEC_HAS_BUILTIN_LINE 1
# define FENNEC_BUILTIN_LINE() __builtin_LINE()
#else
# define FENNEC_HAS_BUILTIN_LINE 0
#endif

#if __has_builtin(__builtin_COLUMN)
# define FENNEC_HAS_BUILTIN_COLUMN 1
# define FENNEC_BUILTIN_COLUMN() __builtin_COLUMN()
#else
# define FENNEC_HAS_BUILTIN_COLUMN 0
#endif

#if __has_builtin(__builtin_FILE)
# define FENNEC_HAS_BUILTIN_FILE 1
# define FENNEC_BUILTIN_FILE() __builtin_FILE()
#else
# define FENNEC_HAS_BUILTIN_FILE 0
#endif

#if __has_builtin(__builtin_FUNCTION)
# define FENNEC_HAS_BUILTIN_FUNCTION 1
# define FENNEC_BUILTIN_FUNCTION() __builtin_FUNCTION()
#else
# define FENNEC_HAS_BUILTIN_FUNCTION 0
#endif


// PROPERTIES ==========================================================================================================

// Inconsistent without intrinsics
#if __has_builtin(__is_abstract)
# define FENNEC_HAS_BUILTIN_IS_ABSTRACT 1
# define FENNEC_BUILTIN_IS_ABSTRACT(arg) __is_abstract(arg)
#else
# define FENNEC_HAS_BUILTIN_IS_ABSTRACT 0
#endif

// Inconsistent without intrinsics
#if __has_builtin(__is_array)
# define FENNEC_HAS_BUILTIN_IS_ARRAY 1
# define FENNEC_BUILTIN_IS_ARRAY(arg) __is_array(arg)
#else
# define FENNEC_HAS_BUILTIN_IS_ARRAY
#endif

// Inconsistent without intrinsics
#if __has_builtin(__is_bounded_array)
# define FENNEC_HAS_BUILTIN_IS_BOUNDED_ARRAY 1
# define FENNEC_BUILTIN_IS_BOUNDED_ARRAY(arg) __is_bounded_array(arg)
#else
# define FENNEC_HAS_BUILTIN_IS_BOUNDED_ARRAY
#endif

// Inconsistent without intrinsics
#if __has_builtin(__is_class)
# define FENNEC_HAS_BUILTIN_IS_CLASS 1
# define FENNEC_BUILTIN_IS_CLASS(arg) __is_class(arg)
#else
# define FENNEC_HAS_BUILTIN_IS_CLASS
#endif

// Inconsistent without intrinsics
#if __has_builtin(__is_scoped_enum)
# define FENNEC_HAS_BUILTIN_IS_SCOPED_ENUM 1
# define FENNEC_BUILTIN_IS_SCOPED_ENUM(arg) __is_scoped_enum(arg)
#else
# define FENNEC_HAS_BUILTIN_IS_SCOPED_ENUM
#endif

#if __has_builtin(__is_member_pointer)
# define FENNEC_HAS_BUILTIN_IS_MEMBER_POINTER 1
# define FENNEC_BUILTIN_IS_MEMBER_POINTER(arg) __is_member_pointer(arg)
#else
# define FENNEC_HAS_BUILTIN_IS_MEMBER_POINTER 0
#endif

#if __has_builtin(__is_member_function_pointer)
# define FENNEC_HAS_BUILTIN_IS_MEMBER_FUNCTION_POINTER 1
# define FENNEC_BUILTIN_IS_MEMBER_FUNCTION_POINTER(arg) __is_member_function_pointer(arg)
#else
# define FENNEC_HAS_BUILTIN_IS_MEMBER_FUNCTION_POINTER 0
#endif

#if __has_builtin(__is_member_object_pointer)
# define FENNEC_HAS_BUILTIN_IS_MEMBER_OBJECT_POINTER 1
# define FENNEC_BUILTIN_IS_MEMBER_OBJECT_POINTER(arg) __is_member_object_pointer(arg)
#else
# define FENNEC_HAS_BUILTIN_IS_MEMBER_OBJECT_POINTER 0
#endif


// CONSTRUCTORS ========================================================================================================

// Difficult and Inconsistent without intrinsics
#if __has_builtin(__is_constructible)
# define FENNEC_HAS_BUILTIN_IS_CONSTRUCTIBLE 1
# define FENNEC_BUILTIN_IS_CONSTRUCTIBLE(type, ...) __is_constructible(type __VA_OPT__(,) __VA_ARGS__)
#else
# define FENNEC_HAS_BUILTIN_IS_CONSTRUCTIBLE 0
#endif

// Difficult and Inconsistent without intrinsics
#if __has_builtin(__is_trivially_constructible)
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE 1
# define FENNEC_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE(type, ...) __is_trivially_constructible(type __VA_OPT__(,) __VA_ARGS__)
#else
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE 0
#endif

// Difficult and Inconsistent without intrinsics
#if __has_builtin(__is_nothrow_constructible)
# define FENNEC_HAS_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE 1
# define FENNEC_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE(type, ...) __is_nothrow_constructible(type __VA_OPT__(,) __VA_ARGS__)
#else
# define FENNEC_HAS_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE 0
#endif

// Difficult and Inconsistent without intrinsics
#if __has_builtin(__has_trivial_destructor)
# define FENNEC_HAS_BUILTIN_HAS_TRIVIAL_DESTRUCTOR 1
# define FENNEC_BUILTIN_HAS_TRIVIAL_DESTRUCTOR(type) __has_trivial_destructor(type)
#else
# define FENNEC_HAS_BUILTIN_HAS_TRIVIAL_DESTRUCTOR 0
#endif

// Difficult and Inconsistent without intrinsics
#if __has_builtin(__is_trivially_destructible)
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE 1
# define FENNEC_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE(type) __is_trivially_destructible(type)
#else
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE 0
#endif


// ASSIGNMENTS =========================================================================================================

// Difficult and Inconsistent without intrinsics
#if __has_builtin(__is_assignable)
# define FENNEC_HAS_BUILTIN_IS_ASSIGNABLE 1
# define FENNEC_BUILTIN_IS_ASSIGNABLE(a, b) __is_assignable(a, b)
#else
# define FENNEC_HAS_BUILTIN_IS_ASSIGNABLE 0
#endif

// Difficult and Inconsistent without intrinsics
#if __has_builtin(__is_trivially_assignable)
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_ASSIGNABLE 1
# define FENNEC_BUILTIN_IS_TRIVIALLY_ASSIGNABLE(a, b) __is_trivially_assignable(a, b)
#else
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_ASSIGNABLE 0
#endif

// Difficult and Inconsistent without intrinsics
#if __has_builtin(__is_nothrow_assignable)
# define FENNEC_HAS_BUILTIN_IS_NOTHROW_ASSIGNABLE 1
# define FENNEC_BUILTIN_IS_NOTHROW_ASSIGNABLE(a, b) __is_nothrow_assignable(a, b)
#else
# define FENNEC_HAS_BUILTIN_IS_NOTHROW_ASSIGNABLE 0
#endif

// Inconsistent without intrinsics
#if __has_builtin(__is_trivial)
# define FENNEC_HAS_BUILTIN_IS_TRIVIAL 1
# define FENNEC_BUILTIN_IS_TRIVIAL(a) __is_trivial(a)
#else
# define FENNEC_HAS_BUILTIN_IS_TRIVIAL 0
#endif

// Difficult and Inconsistent without intrinsics
#if __has_builtin(__is_trivially_copyable)
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_COPYABLE 1
# define FENNEC_BUILTIN_IS_TRIVIALLY_COPYABLE(a) __is_trivially_copyable(a)
#else
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_COPYABLE 0
#endif

// Impossible without instrinsics
#if __has_builtin(__is_standard_layout)
# define FENNEC_HAS_BUILTIN_IS_STANDARD_LAYOUT 1
# define FENNEC_BUILTIN_IS_STANDARD_LAYOUT(arg) __is_standard_layout(arg)
#else
# define FENNEC_HAS_BUILTIN_IS_STANDARD_LAYOUT 0
#endif

// Impossible without instrinsics
#if __has_builtin(__has_unique_object_representations)
# define FENNEC_HAS_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS 1
# define FENNEC_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS(arg) __has_unique_object_representations(arg)
#else
# define FENNEC_HAS_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS 0
#endif


// Type Traits
// can_convert is also very difficult to implement without intrinsics
#if __has_builtin(__is_convertible)
# define FENNEC_HAS_BUILTIN_IS_CONVERTIBLE 1
# define FENNEC_BUILTIN_IS_CONVERTIBLE(arg0, arg1) __is_convertible(arg0, arg1)
#else
# define FENNEC_HAS_BUILTIN_IS_CONVERTIBLE 0
#endif

// Inconsistent without intrinsics.
#if __has_builtin(__is_empty)
# define FENNEC_HAS_BUILTIN_IS_EMPTY 1
# define FENNEC_BUILTIN_IS_EMPTY(arg) __is_empty(arg)
#else
# define FENNEC_HAS_BUILTIN_IS_EMPTY 0

// Inconsistent without intrinsics
#if __has_builtin(__is_final)
# define FENNEC_HAS_BUILTIN_IS_FINAL 1
# define FENNEC_BUILTIN_IS_FINAL(arg) __is_final(arg)
#else
# define FENNEC_HAS_BUILTIN_IS_FINAL 0
#endif
#endif

// Inconsistent without intrinsics.
#if __has_builtin(__is_enum)
# define FENNEC_HAS_BUILTIN_IS_ENUM 1
# define FENNEC_BUILTIN_IS_ENUM(arg) __is_enum(arg)
#else
# define FENNEC_HAS_BUILTIN_IS_ENUM 0
#endif

// Inconsistent without intrinsics.
#if __has_builtin(__is_union)
# define FENNEC_HAS_BUILTIN_IS_UNION 1
# define FENNEC_BUILTIN_IS_UNION(arg) __is_union(arg)
#else
# define FENNEC_HAS_BUILTIN_IS_UNION 0
#endif

// Inconsistent without intrinsics
#if __has_builtin(__is_final)
# define FENNEC_HAS_BUILTIN_IS_FINAL 1
# define FENNEC_BUILTIN_IS_FINAL(arg) __is_final(arg)
#else
# define FENNEC_HAS_BUILTIN_IS_FINAL 0
#endif

// Inconsistent without intrinsics
#if __has_builtin(__is_aggregate)
# define FENNEC_HAS_BUILTIN_IS_AGGREGATE 1
# define FENNEC_BUILTIN_IS_AGGREGATE(arg) __is_aggregate(arg)
#else
# define FENNEC_HAS_BUILTIN_IS_AGGREGATE 0
#endif

// Inconsistent without intrinsics
#if __has_builtin(__builtin_is_implicit_lifetime)
# define FENNEC_HAS_BUILTIN_IS_IMPLICIT_LIFETIME 1
# define FENNEC_BUILTIN_IS_IMPLICIT_LIFETIME(arg) __builtin_is_implicit_lifetime(arg)
#else
# define FENNEC_HAS_BUILTIN_IS_IMPLICIT_LIFETIME 0
#endif

// Inconsistent without intrinsics
#if __has_builtin(__is_function)
# define FENNEC_HAS_BUILTIN_IS_FUNCTION 1
# define FENNEC_BUILTIN_IS_FUNCTION(arg) __is_function(arg)
#else
# define FENNEC_HAS_BUILTIN_IS_FUNCTION 0
#endif

// Inconsistent without intrinsics
#if __has_builtin(__is_object)
# define FENNEC_HAS_BUILTIN_IS_OBJECT 1
# define FENNEC_BUILTIN_IS_OBJECT(arg) __is_object(arg)
#else
# define FENNEC_HAS_BUILTIN_IS_FUNCTION 0
#endif

// Inconsistent without intrinsics
#if __has_builtin(__is_polymorphic)
# define FENNEC_HAS_BUILTIN_IS_POLYMORPHIC 1
# define FENNEC_BUILTIN_IS_POLYMORPHIC(arg) __is_polymorphic(arg)
#else
# define FENNEC_HAS_BUILTIN_IS_POLYMORPHIC 0
#endif


// For compilers without or differently named builtins
#else

// TODO: More compiler support

#if FENNEC_COMPILER_MSVC

# define FENNEC_HAS_BUILTIN_ADDRESS_OF 1
# define FENNEC_BUILTIN_ADDRESS_OF(arg) __builtin_addressof(arg)

# define FENNEC_HAS_BUILTIN_BIT_CAST 1
# define FENNEC_BUILTIN_BIT_CAST(type, arg) __builtin_bit_cast(type, arg)

# define FENNEC_HAS_BUILTIN_IS_CONVERTIBLE 1
# define FENNEC_BUILTIN_IS_CONVERTIBLE(arg0, arg1) __is_convertible_to(arg0, arg1)

# define FENNEC_HAS_BUILTIN_IS_CONSTRUCTIBLE 1
# define FENNEC_BUILTIN_IS_CONSTRUCTIBLE(...) __is_constructible(__VA_ARGS__)

# define FENNEC_HAS_BUILTIN_IS_EMPTY 1
# define FENNEC_BUILTIN_IS_EMPTY(arg) __is_empty(arg)

# define FENNEC_HAS_BUILTIN_IS_FINAL 1
# define FENNEC_BUILTIN_IS_FINAL(arg) __is_final(arg)

# define FENNEC_HAS_BUILTIN_IS_POLYMORPHIC 1
# define FENNEC_BUILTIN_IS_POLYMORPHIC(arg) __is_polymorphic(arg)

# define FENNEC_HAS_BUILTIN_IS_STANDARD_LAYOUT 1
# define FENNEC_BUILTIN_IS_STANDARD_LAYOUT(arg) __is_standard_layout(arg)

#endif

#endif

#endif // FENNEC_LANG_INTRINSICS_H
