#pragma once

#include "CoreTypes.h"
#include "Range/View.h"
#include "Range/Pipe.h"
#include "Range/Utility.h"
#include "Templates/Utility.h"
#include "TypeTraits/TypeTraits.h"

NAMESPACE_REDCRAFT_BEGIN
NAMESPACE_MODULE_BEGIN(Redcraft)
NAMESPACE_MODULE_BEGIN(Utility)

NAMESPACE_BEGIN(Range)

/**
 * A view adapter that references other range.
 * No matter which it is base range, the reference view always satisfies the same range concept.
 */
template <CRange R> requires (CObject<R>)
class TRefView : public IBasicViewInterface<TRefView<R>>
{
private:

	// Use the function to check constructability.
	static void Func(R&);
	static void Func(R&&) = delete;

public:

	template <typename T> requires (!CSameAs<TRemoveCVRef<T>, TRefView> && CConvertibleTo<T, R&> && requires { Func(DeclVal<T>()); })
	FORCEINLINE constexpr TRefView(T&& InRange) : Ptr(AddressOf(static_cast<R&>(Forward<T>(InRange)))) { }

	NODISCARD FORCEINLINE constexpr TRangeIterator<R> Begin() const { return Range::Begin(*Ptr); }
	NODISCARD FORCEINLINE constexpr TRangeSentinel<R> End()   const { return Range::End  (*Ptr); }

	NODISCARD FORCEINLINE constexpr auto   GetData() const requires (CContiguousRange<R>)                          { return Range::GetData(*Ptr); }
	NODISCARD FORCEINLINE constexpr size_t Num()     const requires (CSizedRange<R>)                               { return Range::Num    (*Ptr); }
	NODISCARD FORCEINLINE constexpr bool   IsEmpty() const requires (requires(R Range) { Range::IsEmpty(Range); }) { return Range::IsEmpty(*Ptr); }

	NODISCARD FORCEINLINE constexpr R& GetBase() const { return *Ptr; }

private:

	R* Ptr;

};

template <typename R>
TRefView(R&) -> TRefView<R>;

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

static_assert(CCommonRange<TRefView<ICommonRange<      IForwardIterator<int>>>>);
static_assert( CSizedRange<TRefView< ISizedRange<IInputOrOutputIterator<int>>>>);
static_assert(       CView<TRefView<      IRange<IInputOrOutputIterator<int>>>>);

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

NAMESPACE_END(Range)

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

NAMESPACE_BEGIN(Range)

/**
 * A view adapter that has unique ownership of a range.
 * No matter which it is base range, the reference view always satisfies the same range concept.
 * Specify, the base range type must be movable, and the owning view always is movable but not copyable.
 */
template <CRange R> requires (CMovable<R> && !NAMESPACE_PRIVATE::TIsInitializerList<R>::Value)
class TOwningView : public IBasicViewInterface<TOwningView<R>>
{
public:

	FORCEINLINE constexpr TOwningView() requires (CDefaultConstructible<R>) = default;

	FORCEINLINE constexpr TOwningView(const TOwningView&) = delete;
	FORCEINLINE constexpr TOwningView(TOwningView&&)      = default;

	FORCEINLINE constexpr TOwningView(R&& InRange) : Base(MoveTemp(InRange)) { }

	FORCEINLINE constexpr TOwningView& operator=(const TOwningView&) = delete;
	FORCEINLINE constexpr TOwningView& operator=(TOwningView&&)      = default;

	NODISCARD FORCEINLINE constexpr auto Begin()                                  { return Range::Begin(Base); }
	NODISCARD FORCEINLINE constexpr auto End()                                    { return Range::End  (Base); }
	NODISCARD FORCEINLINE constexpr auto Begin() const requires (CRange<const R>) { return Range::Begin(Base); }
	NODISCARD FORCEINLINE constexpr auto End()   const requires (CRange<const R>) { return Range::End  (Base); }

	NODISCARD FORCEINLINE constexpr auto GetData()       requires (CContiguousRange<      R>) { return Range::GetData(Base); }
	NODISCARD FORCEINLINE constexpr auto GetData() const requires (CContiguousRange<const R>) { return Range::GetData(Base); }

	NODISCARD FORCEINLINE constexpr size_t Num()       requires (CSizedRange<      R>) { return Range::Num(Base); }
	NODISCARD FORCEINLINE constexpr size_t Num() const requires (CSizedRange<const R>) { return Range::Num(Base); }

	NODISCARD FORCEINLINE constexpr bool IsEmpty()       requires (requires(      R Base) { Range::IsEmpty(Base); }) { return Range::IsEmpty(Base); }
	NODISCARD FORCEINLINE constexpr bool IsEmpty() const requires (requires(const R Base) { Range::IsEmpty(Base); }) { return Range::IsEmpty(Base); }

	NODISCARD FORCEINLINE constexpr       R&  GetBase() &       { return                  Base;   }
	NODISCARD FORCEINLINE constexpr       R&& GetBase() &&      { return         MoveTemp(Base);  }
	NODISCARD FORCEINLINE constexpr const R&  GetBase() const&  { return          AsConst(Base);  }
	NODISCARD FORCEINLINE constexpr const R&& GetBase() const&& { return MoveTemp(AsConst(Base)); }

private:

	NO_UNIQUE_ADDRESS R Base;

};

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

static_assert(CCommonRange<TOwningView<ICommonRange<      IForwardIterator<int>>>>);
static_assert( CSizedRange<TOwningView< ISizedRange<IInputOrOutputIterator<int>>>>);
static_assert(       CView<TOwningView<      IRange<IInputOrOutputIterator<int>>>>);

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

NAMESPACE_END(Range)

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

NAMESPACE_BEGIN(Range)

/** Creates A view adapter that includes all elements of a range. */
template <CViewableRange R>
NODISCARD FORCEINLINE constexpr auto All(R&& InRange)
{
	if constexpr (CView<TDecay<R>>)
	{
		return TDecay<R>(Forward<R>(InRange));
	}

	else if constexpr (requires { TRefView(Forward<R>(InRange)); })
	{
		return TRefView(Forward<R>(InRange));
	}

	else return TOwningView(Forward<R>(InRange));
}

/** Creates A view adapter that includes all elements of a range. */
NODISCARD FORCEINLINE constexpr auto All()
{
	return TAdaptorClosure([]<CViewableRange R> requires (requires { All(DeclVal<R>()); }) (R&& Base)
	{
		return All(Forward<R>(Base));
	});
}

/** A view adapter that includes all elements of a range. */
template <CViewableRange R>
using TAllView = decltype(Range::All(DeclVal<R>()));

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

static_assert(CCommonRange<TAllView<ICommonRange<      IForwardIterator<int>>>>);
static_assert( CSizedRange<TAllView< ISizedRange<IInputOrOutputIterator<int>>>>);
static_assert(       CView<TAllView<      IRange<IInputOrOutputIterator<int>>>>);

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

NAMESPACE_END(Range)

NAMESPACE_MODULE_END(Utility)
NAMESPACE_MODULE_END(Redcraft)
NAMESPACE_REDCRAFT_END