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

		MCExtensibleArray.h
		Copyright: (c) 2001 Eovia Corp. All rights reserved.

		Author:	Charles
		Date:	5/8/2003

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

#ifndef __MCExtensibleArray__
#define __MCExtensibleArray__

#if CP_PRAGMA_ONCE
#pragma once
#endif


#include "MCPtrArray.h"
#include "MCSMPCountedObject.h"
#include "MCCountedPtrArray.h"
#include "MCCountedPtrHelper.h"

#include <new.h>

template<class T> class TMCCountedBlock : public TMCSMPCountedObject
{
public:
	TMCCountedBlock(int32 size,boolean zeroMem);
	~TMCCountedBlock();

	static void Create(TMCCountedBlock<T>** newBlock,int32 size,boolean zeroMem);

	T*		GetDataPtr() { return fData; }
protected:
	T*				fData;
	const int32		fSize;		// element count
};

template<class T> TMCCountedBlock<T>::TMCCountedBlock(int32 size,boolean zeroMem) : fSize(size)
{
	// in the safe library we cannot use new, so we initialize the block manually (Charles)
	fData = (T*)MCmalloc(size*sizeof(T));

	T* inDataStart = fData;

	//T inInitValue;

	for (int32 index=0;index<size;index++)
	{
		new (inDataStart) T(); //inInitValue);
		inDataStart++;
	}
	if (zeroMem)
	{
		MCmemfill(0,fData,sizeof(T)*size);
	}
}

template<class T> void TMCCountedBlock<T>::Create(TMCCountedBlock<T>** newBlock,int32 size,boolean zeroMem)
{
	TMCCountedCreateHelper< TMCCountedBlock<T> > result(newBlock);

	result = new TMCCountedBlock<T>(size,zeroMem);
}


template<class T> TMCCountedBlock<T>::~TMCCountedBlock()
{
	// in the safe library we cannot use delete, so we delete the block manually (Charles)
	T* inDataStart = fData;
	for (int32 index=0;index<fSize;index++)
	{
		inDataStart->~T();
		inDataStart++;
	}
	MCfree(fData);
	//delete [] fData;
}

// ****************************************************************************
//								TMCExtensibleArray
// 
// -Description:
//
//
template<class T> class TMCExtensibleArray : public TMCObject
{
public:
	// note that the maximum of 
	TMCExtensibleArray(int32 elemCount,int32 logMaxElemCount,int32 logGrowsize,boolean zeroMem);
	~TMCExtensibleArray();

	// creates a "copy" of the array. The two copy share the same data blocks. This copy can
	// be used to read the array data in SMP while it is modified without fearing that a block
	// is deleted. You should never modify the copy (Charles 04/27/2004)
	TMCExtensibleArray(const TMCExtensibleArray& array);


	void AddElem(const T& data);
	void RemoveLastElem();


	// Returns a reference to the data at the passed in index.
	T& 			operator[](int32 i);
	const T&	operator[](int32 i) const;

	void			SetElemCount(int32 elemCount,boolean zeroMem);

	inline	int32	GetElemCount(void) const	{return fElemCount;}
	
	inline int32 GetLogMaxElemCount() const { return fLogMaxElemCount; }
	inline int32 GetLogGrowSize() const { return fLogGrowSize; }
protected:
	const int32			fLogMaxElemCount;
	const int32			fLogGrowSize;
	const int32			fMask;			// 2^fLogGrowSize -1

	const boolean		fZeroMem;

	int32				fElemCount;

	TMCCountedPtrArray< TMCCountedBlock<T> > fData; // an array of pointers to arrays

	TMCPtrArray<T>		fIndex;			// for faster access															
};

template<class T> TMCExtensibleArray<T>::TMCExtensibleArray(const TMCExtensibleArray& array)
: fLogMaxElemCount(array.fLogMaxElemCount),
  fLogGrowSize(array.fLogGrowSize),
  fMask(array.fMask),
  fZeroMem(array.fZeroMem)
{
	fElemCount			= array.fElemCount;
	fData				= array.fData;
	fIndex				= array.fIndex;
}

template<class T> TMCExtensibleArray<T>::TMCExtensibleArray(int32 elemCount,int32 logMaxElemCount,int32 logGrowSize,boolean zeroMem)
											: fLogMaxElemCount(logMaxElemCount),
											  fLogGrowSize(logGrowSize),
											  fMask((1<<logGrowSize)-1),
											  fZeroMem(zeroMem),
											  fData(1<<(logMaxElemCount-logGrowSize)),
											  fIndex(1<<(logMaxElemCount-logGrowSize),true)
{
	MCAssert(logMaxElemCount>=logGrowSize);
	
	if (elemCount>0)
	{
		int32 arrayCount = 1 + ((elemCount-1) >> logGrowSize);

		for (int32 arrayIndex=0;arrayIndex<arrayCount;arrayIndex++)
		{
			const int32 count = 1<<fLogGrowSize;

			TMCCountedPtr< TMCCountedBlock<T> > newBlock;
			TMCCountedBlock<T>::Create(&newBlock,count,zeroMem);

			//T* array = new T[count];

			fData.SetElem(arrayIndex,newBlock);
			fIndex[arrayIndex] = newBlock->GetDataPtr();
			//fIndex[arrayIndex] = array;

			//if (zeroMem)
			//{
			//	MCmemfill(0,array,sizeof(T)*count);
			//}
		}
	}
	fElemCount = elemCount;
}

template<class T> TMCExtensibleArray<T>::~TMCExtensibleArray()
{
//	const int32 subArrayCount = fIndex.GetElemCount();

//	for (int32 i=0;i<subArrayCount;i++)
//	{
//		delete [] fIndex[i];
//	}
}



template<class T> inline void TMCExtensibleArray<T>::AddElem(const T& data)
{
	int32 index = fElemCount >> fLogGrowSize;

	T* array = fIndex[index];

	if (!array)
	{
		TMCCountedPtr< TMCCountedBlock<T> > newBlock;
		TMCCountedBlock<T>::Create(&newBlock,1<<fLogGrowSize,fZeroMem);
		
		fData.SetElem(index,newBlock);
		fIndex[index] = newBlock->GetDataPtr();
		//array = new T[1<<fLogGrowSize];
		//fIndex[index] = array;

		array = fIndex[index];
	}

	array[fElemCount & fMask] = data;

	fElemCount++;
}

template<class T> inline void TMCExtensibleArray<T>::RemoveLastElem()
{
	fElemCount--;

	int32 subIndex = fElemCount & fMask;

	if (subIndex == 0)
	{
		int32 index = fElemCount >> fLogGrowSize;

		MCAssert(fIndex[index]);

		fData.SetElem(index,NULL);	// calls release on the block
		fIndex[index]	= NULL;
	}
}



template<class T> inline  T&  TMCExtensibleArray<T>::operator[](int32 i)
{
	MCAssert(i>=0 && i < fElemCount);

	int32 index = (i >> fLogGrowSize);

	T* array = fIndex[index];

	MCAssert(array);

	return array[i & fMask];
}


template<class T> inline  const T&	TMCExtensibleArray<T>::operator[](int32 i) const
{
	MCAssert(i>=0 && i < fElemCount);

	int32 index = (i >> fLogGrowSize);

	T* array = fIndex[index];

	MCAssert(array);

	return array[i & fMask];
}


#endif
