#pragma once

#include "CoreTypes.h"
#include "TypeTraits/TypeTraits.h"
#include "Templates/Utility.h"
#include "Templates/TypeHash.h"
#include "Memory/Allocators.h"
#include "Memory/MemoryOperator.h"
#include "Iterators/Utility.h"
#include "Iterators/BasicIterator.h"
#include "Iterators/Sentinel.h"
#include "Iterators/ReverseIterator.h"
#include "Ranges/Utility.h"
#include "Ranges/Factory.h"
#include "Miscellaneous/Compare.h"
#include "Miscellaneous/AssertionMacros.h"

NAMESPACE_REDCRAFT_BEGIN
NAMESPACE_MODULE_BEGIN(Redcraft)
NAMESPACE_MODULE_BEGIN(Utility)

template <CAllocatableObject T, CMultipleAllocator<T> Allocator = FHeapAllocator>
class TList
{
private:

	struct FNode;

	template <bool bConst, typename = TConditional<bConst, const T, T>>
	class TIteratorImpl;

public:

	using FElementType   = T;
	using FAllocatorType = Allocator;

	using      FReference =       T&;
	using FConstReference = const T&;

	using      FIterator = TIteratorImpl<false>;
	using FConstIterator = TIteratorImpl<true >;

	using      FReverseIterator = TReverseIterator<     FIterator>;
	using FConstReverseIterator = TReverseIterator<FConstIterator>;

	static_assert(CBidirectionalIterator<     FIterator>);
	static_assert(CBidirectionalIterator<FConstIterator>);

	/** Default constructor. Constructs an empty container with a default-constructed allocator. */
	TList()
	{
		Impl.HeadNode = Impl->Allocate(1);
		Impl.HeadNode->PrevNode = Impl.HeadNode;
		Impl.HeadNode->NextNode = Impl.HeadNode;

		Impl.ListNum = 0;
	}

	/** Constructs the container with 'Count' default instances of T. */
	explicit TList(size_t Count) requires (CDefaultConstructible<FElementType>) : TList()
	{
		FNode* EndNode = Impl.HeadNode->PrevNode;

		while (Count > Impl.ListNum)
		{
			FNode* Node = new (Impl->Allocate(1)) FNode;

			EndNode->NextNode = Node;
			Node->PrevNode = EndNode;

			++Impl.ListNum;

			EndNode = Node;
		}

		EndNode->NextNode = Impl.HeadNode;
		Impl.HeadNode->PrevNode = EndNode;
	}

	/** Constructs the container with 'Count' copies of elements with 'InValue'. */
	TList(size_t Count, const FElementType& InValue) requires (CCopyable<FElementType>)
		: TList(Ranges::Repeat(InValue, Count))
	{ }

	/** Constructs the container with the contents of the range ['First', 'Last'). */
	template <CInputIterator I, CSentinelFor<I> S> requires (CConstructibleFrom<FElementType, TIteratorReference<I>>)
	TList(I First, S Last) : TList()
	{
		FNode* EndNode = Impl.HeadNode->PrevNode;

		for (; First != Last; ++First)
		{
			FNode* Node = new (Impl->Allocate(1)) FNode(InPlace, *First);

			EndNode->NextNode = Node;
			Node->PrevNode = EndNode;

			++Impl.ListNum;

			EndNode = Node;
		}

		EndNode->NextNode = Impl.HeadNode;
		Impl.HeadNode->PrevNode = EndNode;
	}

	/** Constructs the container with the contents of the range. */
	template <CInputRange R> requires (!CSameAs<TRemoveCVRef<R>, TList> && CConstructibleFrom<FElementType, TRangeReference<R>>)
	FORCEINLINE explicit TList(R&& Range) : TList(Ranges::Begin(Range), Ranges::End(Range)) { }

	/** Copy constructor. Constructs the container with the copy of the contents of 'InValue'. */
	FORCEINLINE TList(const TList& InValue) requires (CCopyConstructible<FElementType>) : TList(InValue.Begin(), InValue.End()) { }

	/** Move constructor. After the move, 'InValue' is guaranteed to be empty. */
	FORCEINLINE TList(TList&& InValue) : TList() { Swap(*this, InValue); }

	/** Constructs the container with the contents of the initializer list. */
	FORCEINLINE TList(initializer_list<FElementType> IL) requires (CCopyConstructible<FElementType>) : TList(Ranges::Begin(IL), Ranges::End(IL)) { }

	/** Destructs the list. The destructors of the elements are called and the used storage is deallocated. */
	~TList()
	{
		FNode* NodeToDeallocate = Impl.HeadNode->NextNode;

		Impl->Deallocate(NodeToDeallocate->PrevNode);

		for (size_t Index = 0; Index != Impl.ListNum; ++Index)
		{
			FNode* NextNode = NodeToDeallocate->NextNode;

			Memory::Destruct(NodeToDeallocate);
			Impl->Deallocate(NodeToDeallocate);

			NodeToDeallocate = NextNode;
		}
	}

	/** Copy assignment operator. Replaces the contents with a copy of the contents of 'InValue'. */
	TList& operator=(const TList& InValue) requires (CCopyable<FElementType>)
	{
		if (&InValue == this) UNLIKELY return *this;

		     FIterator ThisIter  =         Begin();
		FConstIterator OtherIter = InValue.Begin();

		while (ThisIter != End() && OtherIter != InValue.End())
		{
			*ThisIter = *OtherIter;

			++ThisIter;
			++OtherIter;
		}

		if (ThisIter == End())
		{
			while (OtherIter != InValue.End())
			{
				EmplaceBack(*OtherIter);
				++OtherIter;
			}
		}
		else if (OtherIter == InValue.End())
		{
			Erase(ThisIter, End());
		}

		Impl.ListNum = InValue.Num();

		return *this;
	}

	/** Move assignment operator. After the move, 'InValue' is guaranteed to be empty. */
	FORCEINLINE TList& operator=(TList&& InValue) { Swap(*this, InValue); InValue.Reset(); return *this; }

	/** Replaces the contents with those identified by initializer list. */
	TList& operator=(initializer_list<FElementType> IL) requires (CCopyable<FElementType>)
	{
		      FIterator     ThisIter  =        Begin();
		const FElementType* OtherIter = Ranges::Begin(IL);

		while (ThisIter != End() && OtherIter != Ranges::End(IL))
		{
			*ThisIter = *OtherIter;

			++ThisIter;
			++OtherIter;
		}

		if (ThisIter == End())
		{
			while (OtherIter != Ranges::End(IL))
			{
				EmplaceBack(*OtherIter);
				++OtherIter;
			}
		}
		else if (OtherIter == Ranges::End(IL))
		{
			Erase(ThisIter, End());
		}

		Impl.ListNum = Ranges::Num(IL);

		return *this;
	}

	/** Compares the contents of two lists. */
	NODISCARD friend bool operator==(const TList& LHS, const TList& RHS) requires (CWeaklyEqualityComparable<FElementType>)
	{
		if (LHS.Num() != RHS.Num()) return false;

		FConstIterator LHSIter = LHS.Begin();
		FConstIterator RHSIter = RHS.Begin();

		while (LHSIter != LHS.End())
		{
			if (*LHSIter != *RHSIter) return false;

			++LHSIter;
			++RHSIter;
		}

		check(RHSIter == RHS.End());

		return true;
	}

	/** Compares the contents of 'LHS' and 'RHS' lexicographically. */
	NODISCARD friend auto operator<=>(const TList& LHS, const TList& RHS) requires (CSynthThreeWayComparable<FElementType>)
	{
		FConstIterator LHSIter = LHS.Begin();
		FConstIterator RHSIter = RHS.Begin();

		while (LHSIter != LHS.End() && RHSIter != RHS.End())
		{
			if (const auto Result = SynthThreeWayCompare(*LHSIter, *RHSIter); Result != 0) return Result;

			++LHSIter;
			++RHSIter;
		}

		return LHS.Num() <=> RHS.Num();
	}

	/** Inserts 'InValue' before 'Iter' in the container. */
	FORCEINLINE FIterator Insert(FConstIterator Iter, const FElementType& InValue) requires (CCopyConstructible<FElementType>) { return Emplace(Iter, InValue); }

	/** Inserts 'InValue' before 'Iter' in the container. */
	FORCEINLINE FIterator Insert(FConstIterator Iter, FElementType&& InValue) requires (CMoveConstructible<FElementType>) { return Emplace(Iter, MoveTemp(InValue)); }

	/** Inserts 'Count' copies of the 'InValue' before 'Iter' in the container. */
	FIterator Insert(FConstIterator Iter, size_t Count, const FElementType& InValue) requires (CCopyConstructible<FElementType>)
	{
		return Insert(Iter, Ranges::Repeat(InValue, Count));
	}

	/** Inserts elements from range ['First', 'Last') before 'Iter'. */
	template <CInputIterator I, CSentinelFor<I> S> requires (CConstructibleFrom<FElementType, TIteratorReference<I>>)
	FIterator Insert(FConstIterator Iter, I First, S Last)
	{
		if (First == Last) return FIterator(Iter.Pointer);

		FNode* InsertNode = Iter.Pointer->PrevNode;

		const auto InsertOnce = [&]() -> FNode*
		{
			FNode* Node = new (Impl->Allocate(1)) FNode(InPlace, *First);

			InsertNode->NextNode = Node;
			Node->PrevNode = InsertNode;

			++Impl.ListNum;

			InsertNode = Node;

			return Node;
		};

		FNode* FirstNode = InsertOnce();

		for (++First; First != Last; ++First)
		{
			InsertOnce();
		}

		InsertNode->NextNode = Iter.Pointer;
		Iter.Pointer->PrevNode = InsertNode;

		return FIterator(FirstNode);
	}

	/** Inserts elements from range ['First', 'Last') before 'Iter'. */
	template <CInputRange R> requires (CConstructibleFrom<FElementType, TRangeReference<R>>)
	FORCEINLINE FIterator Insert(FConstIterator Iter, R&& Range) { return Insert(Iter, Ranges::Begin(Range), Ranges::End(Range)); }

	/** Inserts elements from initializer list before 'Iter' in the container. */
	FORCEINLINE FIterator Insert(FConstIterator Iter, initializer_list<FElementType> IL) requires (CCopyConstructible<FElementType>) { return Insert(Iter, Ranges::Begin(IL), Ranges::End(IL)); }

	/** Inserts a new element into the container directly before 'Iter'. */
	template <typename... Ts> requires (CConstructibleFrom<FElementType, Ts...>)
	FIterator Emplace(FConstIterator Iter, Ts&&... Args)
	{
		FNode* Node = new (Impl->Allocate(1)) FNode(InPlace, Forward<Ts>(Args)...);

		++Impl.ListNum;

		Node->PrevNode = Iter.Pointer->PrevNode;
		Node->NextNode = Iter.Pointer;

		Node->PrevNode->NextNode = Node;
		Node->NextNode->PrevNode = Node;

		return FIterator(Node);
	}

	/** Removes the element at 'Iter' in the container. */
	FIterator Erase(FConstIterator Iter)
	{
		FNode* NodeToErase = Iter.Pointer;

		checkf(NodeToErase->NextNode != NodeToErase, TEXT("Read access violation. Please check Iter != End()."));

		NodeToErase->PrevNode->NextNode = NodeToErase->NextNode;
		NodeToErase->NextNode->PrevNode = NodeToErase->PrevNode;

		FNode* NextNode = NodeToErase->NextNode;

		Memory::Destruct(NodeToErase);
		Impl->Deallocate(NodeToErase);

		--Impl.ListNum;

		return FIterator(NextNode);
	}

	/** Removes the elements in the range ['First', 'Last') in the container. */
	FIterator Erase(FConstIterator First, FConstIterator Last)
	{
		FNode* FirstToErase = First.Pointer;
		FNode* LastToErase  = Last.Pointer;

		FirstToErase->PrevNode->NextNode = LastToErase;
		LastToErase->PrevNode = FirstToErase->PrevNode;

		while (FirstToErase != LastToErase)
		{
			FNode* NextNode = FirstToErase->NextNode;

			Memory::Destruct(FirstToErase);
			Impl->Deallocate(FirstToErase);

			--Impl.ListNum;

			FirstToErase = NextNode;
		}

		return FIterator(LastToErase);
	}

	/** Appends the given element value to the end of the container. */
	FORCEINLINE void PushBack(const FElementType& InValue) requires (CCopyConstructible<FElementType>) { EmplaceBack(InValue); }

	/** Appends the given element value to the end of the container. */
	FORCEINLINE void PushBack(FElementType&& InValue) requires (CMoveConstructible<FElementType>) { EmplaceBack(MoveTemp(InValue)); }

	/** Appends a new element to the end of the container. */
	template <typename... Ts> requires (CConstructibleFrom<FElementType, Ts...>)
	FORCEINLINE FReference EmplaceBack(Ts&&... Args) { return *Emplace(End(), Forward<Ts>(Args)...); }

	/** Removes the last element of the container. The list cannot be empty. */
	FORCEINLINE void PopBack() { Erase(--End()); }

	/** Prepends the given element value to the beginning of the container. */
	FORCEINLINE void PushFront(const FElementType& InValue) requires (CCopyConstructible<FElementType>) { EmplaceFront(InValue); }

	/** Prepends the given element value to the beginning of the container. */
	FORCEINLINE void PushFront(FElementType&& InValue) requires (CMoveConstructible<FElementType>) { EmplaceFront(MoveTemp(InValue)); }

	/** Prepends a new element to the beginning of the container. */
	template <typename... Ts> requires (CConstructibleFrom<FElementType, Ts...>)
	FORCEINLINE FReference EmplaceFront(Ts&&... Args) { return *Emplace(Begin(), Forward<Ts>(Args)...); }

	/** Removes the first element of the container. The list cannot be empty. */
	FORCEINLINE void PopFront() { Erase(Begin()); }

	/** Resizes the container to contain 'Count' elements. Additional default elements are appended. */
	void SetNum(size_t Count) requires (CDefaultConstructible<FElementType>)
	{
		if (Count == Impl.ListNum) return;

		if (Count < Impl.ListNum)
		{
			FIterator First = End();

			for (size_t Index = 0; Index != Impl.ListNum - Count; ++Index) --First;

			Erase(First, End());

			Impl.ListNum = Count;

			return;
		}

		FNode* EndNode = Impl.HeadNode->PrevNode;

		while (Count > Impl.ListNum)
		{
			FNode* Node = new (Impl->Allocate(1)) FNode;

			EndNode->NextNode = Node;
			Node->PrevNode = EndNode;

			++Impl.ListNum;

			EndNode = Node;
		}

		EndNode->NextNode = Impl.HeadNode;
		Impl.HeadNode->PrevNode = EndNode;
	}

	/** Resizes the container to contain 'Count' elements. Additional copies of 'InValue' are appended. */
	void SetNum(size_t Count, const FElementType& InValue) requires (CCopyConstructible<FElementType>)
	{
		if (Count == Impl.ListNum) return;

		if (Count < Impl.ListNum)
		{
			FIterator First = End();

			for (size_t Index = 0; Index != Impl.ListNum - Count; ++Index) --First;

			Erase(First, End());

			Impl.ListNum = Count;

			return;
		}

		FNode* EndNode = Impl.HeadNode->PrevNode;

		while (Count > Impl.ListNum)
		{
			FNode* Node = new (Impl->Allocate(1)) FNode(InPlace, InValue);

			EndNode->NextNode = Node;
			Node->PrevNode = EndNode;

			++Impl.ListNum;

			EndNode = Node;
		}

		EndNode->NextNode = Impl.HeadNode;
		Impl.HeadNode->PrevNode = EndNode;
	}

	/** @return The iterator to the first or end element. */
	NODISCARD FORCEINLINE      FIterator Begin()       { return      FIterator(Impl.HeadNode->NextNode); }
	NODISCARD FORCEINLINE FConstIterator Begin() const { return FConstIterator(Impl.HeadNode->NextNode); }
	NODISCARD FORCEINLINE      FIterator End()         { return      FIterator(Impl.HeadNode);           }
	NODISCARD FORCEINLINE FConstIterator End()   const { return FConstIterator(Impl.HeadNode);           }

	/** @return The reverse iterator to the first or end element. */
	NODISCARD FORCEINLINE      FReverseIterator RBegin()       { return      FReverseIterator(End());   }
	NODISCARD FORCEINLINE FConstReverseIterator RBegin() const { return FConstReverseIterator(End());   }
	NODISCARD FORCEINLINE      FReverseIterator REnd()         { return      FReverseIterator(Begin()); }
	NODISCARD FORCEINLINE FConstReverseIterator REnd()   const { return FConstReverseIterator(Begin()); }

	/** @return The number of elements in the container. */
	NODISCARD FORCEINLINE size_t Num() const { return Impl.ListNum; }

	/** @return true if the container is empty, false otherwise. */
	NODISCARD FORCEINLINE bool IsEmpty() const { return Num() == 0; }

	/** @return true if the iterator is valid, false otherwise. */
	NODISCARD FORCEINLINE bool IsValidIterator(FConstIterator Iter) const
	{
		FNode* Current = Impl.HeadNode;

		for (size_t Index = 0; Index != Impl.ListNum + 1; ++Index)
		{
			if (Current == Iter.Pointer)
			{
				return true;
			}

			Current = Current->NextNode;
		}

		return false;
	}

	/** @return The reference to the first or last element. */
	NODISCARD FORCEINLINE       FElementType& Front()       { return *Begin();     }
	NODISCARD FORCEINLINE const FElementType& Front() const { return *Begin();     }
	NODISCARD FORCEINLINE       FElementType& Back()        { return *(End() - 1); }
	NODISCARD FORCEINLINE const FElementType& Back()  const { return *(End() - 1); }

	/** Erases all elements from the container. After this call, Num() returns zero. */
	void Reset()
	{
		FNode* NodeToDeallocate = Impl.HeadNode->NextNode;

		Impl.HeadNode->PrevNode = Impl.HeadNode;
		Impl.HeadNode->NextNode = Impl.HeadNode;

		for (size_t Index = 0; Index != Impl.ListNum; ++Index)
		{
			FNode* NextNode = NodeToDeallocate->NextNode;

			Memory::Destruct(NodeToDeallocate);
			Impl->Deallocate(NodeToDeallocate);

			NodeToDeallocate = NextNode;
		}

		Impl.ListNum = 0;
	}

	/** Overloads the GetTypeHash algorithm for TList. */
	NODISCARD friend FORCEINLINE size_t GetTypeHash(const TList& A) requires (CHashable<FElementType>)
	{
		size_t Result = 0;

		for (const FElementType& Element : A)
		{
			Result = HashCombine(Result, GetTypeHash(Element));
		}

		return Result;
	}

	/** Overloads the Swap algorithm for TList. */
	friend FORCEINLINE void Swap(TList& A, TList& B)
	{
		Swap(A.Impl.HeadNode, B.Impl.HeadNode);
		Swap(A.Impl.ListNum,  B.Impl.ListNum);
	}

	ENABLE_RANGE_BASED_FOR_LOOP_SUPPORT

private:

	struct FNode
	{
		FNode* PrevNode;
		FNode* NextNode;
		FElementType Value;

		FORCEINLINE FNode() = default;

		template <typename... Ts>
		FORCEINLINE FNode(FInPlace, Ts&&... Args) : Value(Forward<Ts>(Args)...) { }
	};

	static_assert(CMultipleAllocator<FAllocatorType, FNode>);

	ALLOCATOR_WRAPPER_BEGIN(FAllocatorType, FNode, Impl)
	{
		FNode* HeadNode;
		size_t ListNum;
	}
	ALLOCATOR_WRAPPER_END(FAllocatorType, FNode, Impl)

	template <bool bConst, typename U>
	class TIteratorImpl final
	{
	public:

		using FElementType = TRemoveCV<T>;

		FORCEINLINE TIteratorImpl() = default;

		FORCEINLINE TIteratorImpl(const TIteratorImpl<false>& InValue) requires (bConst)
			: Pointer(InValue.Pointer)
		{ }

		FORCEINLINE TIteratorImpl(const TIteratorImpl&)            = default;
		FORCEINLINE TIteratorImpl(TIteratorImpl&&)                 = default;
		FORCEINLINE TIteratorImpl& operator=(const TIteratorImpl&) = default;
		FORCEINLINE TIteratorImpl& operator=(TIteratorImpl&&)      = default;

		NODISCARD friend FORCEINLINE bool operator==(const TIteratorImpl& LHS, const TIteratorImpl& RHS) { return LHS.Pointer == RHS.Pointer; }

		NODISCARD FORCEINLINE U& operator*()  const { return  Pointer->Value; }
		NODISCARD FORCEINLINE U* operator->() const { return &Pointer->Value; }

		FORCEINLINE TIteratorImpl& operator++() { Pointer = Pointer->NextNode; return *this; }
		FORCEINLINE TIteratorImpl& operator--() { Pointer = Pointer->PrevNode; return *this; }

		FORCEINLINE TIteratorImpl operator++(int) { TIteratorImpl Temp = *this; ++*this; return Temp; }
		FORCEINLINE TIteratorImpl operator--(int) { TIteratorImpl Temp = *this; --*this; return Temp; }

	private:

		FNode* Pointer = nullptr;

		FORCEINLINE TIteratorImpl(FNode* InPointer)
			: Pointer(InPointer)
		{ }

		template <bool, typename> friend class TIteratorImpl;

		friend TList;

	};

};

template <typename I, typename S>
TList(I, S) -> TList<TIteratorElement<I>>;

template <typename R>
TList(R) -> TList<TRangeElement<R>>;

template <typename T>
TList(initializer_list<T>) -> TList<T>;

NAMESPACE_MODULE_END(Utility)
NAMESPACE_MODULE_END(Redcraft)
NAMESPACE_REDCRAFT_END