/*
	Test-ZHashMap.cpp
	Author: James Russell <jcrussell@762studios.com>

	Purpose: Unit Test the ZHashMap class.

	Changelog:
	12/18/2011 - Removed dependency on ZString (crertel)
	03/13/2011 - creation (jcrussell)
*/

#include "ZUnitTest.hpp"

#include <ZSTL/ZHashMap.hpp>
#include <ZSTL/ZString.hpp>

static const char* testPutGetSize();
static const char* testBeginEnd();
static const char* testFind();
static const char* testClearEmpty();
static const char* testContainsKey();
static const char* testContainsValue();
static const char* testKeys();
static const char* testValues();
static const char* testMappings();
static const char* testErase();
static const char* testResize();

//List of unit tests
ZUnitTest ZHashMapUnitTests[] =
{
	{ "ZHashMap: Put, Get, Size",	testPutGetSize },
	{ "ZHashMap: Begin, End",		testBeginEnd },
	{ "ZHashMap: Find",				testFind },
	{ "ZHashMap: Clear, Empty",		testClearEmpty },
	{ "ZHashMap: ContainsKey",		testContainsKey  },
	{ "ZHashMap: ContainsValue",	testContainsValue	},
	{ "ZHashMap: Keys",				testKeys },
	{ "ZHashMap: Values",			testValues },
	{ "ZHashMap: Mappings",			testMappings },
	{ "ZHashMap: Erase",			testErase },
	{ "ZHashMap: Resize",			testResize }
};

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

typedef ZHashMap<ZString, int> StringIntMap;

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

static const char* testPutGetSize()
{
	StringIntMap map;

	TASSERT(map.Size() == 0, "Size() returns non-zero value after initialization!");

	map.Put("wakka", 24);
	map.Put("ooga", 42);

	TASSERT(map.Size() == 2, "Size() returned incorrect value after 2 calls to Put()!");
	TASSERT(map.Get("wakka") == 24, "Get('wakka') retrieved wrong value after 2 calls to Put()!");
	TASSERT(map.Get("ooga") == 42, "Get('ooga') retrieved wrong value after 2 calls to Put()!");

	map.Put("booga", 2442);
	map.Put("wooga", 4224);

	TASSERT(map.Size() == 4, "Size() returned incorrect value after 4 calls to Put()!");
	TASSERT(map.Get("wakka") == 24, "Get('wakka') retrieved wrong value after 4 calls to Put()!");
	TASSERT(map.Get("ooga") == 42, "Get('ooga') retrieved wrong value after 4 calls to Put()!");
	TASSERT(map.Get("booga") == 2442, "Get('booga') retrieved wrong value after 4 calls to Put()!");
	TASSERT(map.Get("wooga") == 4224, "Get('wooga') retrieved wrong value after 4 calls to Put()!!");

	//TODO - a bit more rigor

	return ZTEST_SUCCESS;
}

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

static const char* testBeginEnd()
{
	StringIntMap map;
	bool found[4] = { false, false, false, false };

	map.Put("wakka", 24);
	map.Put("ooga", 42);

	StringIntMap::Iterator itr_begin = map.Begin();
	StringIntMap::Iterator itr_end = map.End();

	for (; itr_begin != itr_end; itr_begin++)
	{
		if ((*itr_begin).Key == "wakka")
		{
			TASSERT((*itr_begin).Value == 24, "'wakka' iterator returned wrong value!");  
			TASSERT(found[0] == false, "Already found value 'wakka'!");
			found[0] = true;
			continue;
		}
		
		if ((*itr_begin).Key == "ooga")
		{
			TASSERT((*itr_begin).Value == 42, "'ooga' iterator returned wrong value!");  
			TASSERT(found[1] == false, "Already found value 'ooga'!");
			found[1] = true;
			continue;
		}

		TASSERT_FAIL("Iterator returned invalid value after two calls to Put()!");
	}

	TASSERT(found[0] && found[1], "Iterator did not find both values!");

	found[0] = false;
	found[1] = false;
	
	map.Put("booga", 2442);
	map.Put("wooga", 4224);

	itr_begin = map.Begin();

	while (itr_begin.HasCurrent())
	{
		if ((*itr_begin).Key == "wakka")
		{
			TASSERT((*itr_begin).Value == 24, "'wakka' iterator returned wrong value!");  
			TASSERT(found[0] == false, "Already found value 'wakka'!");
			found[0] = true;
			itr_begin.Next();
			continue;
		}
		
		if ((*itr_begin).Key == "ooga")
		{
			TASSERT((*itr_begin).Value == 42, "'ooga' iterator returned wrong value!");  
			TASSERT(found[1] == false, "Already found value 'ooga'!");
			found[1] = true;
			itr_begin.Next();
			continue;
		}
		
		if ((*itr_begin).Key == "booga")
		{
			TASSERT((*itr_begin).Value == 2442, "'booga' iterator returned wrong value!");  
			TASSERT(found[2] == false, "Already found value 'wooga'!");
			found[2] = true;
			itr_begin.Next();
			continue;
		}
		
		if ((*itr_begin).Key == "wooga")
		{
			TASSERT((*itr_begin).Value == 4224, "'wooga' iterator returned wrong value!");  
			TASSERT(found[3] == false, "Already found value 'wooga'!");
			found[3] = true;
			itr_begin.Next();
			continue;
		}

		TASSERT_FAIL("Iterator returned invalid value after four calls to Put()!");
	}
	
	TASSERT(found[0] && found[1] && found[2] && found[3], "Iterator did not find all four values!");

	return ZTEST_SUCCESS;
}

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

static const char* testFind()
{
	StringIntMap map;

	map.Put("wakka", 24);
	map.Put("ooga", 42);

	StringIntMap::Iterator itr = map.Find("wakka");

	TASSERT(itr != map.End(), "Find('wakka') returned End()!");
	TASSERT((*itr).Key == "wakka", "Find('wakka') returned incorrect iterator key!");
	TASSERT((*itr).Value == 24, "Find('wakka') returned incorrect iterator value!");
	
	itr = map.Find("ooga");
	
	TASSERT(itr != map.End(), "Find('ooga') returned End()!");
	TASSERT((*itr).Key == "ooga", "Find('ooga') returned incorrect iterator key!");
	TASSERT((*itr).Value == 42, "Find('ooga') returned incorrect iterator value!");
	
	itr = map.Find("woogawakka");
	
	TASSERT(itr == map.End(), "Find('oogawakka') returned a valid iterator!");

	//TODO - a bit more rigor
	
	return ZTEST_SUCCESS;
}

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

static const char* testClearEmpty()
{
	StringIntMap map;

	map.Put("wakka", 24);
	map.Put("ooga", 42);
	map.Put("booga", 2442);
	map.Put("wooga", 4224);

	TASSERT(map.Size() == 4, "Size() returned incorrect value after 4 calls to Put()!");
	TASSERT(!map.Empty(), "Empty() returns true after 4 calls to Put()!");

	map.Clear();

	TASSERT(map.Size() == 0, "Size() returned non-zero value after call to Clear()!");
	TASSERT(map.Empty(), "Empty() returns false after Clear()!");

	return ZTEST_SUCCESS;
}

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

static const char* testContainsKey()
{
	StringIntMap map;

	TASSERT(map.ContainsKey("wakka") == false, "ContainsKey() returned true when given empty set");

	map.Put("oonga",2);
	TASSERT(map.ContainsKey("wakka") == false, "ContainsKey() returned true when given set missing key");

	map.Put("wakka",1);
	TASSERT(map.ContainsKey("wakka"), "ContainsKey() returned false when given set containing key");

	return ZTEST_SUCCESS;
}

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

static const char* testContainsValue()
{
	StringIntMap map;

	map.Put("wakka", 1);
	map.Put("wakkawakka", 2);

	TASSERT(map.ContainsValue(3) == false, "ContainsValue() claims to contain a value it doesn't have");
	TASSERT(map.ContainsValue(2) == true, "ContainsValue() claims to contain a value it does have");

	return ZTEST_SUCCESS;
}

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

static const char* testKeys()
{
	StringIntMap map;

	const char* test[3] = {"wakka", "wakkawakka", "wakkawakkawakka"};

	map.Put(test[0], 11);
	map.Put(test[1], 22);
	map.Put(test[2], 33);
	
	ZList<ZString> keys;

	map.Keys(keys);
	
	TASSERT(keys.Size() == 3, "Keys() did not return accurate sized list of keys in hashmap.");
	
	int found = 0;
	for (ZList<ZString>::Iterator itr = keys.Begin(); itr != keys.End(); ++itr)
	{
		ZString tofind = *itr;
		for (int i = 0; i < 3; i++)
		{
			if (strcmp(tofind.Data(), test[i]) == 0)
				found++;
		}
	}

	TASSERT(found == 3, "Keys() did not return accurate list of keys for values in hashmap.");

	return ZTEST_SUCCESS;
}

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

static const char* testValues()
{
	StringIntMap map;

	int test[3] = {11, 22, 33};

	map.Put("wakka", test[0]);
	map.Put("wakkawakka", test[1]);
	map.Put("wakkawakkawakka", test[2]);
	
	ZList<int> values;

	map.Values(values);

	TASSERT(values.Size() == 3, "Values() did not return accurate sized list of values in hashmap.");
	
	int found = 0;
	for (ZList<int>::Iterator itr = values.Begin(); itr != values.End(); ++itr)
	{
		int tofind = *itr;
		for (int i = 0; i < 3; i++)
		{
			if (tofind == test[i])
				found++;
		}
	}

	TASSERT(found == 3, "Values() did not return accurate list of values for values in hashmap.");

	return ZTEST_SUCCESS;
}

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

static const char* testMappings()
{
	StringIntMap map;

	map.Put("wakka", 11);

	ZList< ZPair<ZString, int> > mappings;

	map.Mappings(mappings);

	TASSERT(mappings.Size() == 1, "Mappings() returned list of incorrect size for key/value pairs.n");
	TASSERT( strcmp(mappings.Front().First.Data(), "wakka") == 0 && mappings.Front().Second == 11, "Mappings() returned incorrect key/value pair.");

	//TODO - more rigor

	return ZTEST_SUCCESS;
}

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

static const char* testErase()
{
	StringIntMap map;

	map.Put("wakka", 1);
	TASSERT(map.ContainsValue(1) && map.ContainsKey("wakka"), "Map has somehow lost our value.");

	map.Erase("wakka");
	TASSERT(map.ContainsValue(1) == false && map.ContainsKey("wakka") == false, "Map claims key despite removal.");

	return ZTEST_SUCCESS;
}

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

static const char* testResize()
{
	StringIntMap map(5);

	map.Put("wakka", 1);
	map.Put("oonga", 2);

	size_t wakkaBucket = (*map.Find("wakka")).HashMod;
	size_t oongaBucket = (*map.Find("oonga")).HashMod;
	
	StringIntMap::Iterator itr1 = map.Find("wakka");
	StringIntMap::Iterator itr2 = map.Find("oonga");
	
	TASSERT(itr1 != map.End(), "Could not find 'wakka' before resize!");
	TASSERT(itr2 != map.End(), "Could not find 'oonga' before resize!");

	map.Resize(11);

	itr1 = map.Find("wakka");
	itr2 = map.Find("oonga");

	TASSERT(itr1 != map.End(), "Could not find 'wakka' after resize!");
	TASSERT(itr2 != map.End(), "Could not find 'oonga' after resize!");

	TASSERT((*itr1).HashMod != wakkaBucket, "ZHashMap did not move 'wakka' to another bucket!");
	TASSERT((*itr2).HashMod != oongaBucket, "ZHashMap did not move 'oonga' to another bucket!");

	//TODO - a bit more rigor

	return ZTEST_SUCCESS;
}
