/* Copyright 2022, Contributors To LensorOS.
* All rights reserved.
*
* This file is part of LensorOS.
*
* LensorOS 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.
*
* LensorOS 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 LensorOS. If not, see <https://www.gnu.org/licenses/>.
 */

#ifndef _LENSOR_OS_TYPE_TRAITS
#define _LENSOR_OS_TYPE_TRAITS

namespace std {
/// Compile-time integer.
template<typename _T, _T __v>
struct integral_constant
{
    using value_type = _T;
    using type = integral_constant<_T, __v>;

    static constexpr _T value = __v;

    constexpr operator value_type() const noexcept { return value; }
    constexpr value_type operator()() const noexcept { return value; }
};

/// Compile-time true.
using true_type =  integral_constant<bool, true>;

/// Compile-time false.
using false_type =  integral_constant<bool, false>;

/// Compile-time type identity.
template<typename _T> struct type_identity { using type = _T; };

/// Convert a bool to true_type or false_type.
template <bool _Cond>
struct bool_constant : integral_constant<bool, _Cond> {};

/// Select `_True` if `_Cond` is true, otherwise select `_Else`.
template <bool _Cond, typename _True, typename _Else>
struct conditional;

template <typename _True, typename _Else>
struct conditional<true, _True, _Else> { using type = _True; };

template <typename _True, typename _Else>
struct conditional<false, _True, _Else> { using type = _Else; };

/// Abbreviation for `conditional`
template <bool _Cond, typename _True, typename _Else>
struct _If : conditional<_Cond, _True, _Else> {};

template <bool _Cond, typename _True, typename _Else>
using _If_t = typename _If<_Cond, _True, _Else>::type;

/// True if multiple conditions are true.
template <typename...> struct _And;
template <> struct _And<> : true_type {};
template <typename __1> struct _And<__1> : __1 {};

template <typename __1, typename __2>
struct _And<__1, __2> : _If<__1::value, __2, __1> {};

template <typename __1, typename __2, typename __3, typename ..._N>
struct _And<__1, __2, __3, _N...> : _If<__1::value, _And<__2, __3, _N...>, __1> {};

/// True if any condition is true.
template <typename...> struct _Or;
template <> struct _Or<> : false_type {};
template <typename __1> struct _Or<__1> : __1 {};

template <typename __1, typename __2>
struct _Or<__1, __2> : _If<__1::value, __1, __2> {};

template <typename __1, typename __2, typename __3, typename ..._N>
struct _Or<__1, __2, __3, _N...> : _If<__1::value, __1, _Or<__2, __3, _N...>> {};

/// True if a condition is not true.
template <typename _T> struct _Not : false_type {};
template <> struct _Not<false_type> : true_type {};

/// Check if two types are the same.
template <typename _T, typename _U>
struct is_same : false_type {};

/// A type is always equal to itself.
template <typename _T>
struct is_same<_T, _T> : true_type {};

/// Check if a type is a pointer.
template <typename _T> struct is_pointer : false_type {};
template <typename _T> struct is_pointer<_T*> : true_type {};

/// Check if a type is a signed integer.
template <typename _T> struct is_signed : false_type {};
template <> struct is_signed<char> : true_type {};
template <> struct is_signed<signed char> : true_type {};
template <> struct is_signed<signed short> : true_type {};
template <> struct is_signed<signed int> : true_type {};
template <> struct is_signed<signed long> : true_type {};
template <> struct is_signed<signed long long> : true_type {};

/// Check if a type is an unsigned integer.
template <typename _T> struct is_unsigned : false_type {};
template <> struct is_unsigned<unsigned char> : true_type {};
template <> struct is_unsigned<unsigned short> : true_type {};
template <> struct is_unsigned<unsigned int> : true_type {};
template <> struct is_unsigned<unsigned long> : true_type {};
template <> struct is_unsigned<unsigned long long> : true_type {};

/// Check if a type is an integer.
template <typename _T>
struct is_integral : _Or<is_signed<_T>, is_unsigned<_T>>::type {};

/// Check if a type is a floating-point number.
template <typename _T> struct is_floating_point : false_type {};
template <> struct is_floating_point<float> : true_type {};
template <> struct is_floating_point<double> : true_type {};
template <> struct is_floating_point<long double> : true_type {};

/// Check if a type is a number.
template <typename _T>
struct _Number : _Or<is_integral<_T>, is_floating_point<_T>> {};

/// Check if a type is a pointer.
template <typename _T>
struct _Pointer : is_pointer<_T> {};

/// Remove a reference from a type.
template <typename _T> struct remove_reference { using type = _T; };
template <typename _T> struct remove_reference<_T&> { using type = _T; };
template <typename _T> struct remove_reference<_T&&> { using type = _T; };

/// Remove const from a type.
template <typename _T> struct remove_const { using type = _T; };
template <typename _T> struct remove_const<const _T> { using type = _T; };

/// Remove volatile from a type.
template <typename _T> struct remove_volatile { using type = _T; };
template <typename _T> struct remove_volatile<volatile _T> { using type = _T; };

/// Remove a pointer from a type.
template <typename _T> struct remove_pointer { using type = _T; };
template <typename _T> struct remove_pointer<_T*> { using type = _T; };

/// Remove const and volatile from a type.
template <typename _T> struct remove_cv {
    using type = typename remove_volatile<typename remove_const<_T>::type>::type;
};

/// Remove reference and cv from a type.
template <typename _T> struct remove_cvref {
    using type = typename remove_cv<typename remove_reference<_T>::type>::type;
};


/// Check if two types are the same, ignoring cv-qualifiers.
template <typename _T, typename _U>
struct _Is : is_same<typename remove_cv<_T>::type, typename remove_cv<_U>::type> {};


/// True if _T is a reference.
template <typename _T> struct is_reference : false_type {};
template <typename _T> struct is_reference<_T&> : true_type {};
template <typename _T> struct is_reference<_T&&> : true_type {};

/// True if _T is an lvalue reference.
template <typename _T> struct is_lvalue_reference : false_type {};
template <typename _T> struct is_lvalue_reference<_T&> : true_type {};

/// Add a reference to a type.
template <typename _T> struct add_lvalue_reference {
    using type = typename remove_reference<_T>::type&;
};

template <typename _T> struct add_rvalue_reference {
    using type = typename remove_reference<_T>::type&&;
};

/// Check if something is void.
template <typename _T> struct is_void : false_type {};
template <> struct is_void<void> : true_type {};

/// Check if something is a function.
template <typename _T> struct is_function : false_type {};
template <typename _R, typename ..._A> struct is_function<remove_cv<_R(_A...)>> : true_type {};
template <typename _R, typename ..._A> struct is_function<remove_cv<_R(_A..., ...)>> : true_type {};

/// Check if something is a reference.
template <typename _T> struct _IsRef : is_reference<_T> {};

/// Check if something is NOT a reference.
template <typename _T> struct _NotRef : _Not<_IsRef<_T>> {};

/// Check whether a type is trivially copyable.
template <typename _T> struct is_trivially_copyable : bool_constant<__has_trivial_copy(_T)> {};

/// Check whether a type is trivially destructible.
template <typename _T> struct is_trivially_destructible : bool_constant<__has_trivial_destructor(_T)> {};

/// Check whether a type is an object, i.e. not a function, reference, or void.
template <typename _T> struct is_object : _And<
    _Not<typename is_function<_T>::type>,
    _Not<typename is_reference<_T>::type>,
    _Not<typename is_void<_T>::type>
>::type::type {};

/// Check if a type is convertible to another type.
namespace __detail {
template <typename _T>
auto __returnable(int) -> decltype(void(static_cast<_T (*)()>(nullptr)), std::true_type{});

template <typename>
auto __returnable(...) -> std::false_type;

template <typename> constexpr bool __type_traits_false = false;
template <typename _T>
typename add_rvalue_reference<_T>::type __type_traits_declval() noexcept { static_assert(__type_traits_false<_T>); }

template <typename _From, typename _To>
auto __implicitly_convertible(int) ->
    decltype(void(__type_traits_declval<void (&)(_To)>()
                 (__type_traits_declval<_From>())),
             std::true_type{});

template <typename, typename>
auto __implicitly_convertible(...) -> std::false_type;
} // namespace __detail

template <typename _From, typename _To>
struct is_convertible : integral_constant<bool,
    (decltype(__detail::__returnable<_To>(0))::value &&
     decltype(__detail::__implicitly_convertible<_From, _To>(0))::value) ||
    (is_void<_From>::value &&
     is_void<_To>::value)> {};

/// Struct that has a member `type` iff `_Cond` is true.
template <bool, typename _T = void>
struct enable_if {};

template <typename _T>
struct enable_if<true, _T> { using type = _T; };

/// SFINAE helper.
template <typename... _Conditions>
struct _Requires : enable_if<_And<_Conditions...>::value> {};

/// Abbreviations.
template <typename _T, typename _U> constexpr inline bool is_same_v = is_same<_T, _U>::value;
template <typename _T> constexpr inline bool is_reference_v = is_reference<_T>::value;
template <typename _T> constexpr inline bool is_lvalue_reference_v = is_lvalue_reference<_T>::value;
template <typename _T> constexpr inline bool is_pointer_v = is_pointer<_T>::value;
template <typename _T> constexpr inline bool is_signed_v = is_signed<_T>::value;
template <typename _T> constexpr inline bool is_unsigned_v = is_unsigned<_T>::value;
template <typename _T> constexpr inline bool is_integral_v = is_integral<_T>::value;
template <typename _T> constexpr inline bool is_floating_point_v = is_floating_point<_T>::value;
template <typename _T> constexpr inline bool is_trivially_copyable_v = is_trivially_copyable<_T>::value;
template <typename _T> constexpr inline bool is_trivially_destructible_v = is_trivially_destructible<_T>::value;
template <typename _T> constexpr inline bool is_object_v = is_object<_T>::value;
template <typename _T> constexpr inline bool is_void_v = is_void<_T>::value;
template <typename _T> constexpr inline bool is_function_v = is_function<_T>::value;
template <typename _From, typename _To> constexpr inline bool is_convertible_v = is_convertible<_From, _To>::value;

template <typename _T> using type_identity_t = typename type_identity<_T>::type;
template <typename _T> using remove_reference_t = typename remove_reference<_T>::type;
template <typename _T> using remove_const_t = typename remove_const<_T>::type;
template <typename _T> using remove_volatile_t = typename remove_volatile<_T>::type;
template <typename _T> using remove_cv_t = typename remove_cv<_T>::type;
template <typename _T> using remove_pointer_t = typename remove_pointer<_T>::type;
template <typename _T> using remove_cvref_t = typename remove_cvref<_T>::type;
template <typename _T> using add_lvalue_reference_t = typename add_lvalue_reference<_T>::type;
template <typename _T> using add_rvalue_reference_t = typename add_rvalue_reference<_T>::type;

template <bool _Cond, typename _True, typename _Else>
using conditional_t = typename conditional<_Cond, _True, _Else>::type;

template <bool _Cond, typename _T>
using enable_if_t = typename enable_if<_Cond, _T>::type;

constexpr inline bool is_constant_evaluated() noexcept { return __builtin_is_constant_evaluated(); }

} // namespace std

#endif // _LENSOR_OS_TYPE_TRAITS

