/* Lecture code 11.0
 *
 * DebugVector example with full support for const-correctness.  Make sure that
 * you understand how the const overloaded functions work and why they're there.
 */
 
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <algorithm>
#include <iterator>
using namespace std;

/* A vector class that has some extra logging information.  Creating a
 * DebugVector prints out the time of creation to clog, and destroying
 * a DebugVector prints out the time of destruction to clog.
 */
template<typename T> class DebugVector
{
public:
	DebugVector();
	~DebugVector();

	/* Accessors return references to the element, so we don't need getAt/setAt.
	 * Note that this function is const-overloaded.
	 */
	T& at(int index);
	const T& at(int index) const;

	/* Iterator support.  These two typedefs convert raw C++ pointers into
	 * DebugVector<T>::iterator and DebugVector<T>::const_iterator.
	 */
	typedef T* iterator;
	typedef const T* const_iterator;

	/* begin and end are const-overloaded and just return the corresponding
	 * parts of the array.
	 */
	iterator begin() { return array; }
	iterator end()   { return array + logicalLength; }
	const_iterator begin() const { return array; }
	const_iterator end()   const { return array + logicalLength; }

	/* Some utility functions to insert, remove, and append elements. */
	void removeAt(int index);
	void insertAt(const T& value, int index);
	void append(const T& value);

	/* Size functions. */
	int size() const;
	bool isEmpty() const;
private:
	void grow();

	T *array;
	int logicalLength;
	int allocatedLength;
	static const int START_SIZE = 8;
};

/* Constructor.  Note that most of the variables are set up in the initializer list. */
template<typename T> DebugVector<T>::DebugVector() :
	array(new T[START_SIZE]), logicalLength(0), allocatedLength(START_SIZE)
{
	time_t currTime;
	time(&currTime);

	clog << "DebugVector created at " << ctime(&currTime) << endl;
}

/* Destructor clears out the vector and logs destruction information. */
template<typename T> DebugVector<T>::~DebugVector()
{
	time_t currTime;
	time(&currTime);

	clog << "DebugVector destroyed at " << ctime(&currTime) << endl;

	delete [] array;
	array = NULL;

	allocatedLength = logicalLength = 0;
}

/* Returns the number of elements - note the const modifier here since this doesn't
 * actually change the state of the object.
 */
template<typename T> int DebugVector<T>::size() const
{
	return logicalLength;
}

template<typename T> bool DebugVector<T>::isEmpty() const
{
	return logicalLength == 0;
}

/* Both versions of the const-overloaded at function. */
template<typename T> T& DebugVector<T>::at(int index)
{
	return array[index];
}

template<typename T> const T& DebugVector<T>::at(int index) const
{
	return array[index];
}

/* removeAt deletes an element and shuffles things down. */
template<typename T> void DebugVector<T>::removeAt(int index)
{
	for(int i = index; i < logicalLength - 1; i++)
		array[i] = array[i + 1];
	
	logicalLength --;
}

/* Inserts a new element, growing if necessary, then shuffles things down. */
template<typename T> void DebugVector<T>::insertAt(const T& value, int index)
{
	if(logicalLength == allocatedLength)
		grow();

	for(int i = logicalLength; i > index; i--)
		array[i] = array[i - 1];
		
	array[index] = value;
	logicalLength ++;
}

/* Appends an element - effectively a glorified call to insertAt. */
template<typename T> void DebugVector<T>::append(const T& value)
{
	insertAt(value, logicalLength);
}

/* Grows the vector when we need more space. */
template<typename T> void DebugVector<T>::grow()
{
	/* In case we were cleared out, reset to the start size. */
	if(allocatedLength == 0)
		allocatedLength = START_SIZE;

	allocatedLength *= 2;
	T *newArray = new T[allocatedLength];

	for(int i = 0; i < logicalLength; i++)
		newArray[i] = array[i];

	delete [] array;
	array = newArray;
}

const int NUM_INTS = 10;

void PrintVector(DebugVector<int> myVector)
{
	copy(myVector.begin(), myVector.end(), ostream_iterator<int>(cout, "\n"));
}

int main()
{
	/* Populate the vector. */
	DebugVector<int> myVector(NUM_INTS);
	for(int i = 0; i < NUM_INTS; ++i)
		myVector.at(i) = i;

	/* Print out the vector. */
	PrintVector(myCopy);
	return 0;
}