//===========================================================================//
// File:	MArray.hpp                                                          //
// Project: MUNGA           Brick:              									  //
// Contents: 																					  //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 08/17/97  ECH  Initial coding.                                            //
//---------------------------------------------------------------------------//
// Copyright (C) 1994-1995, Virtual World Entertainment, Inc.                //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#pragma once

#include "Stuff.hpp"

namespace Stuff {

	class MemoryStream;

	//##########################################################################
	//#########################    ReadOnlyArrayOf    ###########################
	//##########################################################################

	template <class T> class ReadOnlyArrayOf
		#if defined(_ARMOR)
			: public Stuff::Signature
		#endif
	{
	public:
		ReadOnlyArrayOf()
			{Check_Pointer(this); m_data = NULL; m_length = 0;}

		void
			TestInstance() const
				{}

		const T&
			operator[](size_t i) const
				{
					Check_Object(this); Check_Pointer(m_data); Verify(i < m_length);
					return m_data[i];
				}

		void
			AssignData(const T *data, size_t length)
				{Check_Object(this); m_data = data; m_length = length;}

		size_t 
			GetLength() const
				{return m_length;}
		size_t 
			GetSize() const
				{return m_length * sizeof(T);}
		const T*
			GetData() const
				{return m_data;}

		friend MemoryStream&
			MemoryStreamIO_Write(
				MemoryStream *stream,
				const ReadOnlyArrayOf<T> *array
			);

		friend MemoryStream&
			MemoryStreamIO_Read(
				MemoryStream *stream,
				ReadOnlyArrayOf<T> *array
			);

	protected:
		const T 
			*m_data;
		size_t
			m_length;
	};

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	template <class T> MemoryStream&
		MemoryStreamIO_Write(
			MemoryStream *stream,
			const ReadOnlyArrayOf<T> *array
		)
	{
		Check_Object(stream);
		Check_Pointer(array);
		MemoryStreamIO::Write(stream, &array->m_length);
		if (array->m_length > 0)
			stream->WriteBytes(array->m_data, array->m_length * sizeof(T));
		return *stream;
	}

	template <class T> MemoryStream&
		MemoryStreamIO_Read(
			MemoryStream *stream,
			ReadOnlyArrayOf<T> *array
		)
	{
		Check_Object(stream);
		Check_Pointer(array);
		MemoryStreamIO::Read(stream, &array->m_length);
		if (array->m_length > 0)
		{
			array->m_data = Cast_Pointer(const T*,stream->GetPointer());
			stream->AdvancePointer(array->m_length * sizeof(T));
		}
		else
			array->m_data = NULL;
		return *stream;
	}

	//##########################################################################
	//#########################    DynamicArrayOf    ###########################
	//##########################################################################

	template <class T> class DynamicArrayOf:
		public ReadOnlyArrayOf<T>
	{
	public:
		DynamicArrayOf()
			{}
		DynamicArrayOf(const DynamicArrayOf<T>& array)
			{Check_Pointer(this); Check_Object(&array); CopyArray(array);}
		DynamicArrayOf(size_t length);
		DynamicArrayOf(const T &value, size_t length)
			{Check_Pointer(this); AssignValue(value, length);}
		DynamicArrayOf(const T *data, size_t length)
			{Check_Pointer(this); AssignData(data, length);}
		~DynamicArrayOf()
			{
				if (m_data)
				{
					Check_Pointer(m_data);
					delete[] const_cast<T*>(m_data);
				}
			}

		void
			TestInstance() const
				{}

		DynamicArrayOf<T>&
			operator=(const DynamicArrayOf<T>& array)
				{ 
					if (this != &array)
						CopyArray(array);
					return *this; 
				}
		T&
			operator[](size_t i)
				{Check_Pointer(m_data); Verify(i < m_length); return const_cast<T&>(m_data[i]);}
		const T&
			operator[](size_t i) const
				{Check_Pointer(m_data); Verify(i < m_length); return m_data[i];}

		void
			AssignValue(const T &value, size_t length);
		void
			AssignData(const T *data, size_t length);

		void
			SetLength(size_t length);
		T*
			GetData()
				{return const_cast<T*>(m_data);}
		const T*
			GetData() const
				{return m_data;}

		friend MemoryStream&
			MemoryStreamIO_Write(
				MemoryStream *stream,
				const DynamicArrayOf<T> *array
			);

		friend MemoryStream&
			MemoryStreamIO_Read(
				MemoryStream *stream,
				DynamicArrayOf<T> *array
			);

	private:
		void
			SetStorageLength(size_t length);
		void
			CopyArray(const DynamicArrayOf<T>&);
	};

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	template<class T>
		DynamicArrayOf<T>::DynamicArrayOf(size_t length_to_set) 
	{
		SetStorageLength(length_to_set);
		Verify(m_length == length_to_set);
		#if defined(_ARMOR)
			if (length_to_set > 0)
				Check_Pointer(m_data);
			else
				Verify(!m_data);
		#endif
	}

	template <class T> void
		DynamicArrayOf<T>::AssignValue(const T &value, size_t length_to_set) 
	{
		SetStorageLength(length_to_set);
		// Do not memcopy, object semantics may be required
		for (int i = 0; i < m_length; i++)
			(const_cast<T*>(m_data))[i] = value;
	}

	template <class T> void
		DynamicArrayOf<T>::AssignData(const T *data_to_copy, size_t length_to_set) 
	{
		Check_Pointer(data_to_copy);
		SetStorageLength(length_to_set);
		// Do not memcopy, object semantics may be required
		for (int i = 0; i < m_length; i++)
			(const_cast<T*>(m_data))[i] = data_to_copy[i];
	}

	template <class T> void
		DynamicArrayOf<T>::SetLength(size_t length_to_set)
	{
		if (length_to_set != m_length)
		{
			if (length_to_set > 0)
			{
				T* new_data = new T[length_to_set];
				Check_Pointer(new_data);
				if (m_data != NULL)
				{
					// Do not memcopy, object semantics may be required
					size_t i = Min(length_to_set, m_length);
					while (i--)
						new_data[i] = m_data[i];
					Check_Pointer(m_data);
					delete[] const_cast<T*>(m_data);
				}
				m_data = new_data;
			}
			else
			{
				Check_Pointer(m_data);
				delete[] const_cast<T*>(m_data);
				m_data = NULL;
			}
			m_length = length_to_set;
		}
	}

	template <class T> void
		DynamicArrayOf<T>::SetStorageLength(size_t length_to_set)
	{
		if (length_to_set != m_length)
		{
			if (length_to_set > 0)
			{
				if (m_data != NULL)
				{
					Check_Pointer(m_data);
					delete[] const_cast<T*>(m_data);
				}
				m_length = length_to_set;
				m_data = new T[m_length];
				Check_Pointer(m_data);
			}
			else
			{
				Verify(length_to_set == 0);
				Check_Pointer(m_data);
				delete[] const_cast<T*>(m_data);
				m_data = NULL;
			}
		}
	}

	template <class T> void
		DynamicArrayOf<T>::CopyArray(const DynamicArrayOf<T> &array)
	{ 
		if (array.m_length > 0)
		{
			Check_Pointer(array.m_data);
			AssignData(&array.m_data[0], array.m_length);
		}
		else
		{
			Verify(array.m_length == 0);
			Verify(array.m_data == NULL);
			if (m_data != NULL)
			{
				Check_Pointer(m_data);
				delete[] const_cast<T*>(m_data);
				m_length = 0;
			}
		}
	}

	template <class T> MemoryStream&
		MemoryStreamIO_Write(
			MemoryStream *stream,
			const DynamicArrayOf<T> *array
		)
	{
		Check_Object(stream);
		Check_Pointer(array);
		size_t length = array->m_length;
		MemoryStreamIO::Write(stream, &length);
		if (length > 0)
			stream->WriteBytes(array->m_data, length * sizeof(T));
		return *stream;
	}

	template <class T> MemoryStream&
		MemoryStreamIO_Read(
			MemoryStream *stream,
			DynamicArrayOf<T> *array
		)
	{
		Check_Object(stream);
		Check_Pointer(array);
		size_t length;
		MemoryStreamIO::Read(stream, &length);
		array->SetLength(length);
		Verify(length == array->m_length);
		if (length > 0)
			stream->ReadBytes(const_cast<T*>(array->m_data), length * sizeof(T));
		return *stream;
	}

	//##########################################################################
	//##########################    StaticArrayOf    ###########################
	//##########################################################################

	template <class T, size_t N> class StaticArrayOf
		#if defined(_ARMOR)
			: public Stuff::Signature
		#endif
	{
	public:
		StaticArrayOf();
		StaticArrayOf(const StaticArrayOf<T, N>&);
		StaticArrayOf(const T &value);
		StaticArrayOf(T *data, size_t length);
		~StaticArrayOf();

		void
			TestInstance() const
				{}

		StaticArrayOf<T, N>&
			operator=(const StaticArrayOf<T, N>&);
		T&
			operator[](size_t i);
		const T&
			operator[](size_t i) const;

		void
			AssignValue(const T &value);
		void
			AssignData(const T *data, size_t length);

		size_t 
			GetLength() const
				{return N;}
		size_t 
			GetSize() const
				{return N * sizeof(T);}
		T*
			GetData()
				{return data;}
		const T*
			GetData() const
				{return data;}

#if 0
		bool
			Compare(const StaticArrayOf<T, N>&);
#endif

		friend MemoryStream&
			MemoryStreamIO_Write(
				MemoryStream *stream,
				const StaticArrayOf<T, N> *array
			);

		friend MemoryStream&
			MemoryStreamIO_Read(
				MemoryStream *stream,
				StaticArrayOf<T, N> *array
			);

	private:
		T 
			data[N];
	};

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	template<class T, size_t N> inline
		StaticArrayOf<T, N>::StaticArrayOf() 
	{ 
	}

	template<class T, size_t N> inline
		StaticArrayOf<T, N>::StaticArrayOf(const StaticArrayOf<T, N> &array) 
	{
		AssignData(array.data, N);
	}

	template<class T, size_t N> inline
		StaticArrayOf<T, N>::StaticArrayOf(const T &value) 
	{
		AssignValue(value);
	}

	template<class T, size_t N> inline
		StaticArrayOf<T, N>::StaticArrayOf(T *data_to_copy, size_t length) 
	{
		AssignData(data_to_copy, length);
	}

	template<class T, size_t N> inline
		StaticArrayOf<T, N>::~StaticArrayOf() 
	{ 
	}

	template <class T, size_t N> StaticArrayOf<T, N>& 
		StaticArrayOf<T, N>::operator=(const StaticArrayOf<T, N> &array)
	{ 
		if (this != &array)
		{
			AssignData(&array.data[0], N);
		} 
		return *this; 
	}

	template <class T, size_t N> inline T&
		StaticArrayOf<T, N>::operator[](size_t i) 
	{
		Verify(i < N);  
		return data[i]; 
	}

	template <class T, size_t N> inline const T&
		StaticArrayOf<T, N>::operator[](size_t i) const
	{
		Verify(i < N);  
		return data[i]; 
	}

	template <class T, size_t N> void
		StaticArrayOf<T, N>::AssignValue(const T &value) 
	{
		// Do not memcopy, object semantics may be required
		for (int i = 0; i < N; i++)
			data[i] = value;
	}

	template <class T, size_t N> void
		StaticArrayOf<T, N>::AssignData(const T *data_to_copy, size_t length) 
	{
		Check_Pointer(data_to_copy);
		Verify(length <= N);
		// Do not memcopy, object semantics may be required
		for (int i = 0; i < length; i++)
			data[i] = data_to_copy[i];
	}

#if 0
	template <class T, size_t N> bool
		StaticArrayOf<T, N>::Compare(const StaticArrayOf<T, N> &array) 
	{
		if (N != array.GetLength())
			return false;
		for (int i = 0; i < N; i++)
		{
			if (data[i] != array.data[i])
				return false;
		}
		return true;
	}
#endif

	template <class T, size_t N> MemoryStream&
		MemoryStreamIO_Write(
			MemoryStream *stream,
			const StaticArrayOf<T, N> *array
		)
	{
		Check_Object(stream);
		Check_Pointer(array);
		return stream->WriteBytes(&array->data[0], N * sizeof(T));
	}

	template <class T, size_t N> MemoryStream&
		MemoryStreamIO_Read(
			MemoryStream *stream,
			StaticArrayOf<T, N> *array
		)
	{
		Check_Object(stream);
		Check_Pointer(array);
		return stream->ReadBytes(&array->data[0], N * sizeof(T));
	}

}
