#pragma once

#include "CoreTypes.h"
#include "Concepts/Common.h"
#include "TypeTraits/TypeTraits.h"
#include "Concepts/BooleanTestable.h"

NAMESPACE_REDCRAFT_BEGIN
NAMESPACE_MODULE_BEGIN(Redcraft)
NAMESPACE_MODULE_BEGIN(Utility)

template <typename T, typename U>
concept CWeaklyEqualityComparableWith =
	requires(const TRemoveReference<T>::Type & A, const TRemoveReference<U>::Type & B)
	{
		{ A == B } -> CBooleanTestable;
		{ A != B } -> CBooleanTestable;
		{ B == A } -> CBooleanTestable;
		{ B != A } -> CBooleanTestable;
	};

template <typename T>
concept CEqualityComparable = CWeaklyEqualityComparableWith<T, T>;

template <typename T, typename U>
concept CEqualityComparableWith =
	CEqualityComparable<T> &&
	CEqualityComparable<U> &&
	CWeaklyEqualityComparableWith<T, U> &&
	CCommonReferenceWith<const typename TRemoveReference<T>::Type&, const typename TRemoveReference<U>::Type&> &&
	CEqualityComparable<typename TCommonReference<const typename TRemoveReference<T>::Type&,  const typename TRemoveReference<U>::Type&>::Type>;

template <typename T, typename U>
concept CPartiallyOrderedWith =
	requires(const TRemoveReference<T>::Type& A, const TRemoveReference<U>::Type& B)
	{
		{ A <  B } -> CBooleanTestable;
		{ A >  B } -> CBooleanTestable;
		{ A <= B } -> CBooleanTestable;
		{ A >= B } -> CBooleanTestable;
		{ B <  A } -> CBooleanTestable;
		{ B >  A } -> CBooleanTestable;
		{ B <= A } -> CBooleanTestable;
		{ B >= A } -> CBooleanTestable;
	};

template <typename T>
concept CTotallyOrdered = CEqualityComparable<T> && CPartiallyOrderedWith<T, T>;

template <typename T, typename U>
concept CTotallyOrderedWith =
	CTotallyOrdered<T> && CTotallyOrdered<U> &&
	CPartiallyOrderedWith<T, U> &&
	CEqualityComparableWith<T, U> &&
	CTotallyOrdered<typename TCommonReference<const typename TRemoveReference<T>::Type&, const typename TRemoveReference<U>::Type&>::Type>;

NAMESPACE_MODULE_END(Utility)
NAMESPACE_MODULE_END(Redcraft)
NAMESPACE_REDCRAFT_END