/*
	Twilight Prophecy 3D/Multimedia SDK
	A multi-platform development system for virtual reality and multimedia.

	Copyright (C) 1997-2001 by Twilight 3D Finland Oy Ltd.
*/
#ifndef PRCORE_ARRAY_HPP
#define PRCORE_ARRAY_HPP



namespace prcore
{

	template <class T>
	class Array
	{
		public:

		Array();
		Array(int size);
		Array(const Array& array);
		~Array();

		void		operator = (const Array&);
		T&			operator [] (int index);
		const T&	operator [] (int index) const;

		bool		IsEmpty() const;
		int			GetSize() const;
		const T*	GetArray() const;
		T*			GetArray();

		void		SetSize(int size, bool preserve=false);
		T&			PushBack(const T&);
		T&			PushBack();
		T&			PopBack();
		void		Remove(const T&);
		void		RemoveBlock(int index, int count);
		void		ResetIndex();
		void		TrimArray();
		void		Clear();

		private:

		int			mMaxSize;
		T*			mData;
		T*			mCursor;
	};


template <class T>
Array<T>::Array()
{
	mMaxSize = 0;
	mData = NULL;
	mCursor = NULL;
}


template <class T>
Array<T>::Array(int size)
{
	mMaxSize = size;
	mData = new T[ size ];
	mCursor = mData;
}


template <class T>
Array<T>::Array(const Array& array)
{
	*this = array;
}


template <class T>
Array<T>::~Array()
{
	Clear();
}


template <class T>
void Array<T>::operator = (const Array& array)
{
	int size = array.GetSize();
	SetSize( size, false );

	for ( int i=0; i<size; i++ )
		mData[ i ] = array.mData[ i ];
}


template <class T>
T& Array<T>::operator [] (int index)
{
	assert( index>=0 && index < mMaxSize );
	return mData[ index ];
}


template <class T>
const T& Array<T>::operator [] (int index) const
{
	assert( index>=0 && index < mMaxSize );
	return mData[ index ];
}


template <class T>
bool Array<T>::IsEmpty() const
{
	return (mCursor==mData) ? true : false;
}


template <class T>
int Array<T>::GetSize() const
{
	return mCursor - mData;
}


template <class T>
const T* Array<T>::GetArray() const
{
	return mData;
}


template <class T>
T* Array<T>::GetArray()
{
	return mData;
}


template <class T>
void Array<T>::SetSize(int size, bool preserve)
{
	if ( size == mMaxSize )
		return;

	if ( !size )
	{
		if ( mData )
			delete[] mData;

		mMaxSize = 0;
		mData = NULL;
		mCursor = NULL;

		return;
	}

	T* array = new T[ size ];

	if ( preserve )
	{
		int count = (mMaxSize > size) ? size : mMaxSize;
		for ( int i=0; i<count; i++ )
			array[ i ] = mData[ i ];
	}

	if ( mData )
		delete[] mData;

	mMaxSize = size;
	mData = array;
	mCursor = array + size;
}


template <class T>
T& Array<T>::PushBack(const T& type)
{
	T& data = PushBack();
	data = type;
	return data;
}


template <class T>
T& Array<T>::PushBack()
{
	if ( !mMaxSize )
	{
		mMaxSize = 4;
		mData = new T[ 4 ];
		mCursor = mData;
	}
	else if ( mCursor >= (mData+mMaxSize) )
	{
		int cursor = GetSize();
		int size = mMaxSize * 2 + 1;
		SetSize( size,true );
		mCursor = mData + cursor;
	}

	return *mCursor++;
}


template <class T>
T& Array<T>::PopBack()
{
	assert( GetSize() > 0 );
	return *mCursor--;
}


template <class T>
void Array<T>::Remove(const T& type)
{
	int count = GetSize();
	T* src = mData;
	T* dest = mData;

	for ( int i=0; i<count; i++ )
	{
		if ( *src != type )
		{
			if ( src != dest )
				*dest = *src;

			dest++;
		}
		src++;
	}
	mCursor = dest;
}


template <class T>
void Array<T>::RemoveBlock(int index, int count)
{
	T* dest = mData + index;
	T* src = mData + index + count;
	int left = mCursor - src;

	for ( int i=0; i<left; i++ )
	{
		*dest++ = *src++;
	}

	mCursor = dest;
}


template <class T>
void Array<T>::ResetIndex()
{
	mCursor = mData;
}


template <class T>
void Array<T>::TrimArray()
{
	int size = GetSize();
	SetSize( size,true );
}


template <class T>
void Array<T>::Clear()
{
	SetSize( 0,false );
}

} // namespace prcore



#endif