/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2016 Raytrix GmbH. All rights reserved.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma once

#include "Rx.Core/RxExceptions.h"
#include "Rx.Interop.Runtime30/IMemory.h"
#include <vector>
#include <cstring>

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// namespace: Rx
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Rx
{
	/**
	\addtogroup RxCore_Tools
	**/
	/// @{

	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 	Basic array operations.
	/// </summary>
	///
	/// <typeparam name="TValue"> VALUE. </typeparam>
	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	template<class TValue>
	class CRxArrayBase : public Interop::Runtime30::IMemory
	{
	public:

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Default constructor. Creates an empty array.
		/// </summary>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		CRxArrayBase()
		{
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Constructor. Creates a new array with the given number of uninitialized elements.
		/// </summary>
		///
		/// <param name="nElementCount"> The number of elements. </param>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		CRxArrayBase(size_t nElementCount)
		{
			New(nElementCount);
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Constructor. Creates a new array with the given number of initialized elements.
		/// </summary>
		///
		/// <param name="nElementCount"> The number of elements. </param>
		/// <param name="tInitValue">    The initial value of each element in this array. </param>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		CRxArrayBase(size_t nElementCount, const TValue& tInitValue)
		{
			New(nElementCount);

			// Initialize all values with the given initial value
			for (TValue& tValue : m_vtData)
			{
				tValue = tInitValue;
			}
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Copy constructor. Creates a copy of the given array.
		/// </summary>
		///
		/// <param name="xArray"> The array to copy. </param>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		CRxArrayBase(const CRxArrayBase& xArray)
		{
			// Copy the internal vector
			m_vtData = xArray.m_vtData;
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Destructor. Removes all elements from this array.
		/// </summary>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual ~CRxArrayBase()
		{
			m_vtData.clear();
		}

	public:

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Assignment operator.
		/// </summary>
		///
		/// <param name="xArray"> The array to copy. </param>
		///
		/// <returns> This array. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		CRxArrayBase& operator=(const CRxArrayBase& xArray)
		{
			// Copy the internal vector
			m_vtData = xArray.m_vtData;
			return *this;
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Array indexer operator.
		/// </summary>
		///
		/// <param name="nIdx"> The index. </param>
		///
		/// <returns> The indexed value. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		TValue& operator[](size_t nIdx)
		{
			if (nIdx >= Length())
			{
				RX_THROW(CRxException, "Array index out of bounds");
			}

			return m_vtData[nIdx];
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Array indexer operator.
		/// </summary>
		///
		/// <param name="nIdx"> The index. </param>
		///
		/// <returns> The indexed value. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		const TValue& operator[](size_t nIdx) const
		{
			if (nIdx >= Length())
			{
				RX_THROW(CRxException, "Array index out of bounds");
			}

			return m_vtData[nIdx];
		}

	public:

		/************************************************************************/
		/* Rx::Interop::Runtime30::IMemory                                      */
		/************************************************************************/

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Gets the size of a single value in this array.
		/// </summary>
		///
		/// <returns> The element size. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual size_t GetElementSize() const
		{
			return sizeof(TValue);
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Gets the number of elements in this array.
		/// </summary>
		///
		/// <returns> The length. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual size_t Length() const
		{
			return m_vtData.size();
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Query if this object is valid. Returns always true.
		/// </summary>
		///
		/// <returns> True if valid, false if not. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual bool IsValid() const
		{
			return true;
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Set the size of this array to the given number of elements.
		/// </summary>
		///
		/// <param name="nElementCount"> Number of elements. </param>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual void New(size_t nElementCount)
		{
			try
			{
				m_vtData.resize(nElementCount);
			}
			catch (std::bad_alloc& ex)
			{
				RX_THROW(CRxOutOfMemoryException, ex.what());
			}
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Set the size of this array to the given number of elements.
		/// </summary>
		///
		/// <param name="nElementCount"> Number of elements. </param>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual void Resize(size_t nElementCount)
		{
			try
			{
				m_vtData.resize(nElementCount);
			}
			catch (std::bad_alloc& ex)
			{
				RX_THROW(CRxOutOfMemoryException, ex.what());
			}
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Clears the array.
		/// </summary>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual void Delete()
		{
			m_vtData.clear();
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Sets the internal memory to zeros.
		/// </summary>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual void Reset()
		{
			memset(m_vtData.data(), 0, m_vtData.size() * sizeof(TValue));
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Copies the content of the given source memory into this array. Sets the size of this array to the size of the memory.
		/// </summary>
		///
		/// <param name="pxSrcMemory"> The source memory. </param>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual void CopyFrom(const IMemoryAccess* pxSrcMemory)
		{
			if (pxSrcMemory == nullptr)
			{
				RX_THROW(CRxException, "Invalid memory interface pointer");
			}

			if (pxSrcMemory->GetElementSize() != GetElementSize())
			{
				RX_THROW(CRxException, "Element sizes of memory blocks do not agree");
			}

			// Resize this array to fit all elements
			Resize(pxSrcMemory->Length());

			// Get the pointer to the source memory (we increment this below)
			const TValue* ptSrcMemory = (const TValue*) pxSrcMemory->GetPointer();

			// Iterate over each (new) element in this array and assign the value of the source memory
			for (TValue& tValue : m_vtData)
			{
				tValue = *ptSrcMemory++;
			}
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Gets the pointer to the internal data.
		/// </summary>
		///
		/// <returns> The pointer. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual const void* GetPointer() const
		{
			return m_vtData.data();
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Gets the pointer to the internal data.
		/// </summary>
		///
		/// <returns> The pointer. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual void* GetPointer()
		{
			return m_vtData.data();
		}

	protected:

		/// <summary> The internal vector that holds all data. </summary>
		std::vector<TValue> m_vtData;
	};

	/// @}
}
