#pragma once

#include "CoreTypes.h"
#include "Memory/Address.h"
#include "Iterator/Utility.h"
#include "Iterator/Sentinel.h"
#include "Miscellaneous/Compare.h"
#include "TypeTraits/TypeTraits.h"

NAMESPACE_REDCRAFT_BEGIN
NAMESPACE_MODULE_BEGIN(Redcraft)
NAMESPACE_MODULE_BEGIN(Utility)

#if PLATFORM_COMPILER_GCC
#	pragma GCC diagnostic push
#	pragma GCC diagnostic ignored "-Wnon-template-friend"
#endif

/** A concept specifies a type is an input iterator. It is input iterator, incrementable, and sentinel for itself. */
template <typename I>
concept CForwardIterator = CInputIterator<I> && CIncrementable<I> && CSentinelFor<I, I>;

/** This is an example of a forward iterator, indicate the traits that define a forward iterator. */
template <CReferenceable T>
struct IForwardIterator /* : IInputIterator<T>, IIncrementable, ISentinelFor<IForwardIterator> */
{
	// ~Begin CInputIterator.

	using FElementType = TRemoveCVRef<T>;

	// ~End CInputIterator.

	// ~Begin CIncrementable and CSentinelFor<IForwardIterator>.

	IForwardIterator();
	IForwardIterator(const IForwardIterator&);
	IForwardIterator(IForwardIterator&&);                 // Also satisfies IInputIterator.
	IForwardIterator* operator=(const IForwardIterator&);
	IForwardIterator* operator=(IForwardIterator&&);      // Also satisfies IInputIterator.

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

	// ~End CIncrementable and CSentinelFor<IForwardIterator>.

	// ~Begin CInputIterator.

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

	IForwardIterator& operator++(); // Also satisfies CIncrementable.

	IForwardIterator operator++(int); // Also satisfies CIncrementable.

	// ~End CInputIterator.
};

// Use IForwardIterator<int> represents a forward iterator.
static_assert(CForwardIterator<IForwardIterator<int&>>);
static_assert( COutputIterator<IForwardIterator<int&>, int>);

/** A concept specifies a type is a bidirectional iterator. Add the decrement operator to the forward iterator. */
template <typename I>
concept CBidirectionalIterator = CForwardIterator<I>
	&& requires(I Iter) {
		{ --Iter } -> CSameAs<I&>;
		{ Iter-- } -> CSameAs<I >;
	};

/**
 * This is an example of a bidirectional iterator, indicate the traits that define a bidirectional iterator.
 * Regardless of the order in which the increment and decrement operators are applied,
 * the result is always the same if both operations are performed the same number of times.
 */
template <CReferenceable T>
struct IBidirectionalIterator /* : IForwardIterator<T> */
{
	// ~Begin CForwardIterator.

	using FElementType = TRemoveCVRef<T>;

	IBidirectionalIterator();
	IBidirectionalIterator(const IBidirectionalIterator&);
	IBidirectionalIterator(IBidirectionalIterator&&);
	IBidirectionalIterator* operator=(const IBidirectionalIterator&);
	IBidirectionalIterator* operator=(IBidirectionalIterator&&);

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

	T operator*() const;

	// ~End CForwardIterator.

	IBidirectionalIterator& operator++(); // Also satisfies CForwardIterator.
	IBidirectionalIterator& operator--();

	IBidirectionalIterator operator++(int); // Also satisfies CForwardIterator.
	IBidirectionalIterator operator--(int);
};

// Use IBidirectionalIterator<int> represents a bidirectional iterator.
static_assert(CBidirectionalIterator<IBidirectionalIterator<int&>>);
static_assert(       COutputIterator<IBidirectionalIterator<int&>, int>);

/**
 * A concept specifies a type is a random access iterator.
 * Add the three-way comparison, addition, subtraction and subscript operators to the bidirectional iterator.
 */
template <typename I>
concept CRandomAccessIterator = CBidirectionalIterator<I> && CTotallyOrdered<I> && CSizedSentinelFor<I, I>
	&& requires(I Iter, const I Jter, const ptrdiff N) {
		{ Iter   += N } -> CSameAs<I&>;
		{ Jter   +  N } -> CSameAs<I >;
		{ N +  Jter   } -> CSameAs<I >;
		{ Iter   -= N } -> CSameAs<I&>;
		{ Jter   -  N } -> CSameAs<I >;
		{   Jter[N]   } -> CSameAs<TIteratorReference<I>>;
	};

/** This is an example of a random access iterator, indicate the traits that define a random access iterator. */
template <CReferenceable T>
struct IRandomAccessIterator /* : IBidirectionalIterator<T>, ISizedSentinelFor<IRandomAccessIterator> */
{
	// ~Begin CBidirectionalIterator.

	using FElementType = TRemoveCVRef<T>;

	// ~End CBidirectionalIterator.

	// ~Begin CBidirectionalIterator and CSizedSentinelFor<IRandomAccessIterator>.

	IRandomAccessIterator();
	IRandomAccessIterator(const IRandomAccessIterator&);
	IRandomAccessIterator(IRandomAccessIterator&&);
	IRandomAccessIterator* operator=(const IRandomAccessIterator&);
	IRandomAccessIterator* operator=(IRandomAccessIterator&&);

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

	// ~End CBidirectionalIterator and CSizedSentinelFor<IRandomAccessIterator>.

	friend strong_ordering operator<=>(const IRandomAccessIterator&, const IRandomAccessIterator&);

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

	T operator[](ptrdiff) const;

	// ~Begin CBidirectionalIterator.

	IRandomAccessIterator& operator++();
	IRandomAccessIterator& operator--();

	IRandomAccessIterator operator++(int);
	IRandomAccessIterator operator--(int);

	// ~End CBidirectionalIterator.

	IRandomAccessIterator& operator+=(ptrdiff);
	IRandomAccessIterator& operator-=(ptrdiff);

	IRandomAccessIterator operator+(ptrdiff) const;
	IRandomAccessIterator operator-(ptrdiff) const;

	friend IRandomAccessIterator operator+(ptrdiff, const IRandomAccessIterator&);

	friend ptrdiff operator-(const IRandomAccessIterator&, const IRandomAccessIterator&); // Also satisfies CSizedSentinelFor<IRandomAccessIterator>.
};

// Use IRandomAccessIterator<int> represents a random access iterator
static_assert(CRandomAccessIterator<IRandomAccessIterator<int&>>);
static_assert(      COutputIterator<IRandomAccessIterator<int&>, int>);

/**
 * A concept specifies a type is a contiguous iterator.
 * Add the operator-> to the random access iterator and requires the operator* returns a true reference type.
 */
template <typename I>
concept CContiguousIterator = CRandomAccessIterator<I> && CLValueReference<TIteratorReference<I>>
	&& CSameAs<TIteratorElement<I>, TRemoveCVRef<TIteratorReference<I>>>
	&& CSameAs<TIteratorPointer<I>,  TAddPointer<TIteratorReference<I>>>
	&& requires(I& Iter)
	{
		{ ToAddress(Iter) } -> CSameAs<TAddPointer<TIteratorReference<I>>>;
	};

/** This is an example of a contiguous iterator, indicate the traits that define a contiguous iterator. */
template <CLValueReference T>
struct IContiguousIterator /* : IRandomAccessIterator<T> */
{
	// ~Begin CRandomAccessIterator.

	using FElementType = TRemoveCVRef<T>;

	IContiguousIterator();
	IContiguousIterator(const IContiguousIterator&);
	IContiguousIterator(IContiguousIterator&&);
	IContiguousIterator* operator=(const IContiguousIterator&);
	IContiguousIterator* operator=(IContiguousIterator&&);

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

	friend strong_ordering operator<=>(const IContiguousIterator&, const IContiguousIterator&);

	// ~End CRandomAccessIterator.

	/**
	 * Dereference operator. See IForwardIterator.
	 * Specify, the return type must be a true reference type and refer to an element of a contiguous sequence, not a proxy class.
	 * Also satisfies CRandomAccessIterator.
	 */
	T operator*() const;

	/** Indirection operator. Return the address of the element that the iterator is pointing to. */
	TAddPointer<T> operator->() const;

	// ~Begin CRandomAccessIterator.

	T operator[](ptrdiff) const;

	IContiguousIterator& operator++();
	IContiguousIterator& operator--();

	IContiguousIterator operator++(int);
	IContiguousIterator operator--(int);

	IContiguousIterator& operator+=(ptrdiff);
	IContiguousIterator& operator-=(ptrdiff);

	IContiguousIterator operator+(ptrdiff) const;
	IContiguousIterator operator-(ptrdiff) const;

	friend IContiguousIterator operator+(ptrdiff, const IContiguousIterator&);

	friend ptrdiff operator-(const IContiguousIterator&, const IContiguousIterator&);

	// ~End CRandomAccessIterator.
};

// Use IContiguousIterator<int> represents a contiguous iterator
static_assert(CContiguousIterator<IContiguousIterator<int&>>);
static_assert(    COutputIterator<IContiguousIterator<int&>, int>);

// The int* is the most typical example of a contiguous iterator
static_assert(CContiguousIterator<int*>);

#if PLATFORM_COMPILER_GCC
#	pragma GCC diagnostic pop
#endif

NAMESPACE_MODULE_END(Utility)
NAMESPACE_MODULE_END(Redcraft)
NAMESPACE_REDCRAFT_END