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

#ifndef FENNEC_LANG_TYPE_OPERATORS_H
#define FENNEC_LANG_TYPE_OPERATORS_H

namespace fennec
{

// https://stackoverflow.com/questions/6534041/how-to-check-whether-operator-exists

// has_equals ==========================================================================================================

///
/// \brief Checks if a type has a defined equality operator
/// \tparam T0 The first type
/// \tparam T1 The second type
template<typename T0, typename T1 = T0>
struct has_equals {
private:
	// Use SFINAE to check for the operator
	template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() == declval<V>());
	template<typename, typename>     static auto test(...) -> false_type;

public:
	static constexpr bool value = is_same_v<bool, decltype(test<T0, T1>(0))>; //!< The result of the check
};

template<typename T0, typename T1 = T0> constexpr bool has_equals_v = has_equals<T0, T1>::value;

// has_nequals =========================================================================================================

///
/// \brief Checks if a type has a defined inequality operator
/// \tparam T0 The first type
/// \tparam T1 The second type
template<typename T0, typename T1 = T0>
struct has_nequals {
private:
	// Use SFINAE to check for the operator
	template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() != declval<V>());
	template<typename, typename>     static auto test(...) -> false_type;

public:
	static constexpr bool value = is_same_v<bool, decltype(test<T0, T1>(0))>; //!< The result of the check
};

template<typename T0, typename T1 = T0> constexpr bool has_nequals_v = has_nequals<T0, T1>::value;


// has_less ============================================================================================================

///
/// \brief Checks if a type has a defined less operator
/// \tparam T0 The first type
/// \tparam T1 The second type
template<typename T0, typename T1 = T0>
struct has_less {
private:
	// Use SFINAE to check for the operator
	template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() < declval<V>());
	template<typename, typename>     static auto test(...) -> false_type;

public:
	static constexpr bool value = is_same_v<bool, decltype(test<T0, T1>(0))>; //!< The result of the check
};

template<typename T0, typename T1 = T0> constexpr bool has_less_v = has_less<T0, T1>::value;


// has_less_equals =====================================================================================================

///
/// \brief Checks if a type has a defined less equals operator
/// \tparam T0 The first type
/// \tparam T1 The second type
template<typename T0, typename T1 = T0>
struct has_less_equals {
private:
	// Use SFINAE to check for the operator
	template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() <= declval<V>());
	template<typename, typename>     static auto test(...) -> false_type;

public:
	static constexpr bool value = is_same_v<bool, decltype(test<T0, T1>(0))>; //!< The result of the check
};

template<typename T0, typename T1 = T0> constexpr bool has_less_equals_v = has_less_equals<T0, T1>::value;


// has_greater =========================================================================================================

///
/// \brief Checks if a type has a defined greater operator
/// \tparam T0 The first type
/// \tparam T1 The second type
template<typename T0, typename T1 = T0>
struct has_greater {
private:
	// Use SFINAE to check for the operator
	template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() > declval<V>());
	template<typename, typename>     static auto test(...) -> false_type;

public:
	static constexpr bool value = is_same_v<bool, decltype(test<T0, T1>(0))>; //!< The result of the check
};

template<typename T0, typename T1 = T0> constexpr bool has_greater_v = has_greater<T0, T1>::value;


// has_greater_equals ==================================================================================================

///
/// \brief Checks if a type has a defined greater equals operator
/// \tparam T0 The first type
/// \tparam T1 The second type
template<typename T0, typename T1 = T0>
struct has_greater_equals {
private:
	// Use SFINAE to check for the operator
	template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() >= declval<V>());
	template<typename, typename>     static auto test(...) -> false_type;

public:
	static constexpr bool value = is_same_v<bool, decltype(test<T0, T1>(0))>; //!< The result of the check
};

template<typename T0, typename T1 = T0> constexpr bool has_greater_equals_v = has_greater_equals<T0, T1>::value;


}

#endif // FENNEC_LANG_TYPE_OPERATORS_H
