#pragma once

#include <malloc.h>
#include <assert.h>

//#define ASSERT_SMART_ARRAY
//#define INITIAL_CAPACITY 16

template <class T> class SmartArray
{
  public:
//    static const int initialCapacity = 16;
	int size;
  
  protected:
	T* a;
	int capacity;

  public:
	SmartArray()
	{
		size = 0;
		capacity = 0; //INITIAL_CAPACITY;
		a = NULL;
		//a = (T*)malloc(capacity * sizeof(T));
	}

	SmartArray(int initialCapacity)
	{
		size = 0;
		capacity = initialCapacity;
		a = (T*)malloc(capacity * sizeof(T));
	}

	~SmartArray()
	{
		free(a);
	}

	inline void ensureCapacity(const int c)
	{
		if(c > capacity)
		{
			resize(c*2);
		}
	}

	inline int getCapacity()
	{
		return capacity;
	}

	inline void resize(const int c)
	{
		capacity = c;
		a = (T*)realloc(a, capacity * sizeof(T));
	}

	inline void setSize(const int c)
	{
		if(c > capacity)
		{
			resize(c);
		}
		size = c;
	}

	inline T& get(const int i)
	{
		#ifdef ASSERT_SMART_ARRAY
		assert(i >= 0 && i < capacity);
		#endif
		return a[i];
	}

	inline T& operator[] (const int i)
	{
		#ifdef ASSERT_SMART_ARRAY
		assert(i >= 0 && i < capacity);
		#endif
		return a[i];
	}

	inline void put(const int i, const T& e)
	{
		#ifdef ASSERT_SMART_ARRAY
		assert(i >= 0 && i < size);
		#endif
		a[i] = e;
	}

	inline void putAutoSize(const int i, const T& e)
	{
		#ifdef ASSERT_SMART_ARRAY
		assert(i >= 0);
		#endif
		if(i >= capacity)
		{
			size = i + 1;
			resize(size * 2);
		}
		a[i] = e;
	}

	inline void push(const T& e)
	{
		#ifdef ASSERT_SMART_ARRAY
		assert(size < capacity);
		#endif
		a[size] = e;
		size ++;
	}

	inline void pushAutoSize(const T& e)
	{
		if(size >= capacity)
		{
			resize((size + 1) * 2);
		}
		a[size] = e;
		size ++;
	}

	void toString()
	{
		printf("Contents of SmartArray (size=%d capacity=%d):\n", size, capacity);
		for(int i = 0; i < size; i++)
		{
			printf("%d:%f\n", i, a[i]);
		}
	}

	/*
	void shuffle()
	{
		printf("I think this shuffle function is broken\n");

		for(int i = 0; i < size; i++)
		{
			int j = (i + rand()) % size;
			
			T temp = a[i];
			a[i] = a[j];
			a[j] = temp;
		}
	}
	*/

	/*
	inline void shuffle(int first, int last)
	{
		if(last <= first) return;

		//int first = 0;
		//int last;
		//if(_size == -1) last = size - 1;
		//else last = _size - 1;

		const int _RANDOM_BITS = 15;	// minimum random bits from rand()
		const int _RANDOM_MAX = (1U << _RANDOM_BITS) - 1;

		int next = first;

		for(unsigned long index = 2; ++next != last; ++index)
		{
			// assume unsigned long big enough for _Diff count
			unsigned long Rm = _RANDOM_MAX;
			unsigned long Rn = ::rand() & _RANDOM_MAX;
			int iters = 0;
			for (; Rm < index && Rm != ~0UL; Rm = (Rm << _RANDOM_BITS) | _RANDOM_MAX)
			{
				Rn = (Rn << _RANDOM_BITS) | ::rand();	// build random value
				iters ++;
			}
			if(iters > 1)
			{
				printf("Iters greater than 1\n");
			}

			// Swap the next item on the list with any of the preceeding items, including itself.
			// Does this mean that items close to the beginning are more likely to stay at the beginning?
			int diff = Rn % index; // index is 1+next if first = 0
			int swap_with = first + diff;

			T temp = a[next];
			a[next] = a[swap_with];
			a[swap_with] = temp;
		}
	}

	inline void shuffle()
	{
		shuffle(0, size-1);
	}
	*/

};
