//*******************************************************************
//
// File: Asura_HeapArray.h                       
//
// Created: 15 Mar 2006
//
// Description: This is a simple array storage class.
//				Its task is to look after you array memory. Error check it. Delete It.
//				It is not intended for resizing, sort or other vector type methods.
//
// Wiki Documentation:
//				<http://asurawiki.rebellion.co.uk/twiki/bin/viewauth/Programming/AsuraHeapArray>
//
//*******************************************************************

#ifndef _ASURA_ARRAY_H_
#define _ASURA_ARRAY_H_

// other libraries
#include "Debugging/Asura_Assert.h"
#include "Core/Memory/Asura_Mem.h"

// internal dependencies

// DJG - 25/04/2007 - Need to include this explicitly now rather than having a forward declaration.
// PS3 GCC gets really annoyed at the point where it first sees the Asura_HeapArray< T>::Process
// function, as opposed to all other compilers which would only complain at the first instatiation
// of the template.
#include "ChunkSystem/Asura_Chunk_Stream.h"

/*
// forward declarations
class Asura_Chunk_SaveBlock_Stream;
*/

//*******************************************************************
// Constants
//*******************************************************************


//*******************************************************************
// Class Definitions
//*******************************************************************

template< class T > class Asura_HeapArray
{
public:
	Asura_HeapArray();
	~Asura_HeapArray();
	
	// Clear() - dellocates array memory and sets size to 0
	void Clear( void);

	// SetSize() - sets array size and allocates the needed memory. 
	void SetSize( const u_int uSize);
	
	// Shrink() - shrinks the active size of the array without any memory 
	// deallocation.
	void Shrink( const u_int uSize);
	
	// Size() - returns the Size of the currently active array
	u_int Size() const;
	
	// operator[] - Access any item on the Array
	const T& operator[]( const u_int uIndex) const;
	T& operator[]( const u_int uIndex);
	
	// Chunk load/save functions
	bool Process( Asura_Chunk_File_Stream& xStream);
#if defined( ASURA_TOOLS)
	bool Export( Asura_Chunk_SaveBlock_Stream& xStream) const;
#endif

#ifdef ASURA_TOOLS
	// Resizes the array , preserving the old data 
	void Resize( const u_int uSize);
#endif

	// Remove an entry from the array, without any memory deallocation
	void RemoveElement( const u_int uIndex );

private:
	// non-copyable: private and unimplemented copy constructor and operator=
	Asura_HeapArray(const Asura_HeapArray&);
	Asura_HeapArray& operator=(const Asura_HeapArray&);

	// private data
	u_int m_uArraySize;
	T* m_ptArray;
};

//*******************************************************************
// Inline Function Definitions
//*******************************************************************

// DLH - Access any item on the Array
template< class T > inline T& Asura_HeapArray< T>::operator[]( const u_int uIndex)
{
	Asura_Assert( NULL != this->m_ptArray, "Array has not be allocated.");
	Asura_Assertf( uIndex < this->m_uArraySize, ("Trying to access array item %d. The Array only has %d items.", uIndex, this->m_uArraySize));
	
	T& xData = this->m_ptArray[ uIndex];
	
	return( xData);
}

// DLH - Access any const item on the const Array
template< class T > inline const T& Asura_HeapArray< T>::operator[]( const u_int uIndex) const
{
	Asura_Assert( NULL != this->m_ptArray, "Array has not be allocated.");
	Asura_Assertf( uIndex < this->m_uArraySize, ("Trying to access array item %d. The Array only has %d items.", uIndex, this->m_uArraySize));
	
	const T& xData = this->m_ptArray[ uIndex];
	
	return( xData);
}

// DLH - Returns the Size of the currently active array
template< class T > inline u_int Asura_HeapArray< T>::Size( void) const
{
	// DLH - 15 March 2006 12:36:21 - Check that if the pointer is NOT setup then the size is 0
	Asura_Assert( NULL != this->m_ptArray || 0 == this->m_uArraySize, "Asura_HeapArray Mismatch in the data pointer and the size.");
	
	return( this->m_uArraySize);
}

// DLH - Clears and dellocates all data from the array
// DLH - Some memory handling is performed in this routine.
template< class T > inline void Asura_HeapArray< T>::Clear( void)
{
	delete[] this->m_ptArray;
	this->m_ptArray = NULL;
	
	this->m_uArraySize = 0;
}

// DLH - Set the Data Size and allocate the needed memory. 
// DLH - Some memory handling is performed in this routine.
template< class T > inline void Asura_HeapArray< T>::SetSize( const u_int uSize)
{
	Asura_Assertf( NULL == this->m_ptArray, ("Asura_HeapArray already has data in it. Please clear() it first."));
	Asura_Assertf( 0 == this->m_uArraySize, ("Asura_HeapArray already has data in it. Please clear() it first."));
	
	if( 0 == uSize)
	{
		return;
	}
	
	this->m_uArraySize = uSize;
	this->m_ptArray = new T[ uSize];
	
	Asura_Assertf( NULL != this->m_ptArray, ("Asura_HeapArray failed to allocate array of size %d items. (%d bytes)", uSize, uSize * sizeof( T)));
}

// DLH - Shrinks the active size of the array without any memory allocation
template< class T > inline void Asura_HeapArray< T>::Shrink( const u_int uSize)
{
	Asura_Assertf( NULL != this->m_ptArray, ("Asura_HeapArray does not have any data in it!"));
	Asura_Assertf( 0 < this->m_uArraySize, ("Asura_HeapArray does not have any data in it!"));
	Asura_Assertf( uSize <= this->m_uArraySize, ("Asura_HeapArray does not have enought data in it to shink it by this amount!"));
	
	if( this->m_uArraySize <= uSize)
	{
		return;
	}
	
	this->m_uArraySize = uSize;

	// JAT 30/08/07 - if we shrink to zero size then deallocate the array
	if ( uSize == 0 )
	{
		this->Clear();
	}
}

template< class T > inline Asura_HeapArray< T>::Asura_HeapArray( void)
{	
	this->m_ptArray = NULL;
	this->m_uArraySize = 0;
}

template< class T > inline Asura_HeapArray< T>::~Asura_HeapArray( void)
{	
	this->Clear();
}

// DLH - Allows the Array to be streamed to a file
template< class T > inline bool Asura_HeapArray< T>::Process( Asura_Chunk_File_Stream& xStream)
{
	u_int uArraySize;
	xStream >> uArraySize;
	
	this->SetSize( uArraySize);
	
	for( uint32 uArrayIndex = 0; uArrayIndex < uArraySize; uArrayIndex += 1)
	{
		T& tData = (*this)[ uArrayIndex];
		
		if( false == tData.Process( xStream))
		{
			return( false);
		}
	}
	
	return( true);
}

#if defined( ASURA_TOOLS)
// DLH - Allows a streamed array to be streamed from a file
template< class T > inline bool Asura_HeapArray< T>::Export( Asura_Chunk_SaveBlock_Stream& xStream) const
{
	const uint32 uArraySize = this->m_uArraySize;
	xStream << static_cast< uint32>( uArraySize);
	for( uint32 uArrayIndex = 0; uArrayIndex < uArraySize; uArrayIndex += 1)
	{
		const T& tData = (*this)[ uArrayIndex];
		
		if( false == tData.Export( xStream))
		{
			return( false);
		}
	}
	
	return( true);
}
#endif

#ifdef ASURA_TOOLS
template< class T > inline void Asura_HeapArray< T>::Resize( const u_int uSize)
{
	//If reducing the size , then shrink will be sufficient
	if(uSize == m_uArraySize) return;
	if(uSize < m_uArraySize)
	{
		Shrink(uSize);
		return;
	}

	//allocate a new array , and copy the old values across
	T* pxNewArray = new T[ uSize];
	Asura_Assertf( pxNewArray, ("Asura_HeapArray failed to allocate array of size %d items. (%d bytes)", uSize, uSize * sizeof( T)));

	for(u_int i=0; i<m_uArraySize; i++)
	{
		pxNewArray[i] = m_ptArray[i];
	}
	
	delete [] m_ptArray;
	m_ptArray = pxNewArray;
	m_uArraySize = uSize;
}
#endif //ASURA_TOOLS

template< class T > inline void Asura_HeapArray< T>::RemoveElement( const u_int uIndex )
{
	Asura_Assertf( uIndex < m_uArraySize, ("Trying to remove array item %u. The Array only has %u items.", uIndex, m_uArraySize));

	if( uIndex < m_uArraySize )
	{
		if( m_uArraySize == 1 )
		{
			// Array only has one item
			m_uArraySize = 0;
			return;
		}

		if( uIndex < ( m_uArraySize - 1 ) )
		{
			u_int uToShift = ( m_uArraySize - uIndex ) - 1;
			Asura_Mem::Move( &m_ptArray[ uIndex ], &m_ptArray[ uIndex + 1 ], uToShift * sizeof( T ) );
		}
		--m_uArraySize;
	}
}

//*******************************************************************
//
// (c) 2006 Rebellion (Derby)
//
// Warning: This computer program is protected by copyright law and 
// international treaties. Unauthorized reproduction or distribution
// of this program, or any portion of it, may result in severe civil and
// criminal penalties, and will be prosecuted to the maximum extent 
// possible under the law.
//
//*******************************************************************

#endif // _ASURA_ARRAY_H_
