/*********************************************************************

Name:		Raytracer
Author:		Kamil Szatkowski "Netrix" (C)
Website:	http://www.netrix.org.pl/
License:	GNU GPL

*********************************************************************/
#pragma once

#include "nDynamicTable.hpp"

namespace NIne
{
	template<typename Type>
	class NDataVector
	{
	public:
		NDataVector()
			: m_uReallocSize(0),
			  m_uSize(0)
		{
		}

		NRESULT Create(NSize_t uSize, NSize_t uReallocSize = 10)
		{
			NRESULT result;
			NRESULT_CP(result, m_buffer.Create(uSize));

			m_uReallocSize = uReallocSize;
			return NRV_SUCCESS;
		}

		NRESULT Create(const NDataVector<Type>& src)
		{
			NRESULT result;
			NRESULT_CP(result, m_buffer.Create(src.m_buffer));

			m_uReallocSize = src.m_uReallocSize;
			m_uSize = src.m_uSize;
			
			return NRV_SUCCESS;
		}

		void SetReallocSize(NSize_t uReallocSize)
		{
			NAssert(uReallocSize > 0, "uReallocSize must be > 0");

			m_uReallocSize = uReallocSize;
		}

		void Clear()
		{
			m_uSize = 0;
		}

		void Release()
		{
			m_buffer.Release();
			Clear();
		}

		NRESULT PushElement(const Type& element)
		{
			NRESULT result;

			if(m_uSize + 1 > m_buffer.GetSize())
			{
				NRESULT_CP(result, m_buffer.Resize(m_buffer.GetSize() + m_uReallocSize));
			}

			m_buffer[m_uSize] = element;
			++m_uSize;

			return NRV_SUCCESS;
		}

		void PopElement()
		{
			m_uSize = m_uSize > 0 ? m_uSize - 1 : 0;
		}

		NRESULT InsertElement(NSize_t uIndex, const Type& element)
		{
			NAssert(uIndex < m_uSize, "uIndex must be < m_uSize");

			NRESULT result;

			if(m_uSize + 1 > m_buffer.GetSize())
			{
				NRESULT_CP(result, m_buffer.Resize(m_buffer.GetSize() + m_uReallocSize));
			}

			memcpy(m_buffer.GetBuffer() + uIndex + 1, m_buffer.GetBuffer() + uIndex, sizeof(Type) * (m_uSize - uIndex - 1));
			m_buffer[uIndex] = element;

			++m_uSize;
			return NRV_SUCCESS;
		}

		void RemoveElement(NSize_t uIndex)
		{
			NAssert(uIndex < m_uSize, "uIndex must be < m_uSize");

			if(uIndex < m_uSize - 1)
			{
				memcpy(m_buffer.GetBuffer() + uIndex, m_buffer.GetBuffer() + uIndex + 1, sizeof(Type) * (m_uSize - uIndex - 1));
			}

			--m_uSize;
		}

		void RemoveElementUnordered(NSize_t uIndex)
		{
			NAssert(uIndex < m_uSize, "uIndex must be < m_uSize");

			if(uIndex < m_uSize - 1)
			{
				m_buffer[uIndex] = m_buffer[m_uSize - 1];
			}

			--m_uSize;
		}

		NSize_t FindElement(const Type& element)
		{
			NSize_t uIndex = 0;

			while(uIndex < m_uSize && m_buffer[uIndex] != element)
			{
				++uIndex;
			}

			return uIndex;
		}

		const Type* GetBuffer() const
		{
			return m_buffer.GetBuffer();
		}

		Type* GetBuffer()
		{
			return m_buffer.GetBuffer();
		}

		const Type& operator[](NSize_t uIndex) const
		{
			NAssert(uIndex < m_uSize, "Index out of bounds");
			return m_buffer[uIndex];
		}

		Type& operator[](NSize_t uIndex)
		{
			NAssert(uIndex < m_uSize, "Index out of bounds");
			return m_buffer[uIndex];
		}

		NSize_t GetCapacity() const
		{
			return m_buffer.GetSize();
		}

		bool IsEmpty() const
		{
			return m_uSize == 0;
		}

		NSize_t GetSize() const
		{
			return m_uSize;
		}

		NRESULT Reserve(NSize_t uSize)
		{
			NRESULT result;

			if(uSize > m_buffer.GetSize())
			{
				NRESULT_CP(result, m_buffer.Resize(uSize));
			}

			return NRV_SUCCESS;
		}

		NRESULT Resize(NSize_t uSize)
		{
			NRESULT result;

			if(uSize > m_buffer.GetSize())
			{
				NRESULT_CP(result, m_buffer.Resize(uSize));
			}

			m_uSize = uSize;

			return NRV_SUCCESS;
		}

		operator bool() const
		{
			return m_buffer;
		}

	private:
		NDynamicTable<Type> m_buffer;
		NSize_t m_uSize;
		NSize_t m_uReallocSize;
	};
}