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

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

		Author:	Jerome
		Date:	6/2/2005

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

#ifndef __MCSMPArray__
#define __MCSMPArray__

#if CP_PRAGMA_ONCE
#pragma once
#endif

#include "MCArray.h"
#include "MCSMP.h"

// =================================================================================================================
// TMCSMPArray
// =================================================================================================================

#ifndef __MWERKS__
	//disable "this used in base member initializer list" warning from VC
#pragma warning( disable : 4355)
#endif

template<class T> class TMCSMPArrayRequest;


/// \addtogroup MCCore
/// \{

/// <P>TMCSMPArray is a way to get TMCArray in a SMP context wihtout allocating memory each time.</P>
///
/// <P>It behaves like a stack. So a function that uses an SMPArray can be recursively called.
/// </P>
///
/// <P>Usage:<BR>
///	TMCSMPArrayRequest< _TYPE_ > request;<BR>
///	TMCArray< _TYPE_ >& myArray = fArrayVolumeRec.GetArray( request );</P>
///
/// <P>Prelated class: TMCArray</P>
///
template <class T>  class TMCSMPArray : public TMCObject
{
public:

	friend class TMCSMPArrayRequest<T>;
	/// Construt a dynamic array (TMCArray) SMP-friendly. That is to say, the array will not be allocated
	/// on the stack. But all the Threads will have their own.
	///
	TMCSMPArray() : fStackDepth(1), fAverageElemSpace(1)
	{ 
		fTLSArray	= NewTLSWithDelete(FreeArray); 
		fStackIndex	= NewTLS();
	};

	/// Destructor. Remove the TLS. Note: the memory used by the arrays is freed when the thread dies.
	///
	virtual ~TMCSMPArray() 
	{ 
		DeleteTLS(fTLSArray); 
		DeleteTLS(fStackIndex); 
	};

	// Function to use:

	/// [Optional] Can be used to optimize memory allocation by setting directly 
	/// the number of wanted recursion. 
	/// \note Call it before asking the first Array.
	/// 
	void				SetStackDepth			( int32 depth )		{ fStackDepth = depth; };

	/// [Optional] Can be used to optimize memory allocation by setting directly the 
	/// the size of the array.
	/// \note Call it before asking the first Array.
	///
	void				SetAverageElemSpace		( int32 elemSpace ) { fAverageElemSpace = elemSpace; };

	/// This is the way to get a reference to an TMCArray
	///
	/// \param request   is a class instanciated on the stack to help for asking and 
	///					 destructing the array. Just create it like that :
	///					 TMCSMPArrayRequest request;
	///					 <B>or</B>
	///					 TMCSMPArrayRequest request(size_of_the_wanted_array);
	///					 
	TMCArray<T>&		GetArray				( TMCSMPArrayRequest<T>& request );


protected :

	/// Pop the last gotten array
	///
	void PopArray();

	/// This is to remove the all allocated memory when the thread die
	///
	static void	FreeArray(void **privData); // this function is static

	// =================================================================================================================
	// Data
	// =================================================================================================================
	
	int32				fStackDepth;		// The size of the stack
	int32				fAverageElemSpace;	// Size of the allocated array at initialization
	TMCLocalStorage*	fTLSArray;
	TMCLocalStorage*	fStackIndex;		// Current index of the stack (every index above are empty)
};
/// \}


// =================================================================================================================
// GetArray
// =================================================================================================================

template<class T> TMCArray<T>& TMCSMPArray<T>::GetArray( TMCSMPArrayRequest<T>& request )
{
	TMCPtrArray< TMCArray<T> >* arrays = (TMCPtrArray< TMCArray<T> >*) GetTLS(fTLSArray);
	if( !arrays ) // first use
	{
		arrays = new TMCPtrArray< TMCArray<T> >((uint32)0, fStackDepth, (fStackDepth+1)/2, false);
		SetTLS(fTLSArray, (void*) arrays);
	}
	
	MCAssert(arrays);

	int32 count = arrays->GetElemCount();
	int32 elemSpace = (request.GetElemSpace()>fAverageElemSpace)?request.GetElemSpace():fAverageElemSpace; 

	// Get the current stack index
	int32 stackIndex = (int32) GetTLS(fStackIndex);
	
	MCAssert( stackIndex <= count );

	// The stack may not contain enough array
	// So we need to add a new array
	if( stackIndex == count )
	{		
		TMCArray<T>* array = new TMCArray<T>((uint32)0, elemSpace, (elemSpace+1)/2, false);
		arrays->AddElem(array);
	}
	// We might want a bigger array, so let's enlarge it
	else if( (int32) (*arrays)[stackIndex]->GetElemSpace() < elemSpace ) 
	{
		(*arrays)[stackIndex]->SetElemSpace(elemSpace);
	}

	request.SetSMPArray(this);

	// Store the incremented stack index
	SetTLS(fStackIndex, (void*) (stackIndex+1));

	MCAssert( (*arrays)[stackIndex]->GetElemCount() == 0 );
	return *(*arrays)[stackIndex]; //
}

// =================================================================================================================
// GetArray
// =================================================================================================================
template<class T> void TMCSMPArray<T>::PopArray()
{
	// Get a pointer to the stack
	TMCPtrArray< TMCArray<T> >* arrays = (TMCPtrArray< TMCArray<T> >*) GetTLS(fTLSArray);
	
	if( MCVerify(arrays) )
	{	
		// Get the current stack index
		int32 stackIndex = (int32) GetTLS(fStackIndex);
		MCAssert(stackIndex>0); // If not... serious trouble this means that PopArray has been called!
		
		stackIndex--;

		(*arrays)[stackIndex]->SetElemCount(0);

		// Store the decremented stack index
		SetTLS(fStackIndex, (void*) (stackIndex));
	}
}

// =================================================================================================================
// FreeArray
// =================================================================================================================
template<class T> void TMCSMPArray<T>::FreeArray(void **privData) // this function is static
{
	// Get a pointer to the stack
	TMCPtrArray< TMCArray<T> >* arrays = (TMCPtrArray< TMCArray<T> >*)*privData;
	if(arrays)
	{
		arrays->DeleteAndRemoveAll();
		delete arrays;
	}
	// Note: no need to set the TLS to NULL. Because this code is executed when the thread is 
	// killed.
}




template<class T> class TMCSMPArrayRequest : public TMCObject
{
public:
	/// Construct a request on the stack to ask for an array.
	///
	TMCSMPArrayRequest() : fElemSpace(1), fSMPArray(NULL)	{};

	/// Construct a request on the stack to ask for an array with at least 'space' spaces in it.
	///
	TMCSMPArrayRequest( int32 space )	: fElemSpace((space>0)?space:1), fSMPArray(NULL) {};

	virtual ~TMCSMPArrayRequest	()  { if(fSMPArray) fSMPArray->PopArray(); };
	
	int32 GetElemSpace			()	{ return fElemSpace; };

	void SetSMPArray(TMCSMPArray< T >* ptr) { fSMPArray=ptr; };

protected:
	
	TMCSMPArray< T >*					fSMPArray; 
	int32								fElemSpace;		// Size of the array at initialization
};

#endif // for the entire file