#pragma once

#include "CoreTypes.h"
#include "TypeTraits/TypeTraits.h"
#include "Templates/Utility.h"
#include "Iterators/Utility.h"
#include "Iterators/BasicIterator.h"
#include "Iterators/CountedIterator.h"
#include "Numerics/Math.h"
#include "Ranges/Utility.h"
#include "Ranges/Pipe.h"
#include "Ranges/View.h"
#include "Ranges/AllView.h"

NAMESPACE_REDCRAFT_BEGIN
NAMESPACE_MODULE_BEGIN(Redcraft)
NAMESPACE_MODULE_BEGIN(Utility)

NAMESPACE_BEGIN(Range)

/**
 * A view adapter that includes a specified number of elements from the beginning of a range.
 * When based on any view, the take view satisfies the corresponding any view.
 * When based on a random access and sized view, the take view satisfies a common view.
 */
template <CView V>
class TTakeView : public IBasicViewInterface<TTakeView<V>>
{
private:

	template <bool bConst> class FSentinelImpl;

public:

	FORCEINLINE constexpr TTakeView() requires (CDefaultConstructible<V>) = default;

	FORCEINLINE constexpr TTakeView(V InBase, size_t InCount) : Base(MoveTemp(InBase)), Count(InCount) { }

	NODISCARD FORCEINLINE constexpr auto Begin() requires (!CSimpleView<V>)
	{
		if constexpr (CSizedRange<V>)
		{
			if constexpr (CRandomAccessRange<V>)
			{
				return Range::Begin(Base);
			}
			else return MakeCountedIterator(Range::Begin(Base), Num());
		}
		else return MakeCountedIterator(Range::Begin(Base), Count);
	}

	NODISCARD FORCEINLINE constexpr auto Begin() const requires (CRange<const V>)
	{
		if constexpr (CSizedRange<const V>)
		{
			if constexpr (CRandomAccessRange<const V>)
			{
				return Range::Begin(Base);
			}
			else return MakeCountedIterator(Range::Begin(Base), Num());
		}
		else return MakeCountedIterator(Range::Begin(Base), Count);
	}

	NODISCARD FORCEINLINE constexpr auto End() requires (!CSimpleView<V>)
	{
		if constexpr (CSizedRange<V>)
		{
			if constexpr (CRandomAccessRange<V>)
			{
				return Range::Begin(Base) + Num();
			}
			else return DefaultSentinel;
		}
		else return FSentinelImpl<false>(Range::End(Base));
	}

	NODISCARD FORCEINLINE constexpr auto End() const requires (CRange<const V>)
	{
		if constexpr (CSizedRange<const V>)
		{
			if constexpr (CRandomAccessRange<const V>)
			{
				return Range::Begin(Base) + Num();
			}
			else return DefaultSentinel;
		}
		else return FSentinelImpl<true>(Range::End(Base));
	}

	NODISCARD FORCEINLINE constexpr size_t Num()       requires (CSizedRange<      V>) { return Math::Min(Range::Num(Base), Count); }
	NODISCARD FORCEINLINE constexpr size_t Num() const requires (CSizedRange<const V>) { return Math::Min(Range::Num(Base), Count); }

	NODISCARD FORCEINLINE constexpr V GetBase() const& requires (CCopyConstructible<V>) { return          Base;  }
	NODISCARD FORCEINLINE constexpr V GetBase() &&                                      { return MoveTemp(Base); }

private:

	NO_UNIQUE_ADDRESS V Base;

	size_t Count;

	template <bool bConst>
	class FSentinelImpl final
	{
	private:

		using FBase = TConditional<bConst, const V, V>;

	public:

		FORCEINLINE constexpr FSentinelImpl() = default;

		FORCEINLINE constexpr FSentinelImpl(FSentinelImpl<!bConst> Sentinel) requires (bConst && CConvertibleTo<TRangeSentinel<V>, TRangeSentinel<FBase>>)
			: Current(Sentinel.Current)
		{ }

		NODISCARD FORCEINLINE constexpr bool operator==(const TCountedIterator<TRangeIterator<FBase>>& InValue) const&
		{
			return InValue.Num() == 0 || InValue.GetBase() == Current;
		}

		template <bool bOther = !bConst> requires (CSentinelFor<TRangeSentinel<FBase>, TRangeIterator<TConditional<bOther, const V, V>>>)
		NODISCARD FORCEINLINE constexpr bool operator==(const TCountedIterator<TRangeIterator<TConditional<bOther, const V, V>>>& InValue)
		{
			return InValue.Num() == 0 || InValue.GetBase() == Current;
		}

		NODISCARD FORCEINLINE constexpr TRangeSentinel<FBase> GetBase() const { return Current; }

	private:

		NO_UNIQUE_ADDRESS TRangeSentinel<FBase> Current;

		FORCEINLINE constexpr FSentinelImpl(TRangeSentinel<FBase> InCurrent) : Current(InCurrent) { }

		friend TTakeView;
	};

};

template <typename R>
TTakeView(R&&, size_t) -> TTakeView<TAllView<R>>;

static_assert(        CInputRange<TTakeView<TAllView<IRange<        IInputIterator<int&>>>>>);
static_assert(      CForwardRange<TTakeView<TAllView<IRange<      IForwardIterator<int&>>>>>);
static_assert(CBidirectionalRange<TTakeView<TAllView<IRange<IBidirectionalIterator<int&>>>>>);
static_assert( CRandomAccessRange<TTakeView<TAllView<IRange< IRandomAccessIterator<int&>>>>>);
static_assert(   CContiguousRange<TTakeView<TAllView<IRange<   IContiguousIterator<int&>>>>>);

static_assert(CCommonRange<TTakeView<TAllView<ISizedRange< IRandomAccessIterator<int>>>>>);
static_assert( CSizedRange<TTakeView<TAllView<ISizedRange<IInputOrOutputIterator<int>>>>>);
static_assert(       CView<TTakeView<TAllView<     IRange<IInputOrOutputIterator<int>>>>>);

static_assert(COutputRange<TTakeView<TAllView<IRange<IOutputIterator<int&>>>>, int>);

NAMESPACE_END(Range)

template <typename T>
constexpr bool bEnableBorrowedRange<Range::TTakeView<T>> = bEnableBorrowedRange<T>;

NAMESPACE_BEGIN(Range)

/** Creates A view adapter that includes a specified number of elements from the beginning of a range. */
template <CViewableRange R> requires (requires { TTakeView(DeclVal<R>(), DeclVal<size_t>()); })
NODISCARD FORCEINLINE constexpr auto Take(R&& Base, size_t Count)
{
	return TTakeView(Forward<R>(Base), Count);
}

/** Creates A view adapter that includes a specified number of elements from the beginning of a range. */
NODISCARD FORCEINLINE constexpr auto Take(size_t Count)
{
	using FClosure = decltype([]<CViewableRange R> requires (requires { Range::Take(DeclVal<R>(), DeclVal<size_t>()); }) (R&& Base, size_t Count)
	{
		return Range::Take(Forward<R>(Base), Count);
	});

	return TAdaptorClosure<FClosure, size_t>(Count);
}

NAMESPACE_END(Range)

NAMESPACE_MODULE_END(Utility)
NAMESPACE_MODULE_END(Redcraft)
NAMESPACE_REDCRAFT_END