/* Basic test harness for the grid class.  This does NOT test all cases,
 * but should be a good starting point for the rest of your code.
 * This test assumes that there is a file called grid.h containing the
 * grid class.
 *
 * Good luck, and happy testing!
 */

#include <iostream>
#include <iomanip>   // For setw
#include <cstdlib>   // For rand, srand
#include <ctime>     // For time
#include <string>
#include <algorithm> // For generate, fill, equal, binary_search, find
#include <iterator>  // For distance
#include "grid.h"
#include "grid.h"   // This shouldn't cause any problems.
using namespace std;

/* A class that tracks the number of remaining instances of that class.
 * This is used to ensure that the grid cleans up its resources correctly.
 */
class Counter
{
public:
	/* Constructor / copy ctor both increment the number of instances. */
	Counter() { ++numInstances; }
	Counter(const Counter&) { ++numInstances; }

	/* Destructor removes an instance. */
	~Counter() { --numInstances; }

	/* Helper function to return the number of active instances. */
	static int getNumInstances() { return numInstances; }
private:
	static int numInstances;
};
int Counter::numInstances = 0;

/* Helper function that checks that a given condition is true, failing
 * if unable to do so.
 */
const int LINE_NUM_WIDTH = 3;
void DoCheckCondition(bool shouldBeTrue, const string &message, int lineNumber)
{
	if(!shouldBeTrue)
	{
		cout << "FAILED: " << message << endl;
		cout << "        (Test failed at line #" << lineNumber << ')' << endl;
	}
	else
		cout << "PASS:   " << message << endl;
}

/* Macro that invokes DoCheckCondition with the current line number. */
#define CheckCondition(cond, msg) DoCheckCondition((cond), (msg), __LINE__)

/* Basic test that confirms that functionality from Step 0 works correctly. */
void BasicFunctionalityTest()
{
	/* Empty grid testing. */
	{ // New scope keeps variables local.
		grid<string> emptyGrid;
		CheckCondition(emptyGrid.getWidth() == 0, "Default ctor should set width to zero.");
		CheckCondition(emptyGrid.getHeight() == 0, "Default ctor should set height to zero.");
		CheckCondition(emptyGrid.size() == 0, "Default ctor should set size to zero.");
		CheckCondition(emptyGrid.empty(), "Default ctor should make grid empty.");
	}

	/* Size constructor testing. */
	{
		const int defaultWidth = 137;
		const int defaultHeight = 42;
		grid<string> sizedGrid(defaultWidth, defaultHeight);
		CheckCondition(sizedGrid.getWidth() == defaultWidth, "Sizing ctor should set width to defaultWidth.");
		CheckCondition(sizedGrid.getHeight() == defaultHeight, "Sizing ctor should set height to defaultHeight.");
		CheckCondition(sizedGrid.size() == defaultWidth * defaultHeight, "Sizing ctor should set width to defaultWidth * defaultHeight");
		CheckCondition(!sizedGrid.empty(), "Sizing ctor should make grid nonempty.");
		CheckCondition(sizedGrid.getAt(0, 0).empty(), "Sizing ctor should have all elements default-initialized.");
	}

	/* Resize/clear testing. */
	{
		const int newWidth = 137, newHeight = 42;
		grid<string> myGrid;
		myGrid.resize(newWidth, newHeight);
		CheckCondition(myGrid.getWidth() == newWidth, "New width should be newWidth.");
		CheckCondition(myGrid.getHeight() == newHeight, "New height should be newHeight.");
		CheckCondition(myGrid.size() == newWidth * newHeight, "New size should be newWidth * newHeight.");
		myGrid.getAt(0, 0) = "This is a test!";
		myGrid.resize(1, 1);
		CheckCondition(myGrid.getAt(0, 0).empty(), "Resize should discard existing elements.");
		myGrid.clear();
		CheckCondition(myGrid.empty(), "Clear should discard all elements.");
	}
}

/* Basic test that confirms that iterators from Step 1 work correctly. */
void BasicIteratorTest()
{
	const int newWidth = 137, newHeight = 42;
	grid<int> myGrid(newWidth, newHeight);

	CheckCondition(distance(myGrid.begin(), myGrid.end()) == myGrid.size(), "Iterator range should be same size as container.");
	CheckCondition(myGrid.begin() == &myGrid.getAt(0, 0), "Begin iterator should be at beginning of range.");

	fill(myGrid.begin(), myGrid.end(), 137);

	CheckCondition(find(myGrid.begin(), myGrid.end(), 42) == myGrid.end(), "Filling grid with 137 should make 42 not present.");
	CheckCondition(!binary_search(myGrid.begin(), myGrid.end(), 42), "Filling grid with 137 should make binary search for 42 fail.");

	myGrid.getAt(21, 27) = -1;

	CheckCondition(*myGrid.getIteratorToElement(21, 27) == -1, "Getting iterator to location should return correct value.");

	grid<int>::const_iterator itr = find(myGrid.begin(), myGrid.end(), -1);
	CheckCondition(myGrid.getCoordinatesForIterator(itr).first == 21, "Should have found -1 at X coordinate 21.");
	CheckCondition(myGrid.getCoordinatesForIterator(itr).second == 27, "Should have found -1 at Y coordinate 27.");
}

/* Basic check that copying works correctly. */
void BasicAssignmentTest()
{
	const int width = 137, height = 42;
	grid<int> masterGrid(width, height);

	/* Fill the grid with random numbers. */
	generate(masterGrid.begin(), masterGrid.end(), rand);

	{ // Copy ctor
		grid<int> firstCopy = masterGrid;
		CheckCondition(firstCopy.getWidth() == masterGrid.getWidth(), "Copy ctor should copy width correctly.");
		CheckCondition(firstCopy.getHeight() == masterGrid.getHeight(), "Copy ctor should copy height correctly.");
		CheckCondition(equal(firstCopy.begin(), firstCopy.end(), masterGrid.begin()), "Copy ctor should copy elements correctly.");
	}

	{ // Assignment operator
		grid<int> firstCopy;
		firstCopy = masterGrid;
		CheckCondition(firstCopy.getWidth() == masterGrid.getWidth(), "Assignment operator should copy width correctly.");
		CheckCondition(firstCopy.getHeight() == masterGrid.getHeight(), "Assignment operator should copy height correctly.");
		CheckCondition(equal(firstCopy.begin(), firstCopy.end(), masterGrid.begin()), "Assignment operator should copy elements correctly.");
	}

	{ // Self-assignment
		grid<int> firstCopy = masterGrid;
		firstCopy = firstCopy;
		CheckCondition(firstCopy.getWidth() == masterGrid.getWidth(), "Self-assignment shouldn't break width.");
		CheckCondition(firstCopy.getHeight() == masterGrid.getHeight(), "Self-assignment shouldn't break height.");
		CheckCondition(equal(firstCopy.begin(), firstCopy.end(), masterGrid.begin()), "Self-assignment shouldn't break elements.");
	}
}

/* Basic check that the bracket operator is working correctly. */
void BasicBracketTest()
{
	const int width = 137, height = 42, numTrials = 1024;
	grid<int> masterGrid(width, height);

	/* Fill the grid with random numbers. */
	generate(masterGrid.begin(), masterGrid.end(), rand);

	bool success = true;
	for(int i = 0; i < numTrials; ++i)
	{
		int x = rand() % masterGrid.getWidth();
		int y = rand() % masterGrid.getHeight();

		if(masterGrid[x][y] != masterGrid.getAt(x, y))
		{
			success = false;
			break;
		}
	}

	CheckCondition(success, "Bracket operator should be indistinguishable from getAt.");
}

/* Basic check of relational operators. */
void BasicRelationalOpTest()
{
	const int numTests = 1024;
	const int maxDimension = 100;
	for(int i = 0; i < numTests; ++i)
	{
		/* Make two random grids. */
		grid<int> one(rand() % maxDimension, rand() % maxDimension);
		grid<int> two(rand() % maxDimension, rand() % maxDimension);
		generate(one.begin(), one.end(), rand);
		generate(two.begin(), two.end(), rand);

		/* Check relations between grids. */
		const bool less         = one <  two;
		const bool lessEqual    = one <= two;
		const bool equal        = one == two;
		const bool equal2       = two == one;
		const bool notEqual     = one != two;
		const bool notEqual2    = two != one;
		const bool greater      = one >  two;
		const bool greaterEqual = one >= two;

		/* Should be comparable. */
		if(!less && !equal && !greater)
			CheckCondition(false, "one < two || one == two || one > two");

		/* Comparison should obey trichotomy. */
		if((less && equal) || (less && greater) || (equal && greater))
			CheckCondition(false, "More than one of one < two, one == two, one > two.");
		
		/* Equality is symmetric. */
		if(equal != equal2)
			CheckCondition(false, "one == two  !=  two == one");

		/* Disequality is symmetric. */
		if(notEqual != notEqual2)
			CheckCondition(false, "one != two  !=  two != one");

		/* < implies <= */
		if(less && !lessEqual)
			CheckCondition(false, "one < two  && !(one <= two)");

		/* > implies >= */
		if(greater && !greaterEqual)
			CheckCondition(false, "one > two  && !(one >= two)");
	}
}

/* Basic check to confirm that all memory is getting cleaned up. */
void BasicMemoryTest()
{
	const int width = 137, height = 42;
	CheckCondition(Counter::getNumInstances() == 0, "Shouldn't have any instances prior to test.");
	
	/* Confirm that we're allocating correctly. */
	{
		grid<Counter> myGrid(width, height);
		CheckCondition(Counter::getNumInstances() == width * height, "Shouldn't be overallocating instances.");

		while(myGrid.getWidth() > 0)
			myGrid.resize(myGrid.getWidth() - 1, myGrid.getHeight());
	}

	/* Confirm we cleaned everything up. */
	CheckCondition(Counter::getNumInstances() == 0, "Shouldn't be any instances left.");
}

int main()
{
	srand((unsigned int)time(NULL)); // Seed the randomizer

	BasicFunctionalityTest();
	BasicIteratorTest();
	BasicAssignmentTest();
	BasicBracketTest();
	BasicRelationalOpTest();
	BasicMemoryTest();

	return 0;
}