#include "ZUnitTest.hpp"

#include <ZSTL/ZList.hpp>
#include <ZSTL/ZListAlgo.hpp>

static const char* test_Constructors_PushBack_Size_Empty();
static const char* test_Begin_End_At_Iterator();
static const char* test_Allocator_Front_Back_Clear();
static const char* test_Copy_Equals();
static const char* test_PushFront_PopBack_PopFront();
static const char* test_Insert_Erase();
static const char* test_Swap_Splice_SwapNodes();

//List of unit tests
ZUnitTest ZListUnitTests[] =
{
	{ "ZList: Constructors, PushBack, Size, Empty",	test_Constructors_PushBack_Size_Empty },
	{ "ZList: Begin, End, At, Iterator",			test_Begin_End_At_Iterator },
	{ "ZList: Allocator, Front, Back, Clear",		test_Allocator_Front_Back_Clear },
	{ "ZList: Copy, Equals",						test_Copy_Equals },
	{ "ZList: PushFront, PopBack, PopFront",		test_PushFront_PopBack_PopFront },
	{ "ZList: Insert, Erase",						test_Insert_Erase },
	{ "ZList: Swap, Splice, SwapNodes",				test_Swap_Splice_SwapNodes }
};

//Now declare the ZUnitTestBlock associated with this.
DECLARE_ZTESTBLOCK(ZList);

/*************************************************************************/

static const char* test_Constructors_PushBack_Size_Empty()
{
	//Test our default list constructor with default allocator
	ZList<int> list1;

	//Should start empty
	TASSERT(list1.Size() == 0, "Size() returns non-zero value but should be empty!");
	TASSERT(list1.Empty() == true, "Empty() returns false on newly created list!");

	//Fill in with 5 elements
	for(int i = 0; i < 5; i++)
	{
		list1.PushBack(i);

		TASSERT(list1.Size() == (size_t)(i + 1), "Size() returns incorrect value after PushBack()!");
		TASSERT(list1.Empty() == false, "Empty() returns true on list with elements!");
	}

	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* test_Begin_End_At_Iterator()
{
	int i;

	ZList<int> list1;
	ZList<int>::Iterator itr1;
	ZListIterator<int> itr2;

	itr1 = list1.Begin();

	TASSERT(itr1.HasNext() == false, "Iterator HasNext() returns true on empty list iterator!");
	TASSERT(itr1.HasPrev() == false, "Iterator HasPrev() returns true on empty list iterator!");
	TASSERT(itr1.HasCurrent() == false, "Iterator HasCurrent() returns false on empty list iterator!");

	for(int i = 0; i < 5; i++)
		list1.PushBack(i);		//These calls will invalidate the iterator, or, rather, have it point to 'End'

	itr1 = list1.Begin();

	TASSERT(itr1.HasNext() == true, "Iterator HasNext() returns false when elements remain!");
	TASSERT(itr1.HasPrev() == false, "Iterator HasPrevious() returns true at list Begin()!");

	//Check going forwards
	for (itr1 = list1.Begin(), i = 0;  i < 5; itr1++, i++)
	{
		TASSERT(itr1.HasCurrent() == true, "Iterator HasCurrent() returns false when elements should exist!");
		TASSERT(itr1.Get() == i, "Iterator Get() does not return correct value when iterating list!");
		TASSERT((*itr1) == i, "Iterator operator * does not return correct value when iterating list!");

		TASSERT(itr1 == list1.At(i), "At() returns incorrect iterator when forward iterating!");
	}

	//Should be at end
	TASSERT(list1.End() == itr1, "Iterator should be at end after forward iteration but == End() returns false!");
	TASSERT(itr1.HasNext() == false, "Iterator HasNext() should return false at End() but returns true!");
	TASSERT(itr1.HasPrev() == true, "Iterator HasPrev() should return true at End() but returns false!");
	TASSERT(itr1.HasCurrent() == false, "Iterator HasCurrent() should return false at End() but returns true!");

	for (i = 4; i >= 0; i--)
	{
		itr1--;

		TASSERT(itr1.Get() == i, "Iterator Get() does not return correct value when reverse iterating list!");
		TASSERT((*itr1) == i, "Iterator operator * does not return correct value when reverse iterating list!");

		TASSERT(itr1 == list1.At(i), "At() returns incorrect iterator when reverse iterating!");
	}

	//Should be at the beginning now
	TASSERT(itr1 == list1.Begin(), "Iterator should be at start after reverse iteration but == Begin() returns false!");

	for (i = 0; i < 5; i++)
	{
		itr1 = list1.At(i);

		TASSERT((*itr1) == i, "Iterator operator * returns incorrect value after iterator assignment!");
	}

	return ZTEST_SUCCESS;
}

/*************************************************************************/

//Allocator that counts current number of node allocations
template <typename T>
class ZListTestAllocator : public ZListAllocator<T>
{
public:
	//Current number of allocated nodes
	int Count;

	ZListTestAllocator() : Count(0) { }

	ZListNode<T>* Allocate() 
	{
		Count++;

		return new (std::nothrow) ZListNode<T>();
	}

	void Deallocate( ZListNode<T>* _node ) 
	{
		Count--;

		delete _node;
	}
};

static const char* test_Allocator_Front_Back_Clear()
{
	ZList<int, ZListTestAllocator<int> > list1;
	ZList<int>::Iterator itr;

	for (int i = 0; i < 5; i++)
	{
		list1.PushBack(i);

		TASSERT(list1.Allocator().Count == i + 1, "Allocator() returns incorrect count for number of allocated nodes!");
	}

	TASSERT(list1.Front() == 0, "Front() returns incorrect value!");
	TASSERT(list1.Back() == 4, "Back() returns incorrect value!");

	list1.Clear();

	TASSERT(list1.Empty(), "Empty() returns false after call to Clear()!");
	TASSERT(list1.Allocator().Count == 0, "Clear() failed to deallocate nodes!");;

	for (int i = 0; i < 5; i++)
		list1.PushBack(i);

	itr = list1.At(2);

	list1.Clear(itr);

	TASSERT(list1.Size() == 2, "Size() returns incorrect value after call to Clear()!");

	itr = list1.End();

	list1.Clear(itr);

	TASSERT(list1.Size() == 2, "Size() returns incorrect value after call to Clear(End())!");

	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* test_Copy_Equals()
{
	ZList<int, ZListTestAllocator<int> > list1;
	ZList<int, ZListTestAllocator<int> > list2;

	ZList<int>::Iterator itr;

	TASSERT(list1.Equals(list2) == true, "Equals() returns false when empty list is compared to empty list!");

	for (int i = 0; i < 5; i++)
		list1.PushBack(i);

	TASSERT(list1.Equals(list2) == false, "Equals() returns true when empty list is compared to non-empty list!");

	list2.Copy(list1);

	TASSERT(list1.Size() == list2.Size(), "Size() of list1 does not equal Size() of list2 after Copy!");
	TASSERT(list1.Equals(list2) == true, "Equals(list1, list2) returns false after Copy!");
	TASSERT(list2.Equals(list1) == true, "Equals(list2, list1) returns false after Copy!");

	itr = list2.At(3);

	list2.Clear(itr);

	TASSERT(list1.Equals(list2) == false, "Equals(list1, list2) returns true after Clear()!");
	TASSERT(list2.Equals(list1) == false, "Equals(list2, list1) returns true after Clear()!");

	ZList<int, ZListTestAllocator<int> > list3(list2.Begin(), list2.End());
	ZList<int, ZListTestAllocator<int> > list4(list2.Begin(), --itr);

	TASSERT(list3.Back() == 2, "Back() returns incorrect value on list3 after sub-list constructor!");
	TASSERT(list4.Back() == 1, "Back() returns incorrect value on list4 after sub-list constructor!");
	TASSERT(list3.Equals(list4) == false, "Equals(list3, list4) returns true after sub-list constructor!");

	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* test_PushFront_PopBack_PopFront()
{
	int i;

	ZList<int, ZListTestAllocator<int> > list1;

	for (i = 0; i < 5; i++)
	{
		list1.PushBack(i);

		TASSERT(list1.Allocator().Count == i + 1, "Allocator() returns incorrect count for number of allocated nodes after PushBack()!");
		TASSERT(list1.Back() == i, "Back() returns incorrect value after PushBack()!");
	}

	TASSERT(list1.Size() == 5, "Size() returns incorrect value after after PushBack()!");

	for (i = 5; i > 0; i--)
	{
		int j = list1.PopBack();

		TASSERT(j == i - 1, "PopBack() returns incorrect value!");

		TASSERT(list1.Allocator().Count == i - 1, "Allocator() returns incorrect count for number of allocated nodes after PopBack()!");
		TASSERT(list1.Size() == (size_t)(i - 1), "Size() returns incorrect value after PopBack()!");
	}

	list1.Clear();

	for (i = 0; i < 5; i++)
	{
		list1.PushFront(i);

		TASSERT(list1.Allocator().Count == i + 1, "Allocator() returns incorrect count for number of allocated nodes!");
		TASSERT(list1.Front() == i, "Front() returns wrong value after calls to PushFront()!");
	}

	TASSERT(list1.Size() == 5, "Size() returns incorrect value after after PushFront()!");

	for (i = 5; i > 0; i--)
	{
		int j = list1.PopFront();

		TASSERT(j == i - 1, "PopFront() returns incorrect value!");

		TASSERT(list1.Allocator().Count == i - 1, "Allocator() returns incorrect count for number of allocated nodes!");
		TASSERT(list1.Size() == (size_t)(i - 1), "Size() returns incorrect value after PopFront()!");
	}

	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* test_Insert_Erase()
{
	ZList<int, ZListTestAllocator<int> > list1;
	ZList<int, ZListTestAllocator<int> > list2;

	ZList<int>::Iterator itr1;
	ZList<int>::Iterator itr2;

	for (int i = 0; i < 5; i++)
		list1.PushBack(i);

	itr1 = list1.Begin();

	list1.Insert(itr1, 10);

	TASSERT((*itr1) == 0, "Iterator operator * returns incorrect value after Insert()!");
	TASSERT(list1.Begin().Get() == 10, "Iterator Get() returns incorrect value after Insert()!");
	TASSERT(list1.Size() == 6, "Size() returns incorrect value after Insert()!")

	itr2 = list1.End();

	list1.Insert(itr2, 20);

	TASSERT(list1.Back() == 20, "Back() returns incorrect value after Insert()!");
	TASSERT(itr2 == list1.End(), "Iterator does not equal End after Insert()!");
	TASSERT(list1.Size() == 7, "Size() returns incorrect value after Insert(End())!");

	itr1 = list1.Begin();

	int j = list1.Erase(itr1);

	TASSERT(j == 10, "Erase(itr1) returns incorrect value!");
	TASSERT(list1.Front() == 0, "Front() returns incorrect value after Erase()!");
	TASSERT(list1.Size() == 6, "Size() returns incorrect value after Erase(Begin())!");

	itr2 = list1.End() - 1;

	j = list1.Erase(itr2);

	TASSERT(j == 20, "Erase(itr2) returns incorrect value!");
	TASSERT(list1.Back() == 4, "Back() returns incorrect value after Erase()!");
	TASSERT(list1.Size() == 5, "Size() returns incorrect value after Erase(End() - 1))!");

	itr1 = list1.Begin();
	itr2 = list1.At(2);	//0, 1, 2

	list2.Insert(list2.Begin(), itr1, itr2);

	TASSERT(list2.Front() == 0 && list2.Back() == 1, "Front() and Back() do not return correct values after Insert(Begin(), itr1, itr2)!");

	itr1 = list2.Begin();
	itr2 = list2.End();

	list1.Insert(list1.At(2), itr1, itr2);

	TASSERT(list1.Size() == 7, "Size() returns incorrect value after Insert(At(2), itr1, itr2)!");

	TASSERT(list1.Front() == 0, "Front() returns incorrect value after Insert(At(2), itr1, itr2)!");
	TASSERT(list1.Back() == 4, "Back() returns incorrect value after Insert(At(2), itr1, itr2)");

	TASSERT(*list1.At(2) == 0, "At(2) dereference contains invalid value after Insert(At(2), itr1, itr2)!");
	TASSERT(*list1.At(3) == 1, "At(3) dereference contains invalid value after Insert(At(2), itr1, itr2)!");

	itr1 = list1.At(2);
	itr2 = list1.At(4);

	list1.Erase(itr1, itr2);

	TASSERT(list1.Size() == 5, "Size() returns incorrect value after Erase(itr1, itr2)!");

	itr1 = list1.Begin();

	for (int i = 0; itr1 != list1.End(); itr1++, i++)
	{
		TASSERT((*itr1) == i, "Iterator operator * returns incorrect value after Erase(itr1, itr2)!");
	}

	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* test_Swap_Splice_SwapNodes()
{
	int i;

	ZList<int> list1;
	ZList<int> list2;

	ZList<int>::Iterator itr1;
	ZList<int>::Iterator itr2;

	for (int i = 0; i < 5; i++)
		list1.PushBack(i);

	for (int i = 5; i < 10; i++)
		list2.PushBack(i);

	list1.Swap(list2);

	for (i = 5, itr1 = list1.Begin(); itr1 != list1.End(); i++, itr1++)
	{
		TASSERT((*itr1) == i, "Iterator operator * returns incorrect value on list1 after Swap()!");
	}

	for (i = 0, itr2 = list2.Begin(); itr2 != list2.End(); i++, itr2++)
	{
		TASSERT((*itr2) == i, "Iterator operator * returns incorrect value on list2 after Swap()!");
	}

	itr1 = list1.Begin();
	itr2 = list1.End();

	list2.Splice(list2.End(), list1, itr1, itr2);

	TASSERT(list2.Size() == 10, "Size() returns incorrect value after Splice()!");
	TASSERT(list1.Empty(), "Empty() returns false after Splice()!");

	for (i = 0, itr2 = list2.Begin(); itr2 != list2.End(); i++, itr2++)
	{
		TASSERT((*itr2) == i, "Iterator operator * returns incorrect value on list2 after Splice()!");
	}

	itr1 = list2.At(3);
	itr2 = list2.At(6);

	list1.Splice(list1.Begin(), list2, itr1, itr2);

	TASSERT(list2.Size() == 7, "Size() returns incorrect value for list2 after Splice(At(3), At(6))!");
	TASSERT(list1.Size() == 3, "Size() returns incorrect value for list1 after Splice(At(3), At(6))!");

	for (i = 3, itr1 = list1.Begin(); itr1 != list1.End(); i++, itr1++)
	{
		TASSERT((*itr1) == i, "Iterator operator * returns incorrect value on list1 after Splice()!");
	}

	list1.SwapNodes(list1.Begin(), list1.End() - 1);

	TASSERT(list1.Front() == 5, "Front() returns invalid value after call to SwapNodes()!");
	TASSERT(list1.Back() == 3, "Back() returns invalid value after call to SwapNodes()!");

	return ZTEST_SUCCESS;
}
