// Copyright (C) 2008-2012 Colin MacDonald
// No rights reserved: this software is in the public domain.

#include "testUtils.h"
#include <irrlicht.h>

using namespace irr;
using namespace core;

core::map<int, int> countReferences;

struct SDummy
{
	SDummy(int a) : x(a)
	{
		countReferences.insert(x,1);
	}

	SDummy() : x(0)
	{
		countReferences.insert(x,1);
	}

	SDummy(const SDummy& other)
	{
		x = other.x;
		countReferences[x] = countReferences[x] + 1;
	}

	~SDummy()
	{
		countReferences[x] = countReferences[x] - 1;
	}

	int x;
};

static bool testErase()
{
	{
		core::array<SDummy> aaa;
		aaa.push_back(SDummy(0));
		aaa.push_back(SDummy(1));
		aaa.push_back(SDummy(2));
		aaa.push_back(SDummy(3));
		aaa.push_back(SDummy(4));
		aaa.push_back(SDummy(5));

		aaa.erase(0,2);
	}

	for ( core::map<int,int>::Iterator it = countReferences.getIterator(); !it.atEnd(); it++ )
	{
		if ( it->getValue() != 0 )
		{
			logTestString("testErase: wrong count for %d, it's: %d\n", it->getKey(), it->getValue());
			return false;
		}
	}
	return true;
}


struct VarArray
{
	core::array < int, core::irrAllocatorFast<int> > MyArray;
};

static bool testSelfAssignment()
{
	core::array<int> myArray;
	myArray.push_back(1);
	myArray = myArray;
	return myArray.size() == 1;
}

// this will (did once) crash when wrong due to deallocating memory twice, so no return value
static void crashTestFastAlloc()
{
	core::array < VarArray, core::irrAllocatorFast<VarArray> > ArrayArray;
	ArrayArray.setAllocStrategy(core::ALLOC_STRATEGY_SAFE); // force more re-allocations
	VarArray var;
	var.MyArray.setAllocStrategy(core::ALLOC_STRATEGY_SAFE); // force more re-allocations
	var.MyArray.push_back( 0 );

	for ( int i=0; i< 100; ++i )
	{
		ArrayArray.push_back(var);
		ArrayArray.push_back(var);
	}
}

static bool testSwap()
{
	bool result = true;

	core::array<int> array1, array2, copy1, copy2;
	for ( int i=0; i<99; ++i )
	{
		array1.push_back(i);
		if ( i < 10 )	// we want also different container sizes
			array2.push_back(99-i);
	}
	copy1 = array1;
	copy2 = array2;
	array1.swap(array2);

	result &= (array1 == copy2);
	result &= (array2 == copy1);

	assert_log( result );

	return result;
}

// add numbers to the array going down from size to 1
static void addInvNumbers(irr::core::array<int>& arr, irr::u32 size)
{
	for ( irr::u32 i=0; i<size; ++i )
	{
		arr.push_back(size-i);
	}
}
 
// Ensure numbers are sorted in ascending order
static bool validateSortedAscending(const irr::core::array<int>& arr)
{
	for ( irr::u32 i=1; i< arr.size(); ++ i)
	{
		if ( arr[i-1] > arr[i] )
			return false;
	}
 
	return true;
}

static bool testSort()
{
	irr::core::array<int> arr;
	for ( irr::u32 i=0; i<1000; ++i )
	{
		arr.clear();
		addInvNumbers(arr, i);
		arr.sort();
		if ( !validateSortedAscending(arr) )
		{
			return false;
		}
	}
	
	for ( irr::u32 i=0; i<1000; ++i )
	{
		arr.clear();
		addInvNumbers(arr, i);
		addInvNumbers(arr, i);
		arr.sort();
		if ( !validateSortedAscending(arr) )
		{
			return false;
		}
	}
	
	return true;
}

// Test the functionality of core::array
bool testIrrArray(void)
{
	bool allExpected = true;

	logTestString("crashTestFastAlloc\n");
	crashTestFastAlloc();
	allExpected &= testSelfAssignment();
	allExpected &= testSwap();
	allExpected &= testErase();
	allExpected &= testSort();

	if(allExpected)
		logTestString("\nAll tests passed\n");
	else
		logTestString("\nFAIL!\n");

	return allExpected;
}