/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


#ifndef	__NARRAY__
#define	__NARRAY__


		#include "alloc/ialloc.h"
		#include "memory/memory.h"
		#include "assert/nassert.h"


namespace GS {

/*!
	@short	Managed array.
	@author	Emmanuel Julien (ejulien@gsworks.fr)
*/
template <class T> class Array
{
		uint			count;
		T				*data;

	#if	__ENABLE_ALLOCATION_STAT__
		Alloc::System	system;
	#endif

public:

inline	operator	T	*() const
		{	return data;	}
inline	T				*c_ptr() const
		{	return data;	}

inline	T				&operator [] (int n) const
		{	return data[n];	}
inline	T				&operator [] (uint n) const
		{	return data[n];	}

		void			operator = (Array <T> &o)		///< Transfer assignation.
		{	Transfer(o);	}

inline	T				*Start() const
		{	return data;	}
inline	T				*End() const
		{	return &data[count];	}

inline	bool			IsValid() const
		{	return data ? true : false;	}
inline	bool			IsNull() const
		{	return data ? false : true;	}

		/// Return the number of elements of type T in the buffer.
inline	uint			GetCount() const
		{	return count;	}
		/// Return the buffer size in bytes.
inline	size_t			GetSize() const
		{	return count * sizeof(T);	}

		void			Free()
		{
			if	(data)
				__NSTAT_DELETE(count * sizeof(T), system);
			//	_safe_delete_array(data);
			delete[] data;
			data = 0;
			count = 0;
		}

		/// Reallocate buffer elements.
		bool			Reallocate(uint new_count)
		{
			if	(new_count == count)
				return true;

			if	(T *new_data = new T[new_count])
			{
				__NSTAT_ALLOC(new_count * sizeof(T), system);
				Memory::Copy(new_data, data, GetSize());

				if	(data)
					__NSTAT_DELETE(count * sizeof(T), system);
			//	_safe_delete_array(data);
				delete[] data;
				data = 0;

				data = new_data;
				count = new_count;
			}
			else
				return false;

			return true;
		}

		/// Allocate buffer elements.
		bool			Allocate(uint _count)
		{
			/// @note We could add a small tolerance here to potentially reduce fragmentation?
			if	(_count == count)
				return true;

			Free();

			if	(_count && ((data = new T[_count]) == 0))
				return false;

			__NSTAT_ALLOC(_count * sizeof(T), system);
			count = _count;
			return true;
		}

		/// Relinquish ownership of the managed memory block.
		T				*Detach()
		{
			T *r = data;
			data = 0;
			count = 0;
			return r;
		}
		/// Take ownership of a managed memory block.
		void			Attach(uint _count, T *_data)
		{
			Free();
			count = _count;
			data = _data;
		}

		/// Transfer data buffer.
		void			Transfer(Array <T> &b)
		{
			Free();
			count = b.GetCount();
			data = b.Detach();
		#if	__ENABLE_ALLOCATION_STAT__
			__NSTAT_DELETE(count * sizeof(T), b.system);
			__NSTAT_ALLOC(count * sizeof(T), system);
		#endif
		}
		/// Clone data buffer.
		bool			Clone(const Array <T> &b)
		{
			Free();
			if	(!Allocate(b.GetCount()))
				return false;
			for (uint n = 0; n < GetCount(); ++n)
				data[n] = b[n];
			return true;
		}

		/// Fill data buffer.
		void			Fill(const T &v, uint from = 0, uint to = 0)
		{
			if	(to <= 0)
				to = count + to;

			__ASSERT__(from <= count);
			__ASSERT__(to <= count);
			for (uint n = from; n < to; ++n)
				data[n] = v;
		}

		/// Swap two data buffer.
static	void			Swap(Array <T> &a, Array <T> &b)
		{
			uint count_a = a.GetCount(), count_b = b.GetCount();
			T *data_a = a.Detach(), *data_b = b.Detach();
			a.Attach(count_b, data_b);
			b.Attach(count_a, data_a);
		}

		Array(uint _count, Alloc::System sys = Alloc::General)
		{
			data = 0; count = 0;
		#if	__ENABLE_ALLOCATION_STAT__
			system = sys;
		#endif
			Allocate(_count);
		}
		Array(uint _count, const T *_data, Alloc::System sys = Alloc::General)
		{
			data = 0; count = 0;
		#if	__ENABLE_ALLOCATION_STAT__
			system = sys;
		#endif
			if	(Allocate(_count))
				Memory::Copy(data, _data, sizeof(T) * _count);
		}
		Array(const Array <T> &array, Alloc::System sys = Alloc::General)	///< Copy constructor.
		{
			data = 0; count = 0;
		#if	__ENABLE_ALLOCATION_STAT__
			system = sys;
		#endif
			Allocate(array.GetCount());
			Memory::Copy(data, array.c_ptr(), array.GetSize());
		}
		Array(Alloc::System sys = Alloc::General)
		{
			data = 0; count = 0;
		#if	__ENABLE_ALLOCATION_STAT__
			system = sys;
		#endif
		}
		~Array()
		{	Free();	}
};

} // GS


#endif	// __NARRAY__
