#pragma once

#include "CoreTypes.h"
#include "Templates/Utility.h"
#include "TypeTraits/TypeTraits.h"

NAMESPACE_REDCRAFT_BEGIN
NAMESPACE_MODULE_BEGIN(Redcraft)
NAMESPACE_MODULE_BEGIN(Utility)

NAMESPACE_PRIVATE_BEGIN

template <typename T> using TWithReference = T&;

template <typename I> struct TIteratorElementImpl     {                            };
template <typename T> struct TIteratorElementImpl<T*> { using Type = TRemoveCV<T>; };

template <typename I> requires (requires { typename I::ElementType; })
struct TIteratorElementImpl<I> { using Type = typename I::ElementType; };

template <typename I> struct TIteratorPointerImpl     {                  };
template <typename T> struct TIteratorPointerImpl<T*> { using Type = T*; };

template <typename I> requires (requires(I& Iter) { { Iter.operator->() } -> CPointer; })
struct TIteratorPointerImpl<I> { using Type = decltype(DeclVal<I&>().operator->()); };

NAMESPACE_PRIVATE_END

template <typename T>
concept CReferenceable = requires { typename NAMESPACE_PRIVATE::TWithReference<T>; };

template <typename T>
concept CDereferenceable = requires(T& A) { { *A } -> CReferenceable; };

template <typename I>
using TIteratorElement = typename NAMESPACE_PRIVATE::TIteratorElementImpl<TRemoveCVRef<I>>::Type;

template <typename I>
using TIteratorPointer = typename NAMESPACE_PRIVATE::TIteratorPointerImpl<TRemoveCVRef<I>>::Type;

template <CReferenceable I>
using TIteratorReference = decltype(*DeclVal<I&>());

template <CReferenceable I> requires (requires(I& Iter) { { MoveTemp(*Iter) } -> CReferenceable; })
using TIteratorRValueReference = decltype(MoveTemp(*DeclVal<I&>()));

NAMESPACE_PRIVATE_BEGIN

template <typename I>
concept CIndirectlyReadable =
	requires(const I Iter)
	{
		typename TIteratorElement<I>;
		typename TIteratorReference<I>;
		typename TIteratorRValueReference<I>;
		{          *Iter  } -> CSameAs<TIteratorReference<I>>;
		{ MoveTemp(*Iter) } -> CSameAs<TIteratorRValueReference<I>>;
	}
	&& CSameAs<TIteratorElement<I>, TRemoveCVRef<TIteratorElement<I>>>
	&& CCommonReference<TIteratorReference<I>&&, TIteratorElement<I>&>
	&& CCommonReference<TIteratorReference<I>&&, TIteratorRValueReference<I>&&>
	&& CCommonReference<TIteratorRValueReference<I>&&, const TIteratorElement<I>&>;

NAMESPACE_PRIVATE_END

/** A concept specifies a type is indirectly readable by expression '*Iter'. */
template <typename I>
concept CIndirectlyReadable = NAMESPACE_PRIVATE::CIndirectlyReadable<TRemoveCVRef<I>>;

/** This is an example of an indirectly readable type, indicate the traits that define an indirectly readable type. */
template <CReferenceable T>
struct IIndirectlyReadable
{
	/**
	 * The element type of the indirectly readable type.
	 * It must be a non-const, non-volatile and non-reference type and can be referenced, i.e. not a void type.
	 */
	using ElementType = TRemoveCVRef<T>;

	/**
	 * Indirectly read the element from the indirectly readable type.
	 * The return type may not be const ElementType&, this concept only requires that the return type
	 * and ElementType has some relationship, such as copy constructible to ElementType if the type is copyable.
	 * This means that returning a proxy class castable to ElementType is also valid.
	 * If this is an iterator adaptor, use decltype(auto) to forward the return value.
	 */
	T operator*() const;
};

// Use IIndirectlyReadable<int> represents an indirectly readable type and int is the regular element type.
static_assert(CIndirectlyReadable<IIndirectlyReadable<int>> && CRegular<int>);

// The CIndirectlyReadable requires this code is valid.
static_assert(
	requires(IIndirectlyReadable<int> Iter, int& A)
	{
		A = *Iter;
	}
);

/** A concept specifies a type is indirectly writable by expression '*Iter = A'. */
template <typename I, typename T>
concept CIndirectlyWritable =
	requires(I&& Iter, T&& A)
	{
		*Iter             = Forward<T>(A);
		*Forward<I>(Iter) = Forward<T>(A);
		const_cast<const TIteratorReference<I>&&>(*Iter)             = Forward<T>(A);
		const_cast<const TIteratorReference<I>&&>(*Forward<I>(Iter)) = Forward<T>(A);
	};

/** This is an example of an indirectly writable type, indicate the traits that define an indirectly writable type. */
template <CReferenceable T>
struct IIndirectlyWritable
{
	/**
	 * Indirectly write the element from the indirectly writable type.
	 * The return type may not be T&, this concept only requires that the return type and T has some relationship,
	 * such as can be assigned from T& if the type is copyable or T&& if the type is movable.
	 * This means that returning a proxy class can be assigned from T is also valid.
	 * If this is also an indirectly readable type, the equivalent value is read after writing.
	 * If this is an iterator adaptor, use decltype(auto) to forward the return value.
	 */
	T operator*() const;
};

// Use IIndirectlyWritable<int> represents an indirectly writable type and int is the regular element type.
static_assert(CIndirectlyWritable<IIndirectlyWritable<int&>, int> && CRegular<int>);

// The CIndirectlyWritable requires this code is valid.
static_assert(
	requires(IIndirectlyWritable<int&> Iter, int& A)
	{
		*Iter = A;
	}
);

/** A concept specifies a type is incrementable by expression '++Iter' and the type must be movable. */
template <typename I>
concept CWeaklyIncrementable = CMovable<I>
	&& requires(I Iter) { { ++Iter } -> CSameAs<I&>; Iter++; };

/** This is an example of a weakly incrementable type, indicate the traits that define a weakly incrementable type. */
struct IWeaklyIncrementable
{
	IWeaklyIncrementable(IWeaklyIncrementable&&);
	IWeaklyIncrementable* operator=(IWeaklyIncrementable&&);

	IWeaklyIncrementable& operator++();

	/** Post-increment operator. Specify, the concept not requires the return type is any specific type, so the return type can be void. */
	void operator++(int);
};

// Use IWeaklyIncrementable represents a weakly incrementable type.
static_assert(CWeaklyIncrementable<IWeaklyIncrementable>);

/**
 * A concept specifies a type is incrementable by expression 'Iter++' and the expression returns the original value.
 * In addition, the type must be default constructible, copyable and weakly equality comparable.
 */
template <typename I>
concept CIncrementable = CRegular<I> && CWeaklyIncrementable<I>
	&& requires(I Iter) { { Iter++ } -> CSameAs<I>; };

/**
 * This is an example of an incrementable type, indicate the traits that define an incrementable type.
 * The copy object of this type produced by copy constructor, copy assignment or post-increment
 * should produce the same effect as the original object when incrementing.
 */
struct IIncrementable /* : IWeaklyIncrementable */
{
	IIncrementable();
	IIncrementable(const IIncrementable&);
	IIncrementable(IIncrementable&&);                 // Also satisfies IWeaklyIncrementable.
	IIncrementable* operator=(const IIncrementable&);
	IIncrementable* operator=(IIncrementable&&);      // Also satisfies IWeaklyIncrementable.

	friend bool operator==(const IIncrementable&, const IIncrementable&);

	IIncrementable& operator++(); // Also satisfies IWeaklyIncrementable.

	/** Post-increment operator. Specify, the concept requires the return value is the original value before incrementing. */
	IIncrementable operator++(int);
};

// Use IIncrementable represents an incrementable type.
static_assert(CIncrementable<IIncrementable>);

/**
 * A concept specifies a type is potentially an iterator. It only requires weakly incrementable and dereferenceable.
 * This concept should only be used in scenarios where the specific type of the iterator is not important, such as iterator adapters.
 */
template <typename I>
concept CInputOrOutputIterator = CWeaklyIncrementable<I>
	&& requires(I Iter) { { *Iter } -> CReferenceable; };

/** This is an example of an input or output iterator, indicate the traits that define an input or output iterator. */
template <CReferenceable T>
struct IInputOrOutputIterator /* : IWeaklyIncrementable */
{
	// ~Begin CWeklyIncrementable.

	IInputOrOutputIterator(IInputOrOutputIterator&&);
	IInputOrOutputIterator* operator=(IInputOrOutputIterator&&);

	IInputOrOutputIterator& operator++();

	void operator++(int);

	// ~End CWeklyIncrementable.

	/** Dereference operator. It does not matter what the return type is, as long as it is referenceable. */
	T operator*() const;
};

// Use IInputOrOutputIterator represents an input or output iterator.
static_assert(CInputOrOutputIterator<IInputOrOutputIterator<int>>);

/** A concept specifies a type is an input iterator. */
template <typename I>
concept CInputIterator = CInputOrOutputIterator<I> && CIndirectlyReadable<I>;

/** This is an example of an input iterator, indicate the traits that define an input iterator. */
template <CReferenceable T>
struct IInputIterator /* : IInputOrOutputIterator, IIndirectlyReadable */
{
	// ~Begin CIndirectlyReadable.

	using ElementType = TRemoveCVRef<T>;

	// ~End CIndirectlyReadable.

	// ~Begin CInputOrOutputIterator.

	IInputIterator(IInputIterator&&);
	IInputIterator* operator=(IInputIterator&&);

	T operator*() const; // Also satisfies CIndirectlyReadable.

	IInputIterator& operator++();

	void operator++(int);

	// ~End CInputOrOutputIterator.
};

// Use IInputIterator<int> represents an input iterator and int is the regular element type.
static_assert(CInputIterator<IInputIterator<int>> && CRegular<int>);

// The CInputIterator requires this code is valid.
static_assert(
	requires(IInputIterator<int> Iter, int& A)
	{
		++Iter;
		Iter++;
		A = *++Iter;
		A =   *Iter;
	}
);

/** A concept specifies a type is an output iterator and expression '*Iter++ = A' is valid to write the element. */
template <typename I, typename T>
concept COutputIterator = CInputOrOutputIterator<I> && CIndirectlyWritable<I, T>
	&& requires(I Iter) { { Iter++ } -> CIndirectlyWritable<T>; };

/** This is an example of an output iterator, indicate the traits that define an output iterator. */
template <CReferenceable T>
struct IOutputIterator /* : IInputOrOutputIterator, IIndirectlyWritable<T> */
{
	// ~Begin CIndirectlyWritable.

	IOutputIterator(IOutputIterator&&);
	IOutputIterator* operator=(IOutputIterator&&);

	T operator*() const; // Also satisfies CIndirectlyWritable.

	IOutputIterator& operator++();

	/**
	 * Post-increment operator.
	 * Specify, the concept not requires the return type is self type,
	 * but requires the expression '*Iter++ = A;' is equivalent to '*Iter = A; ++Iter;'.
	 * This means that returning a proxy class that satisfies CIndirectlyWritable<T> is also valid.
	 * Also satisfies CIndirectlyWritable.
	 */
	IIndirectlyWritable<T> operator++(int);

	// ~End CIndirectlyWritable.
};

// Use IOutputIterator<int> represents an output iterator and int is the regular element type.
static_assert(COutputIterator<IOutputIterator<int&>, int> && CRegular<int>);

// The CInputIterator requires this code is valid.
static_assert(
	requires(IOutputIterator<int&> Iter, int& A)
	{
		++Iter;
		Iter++;
		*++Iter = A;
		*Iter++ = A;
		  *Iter = A;
	}
);

#define ENABLE_RANGE_BASED_FOR_LOOP_SUPPORT public:                        \
	NODISCARD FORCEINLINE constexpr auto begin()       { return Begin(); } \
	NODISCARD FORCEINLINE constexpr auto begin() const { return Begin(); } \
	NODISCARD FORCEINLINE constexpr auto end()         { return End();   } \
	NODISCARD FORCEINLINE constexpr auto end()   const { return End();   }

NAMESPACE_MODULE_END(Utility)
NAMESPACE_MODULE_END(Redcraft)
NAMESPACE_REDCRAFT_END