#pragma once

#pragma once

#include "Array.h"

NS_BF_BEGIN;

template <typename T, typename TAlloc = AllocatorCLib >
class SizedArrayBase : protected TAlloc
{
public:
	typedef T value_type;

	T* mVals;
	intptr mSize;
	intptr mAllocSize;

	struct iterator
	{
	public:
		typedef std::random_access_iterator_tag iterator_category;
		typedef T value_type;
		typedef intptr difference_type;

		typedef T* pointer;
		typedef T& reference;

	public:
		T* mPtr;

	public:
		iterator()
		{
			mPtr = NULL;
		}

		iterator(T* ptr)
		{
			mPtr = ptr;
		}

		iterator& operator++()
		{
			mPtr++;
			return *this;
		}

		iterator operator++(int)
		{
			auto prevVal = *this;
			mPtr++;
			return prevVal;
		}

		iterator& operator--()
		{
			mPtr--;
			return *this;
		}

		iterator operator--(int)
		{
			auto prevVal = *this;
			mPtr--;
			return prevVal;
		}

		iterator& operator+=(intptr offset)
		{
			mPtr += offset;
			return *this;
		}

		bool operator!=(const iterator& itr) const
		{
			return itr.mPtr != mPtr;
		}

		bool operator==(const iterator& itr) const
		{
			return itr.mPtr == mPtr;
		}

		intptr operator-(const iterator& itr) const
		{
			return mPtr - itr.mPtr;
		}

		iterator operator+(intptr offset) const
		{
			iterator itr(mPtr + offset);
			return itr;
		}

		iterator operator-(intptr offset) const
		{
			iterator itr(mPtr - offset);
			return itr;
		}

		T& operator*() const
		{
			return *mPtr;
		}

		T* operator->() const
		{
			return mPtr;
		}

		bool operator<(const iterator& val2)
		{
			return mPtr < val2.mPtr;
		}

		bool operator>(const iterator& val2)
		{
			return mPtr > val2.mPtr;
		}

		bool operator>=(const iterator& val2)
		{
			return mPtr >= val2.mPtr;
		}
	};

	struct const_iterator
	{
	public:
		typedef std::random_access_iterator_tag iterator_category;
		typedef T value_type;
		typedef intptr difference_type;

		typedef const T* pointer;
		typedef const T& reference;

	public:
		const T* mPtr;

	public:
		const_iterator(const T* ptr)
		{
			mPtr = ptr;
		}

		const_iterator& operator++()
		{
			mPtr++;
			return *this;
		}

		const_iterator operator++(int)
		{
			auto prevVal = *this;
			mPtr++;
			return prevVal;
		}

		bool operator!=(const const_iterator& itr) const
		{
			return itr.mPtr != mPtr;
		}

		bool operator==(const const_iterator& itr) const
		{
			return itr.mPtr == mPtr;
		}

		intptr operator-(const iterator& itr) const
		{
			return mPtr - itr.mPtr;
		}

		const_iterator operator+(intptr offset) const
		{
			const_iterator itr(mPtr + offset);
			return itr;
		}

		const T& operator*()
		{
			return *mPtr;
		}

		const T* operator->()
		{
			return mPtr;
		}

		bool operator<(const const_iterator& val2)
		{
			return mPtr < val2.mPtr;
		}
	};

private:


public:
	SizedArrayBase()
	{
		this->mVals = NULL;
		this->mSize = 0;
		this->mAllocSize = 0;
	}

	SizedArrayBase(SizedArrayBase<T, TAlloc>&& val)
	{
		this->mVals = val.mVals;
		this->mSize = val.mSize;
		this->mAllocSize = val.mAllocSize;

		val.mVals = NULL;
		val.mSize = 0;
		val.mAllocSize = 0;
	}

	T& operator[](intptr idx)
	{
		BF_ASSERT((uintptr)idx < (uintptr)this->mSize);
		return this->mVals[idx];
	}

	const T& operator[](intptr idx) const
	{
		BF_ASSERT((uintptr)idx < (uintptr)this->mSize);
		return this->mVals[idx];
	}
	
	bool operator==(const SizedArrayBase& arrB) const
	{
		if (this->mSize != arrB.mSize)
			return false;
		for (int i = 0; i < this->mSize; i++)
			if (this->mVals[i] != arrB.mVals[i])
				return false;
		return true;
	}

	bool operator!=(const SizedArrayBase& arrB) const
	{
		if (this->mSize != arrB.mSize)
			return true;
		for (int i = 0; i < this->mSize; i++)
			if (this->mVals[i] != arrB.mVals[i])
				return true;
		return false;
	}

	const_iterator begin() const
	{
		return this->mVals;
	}

	const_iterator end() const
	{
		return this->mVals + this->mSize;
	}

	iterator begin()
	{
		return this->mVals;
	}

	iterator end()
	{
		return this->mVals + this->mSize;
	}

	T& front() const
	{
		return this->mVals[0];
	}

	T& back() const
	{
		return this->mVals[this->mSize - 1];
	}

	intptr size() const
	{
		return this->mSize;
	}

	bool empty() const
	{
		return this->mSize == 0;
	}

	bool IsEmpty() const
	{
		return this->mSize == 0;
	}

	void clear()
	{
		this->mSize = 0;
	}

	void Clear()
	{
		this->mSize = 0;
	}

	/*void Free()
	{
	if (this->mVals != NULL)
	{
	deallocate(this->mVals);
	}
	this->mVals = NULL;
	this->mAllocSize = 0;
	this->mSize = 0;
	}*/

	T GetSafe(intptr idx)
	{
		if ((idx < 0) || (idx >= this->mSize))
			return T();
		return this->mVals[idx];
	}

	T GetLastSafe()
	{
		if (this->mSize == 0)
			return T();
		return this->mVals[this->mSize - 1];
	}

	T GetFirstSafe()
	{
		if (this->mSize == 0)
			return T();
		return this->mVals[0];
	}

	bool Contains(T val) const
	{
		for (int i = 0; i < this->mSize; i++)
			if (this->mVals[i] == val)
				return true;
		return false;
	}

	intptr IndexOf(T val) const
	{
		for (int i = 0; i < this->mSize; i++)
			if (this->mVals[i] == val)
				return i;
		return -1;
	}
};

// NON-POD
template <typename T, typename TAlloc, bool TIsPod>
class SizedArrayBaseT : public SizedArrayBase<T, TAlloc>
{
public:
	typedef typename std::aligned_storage<sizeof(T), alignof(T)>::type TStorage;
	TStorage mFirstVal;

protected:
	void MoveArray(T* to, T* from, intptr count)
	{
		if (to < from)
		{
			// Prefer in-order moves 
			for (intptr i = 0; i < count; i++)
				new (&to[i]) T(std::move(from[i]));
		}
		else
		{
			for (intptr i = count - 1; i >= 0; i--)
				new (&to[i]) T(std::move(from[i]));
		}
	}

	void Grow(intptr newSize)
	{
		T* newVals = TAlloc::template allocate<T>(newSize);
		if (this->mVals != NULL)
		{
			if (this->mSize > 0)
				MoveArray(newVals, this->mVals, this->mSize);
			if (this->mVals != (T*)&mFirstVal)
				 TAlloc::deallocate(this->mVals);
		}
		this->mVals = newVals;
		this->mAllocSize = newSize;
	}

	void EnsureFree(intptr freeCount)
	{
		if (this->mSize + freeCount > this->mAllocSize)
			Grow(std::max(this->mAllocSize + this->mAllocSize / 2 + 1, this->mSize + freeCount));
	}

public:
	using SizedArrayBase<T, TAlloc>::SizedArrayBase;

	SizedArrayBaseT() : SizedArrayBase<T, TAlloc>()
	{

	}

	SizedArrayBaseT(const SizedArrayBaseT& val)
	{
		this->mVals = NULL;
		this->mSize = 0;
		this->mAllocSize = 0;

		*this = val;
	}

	SizedArrayBaseT(SizedArrayBaseT&& val) : SizedArrayBase<T, TAlloc>(val)
	{

	}

	~SizedArrayBaseT()
	{
		for (int i = 0; i < this->mSize; i++)
			this->mVals[i].~T();
		if (this->mVals != (T*)&mFirstVal)
		{
			 TAlloc::deallocate(this->mVals);
		}		
	}

	void resize(intptr size)
	{
		while (size < this->mSize)
			pop_back();
		if (size > this->mSize)
		{
			reserve(size);
			while (size > this->mSize)
				new (&this->mVals[this->mSize++]) T();
		}
	}

	void reserve(intptr size)
	{
		if (size > this->mAllocSize)
			Grow(size);
	}

	void SetSize(intptr size)
	{
		if (size > this->mAllocSize)
			Grow(size);

		this->mSize = size;
	}

	SizedArrayBaseT& operator=(const SizedArrayBaseT& val)
	{
		if (&val == this)
			return *this;
		for (int i = 0; i < this->mSize; i++)
			this->mVals[i].~T();
		this->mSize = 0;
		if (val.mSize > this->mAllocSize)
			Grow(val.mSize);
		resize(val.mSize);
		for (int i = 0; i < val.mSize; i++)
			new (&this->mVals[i]) T(val.mVals[i]);
		this->mSize = val.mSize;
		return *this;
	}

	void RemoveAt(intptr idx)
	{
		BF_ASSERT((uintptr)idx < (uintptr)this->mSize);

		this->mVals[idx].~T();

		// If we're removing the last element then we don't have to move anything
		if (idx != this->mSize - 1)
		{
			intptr moveCount = this->mSize - idx - 1;
			MoveArray(this->mVals + idx, this->mVals + idx + 1, moveCount);
		}
		this->mSize--;
	}

	void RemoveAll(const std::function<bool(const T& val)>& func)
	{
		int outIdx = 0;
		for (int i = 0; i < this->mSize; i++)
		{
			if (func(this->mVals[i]))
			{
				this->mVals[i].~T();
			}
			else 
			{
				if (i != outIdx)
				{
					new (&this->mVals[outIdx]) T(this->mVals[i]);
				}

				outIdx++;
			}
		}
		this->mSize = outIdx;
	}

	void RemoveRange(intptr idx, intptr length)
	{
		BF_ASSERT(
			((uintptr)idx < (uintptr)this->mSize) &&
			((uintptr)length > 0) &&
			((uintptr)(idx + length) <= (uintptr)this->mSize));

		for (intptr i = idx; i < idx + length; i++)
			this->mVals[i].~T();

		// If we're removing the last element then we don't have to move anything
		if (idx != this->mSize - length)
		{
			intptr moveCount = this->mSize - idx - length;
			MoveArray(this->mVals + idx, this->mVals + idx + length, moveCount);
		}
		this->mSize -= length;
	}

	void Insert(intptr idx, T val)
	{
		BF_ASSERT((uintptr)idx <= (uintptr)this->mSize);
		if (this->mSize >= this->mAllocSize)
		{
			intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1;

			T* newVals =  TAlloc::template allocate<T>(newSize);
			if (this->mVals != NULL)
			{
				if (idx > 0) // Copy left of idx
					MoveArray(newVals, this->mVals, idx);
				if (idx < this->mSize) // Copy right of idx
					MoveArray(newVals + idx + 1, this->mVals + idx, this->mSize - idx);
				if (this->mVals != (T*)&mFirstVal)
					 TAlloc::deallocate(this->mVals);
			}
			this->mVals = newVals;
			this->mAllocSize = newSize;
		}
		else if (idx != this->mSize)
		{
			intptr moveCount = this->mSize - idx;
			MoveArray(this->mVals + idx + 1, this->mVals + idx, moveCount);
		}
		new (&this->mVals[idx]) T(val);
		this->mSize++;
	}

	void Insert(intptr idx, T* vals, intptr size)
	{
		BF_ASSERT((uintptr)idx <= (uintptr)this->mSize);
		if (this->mSize + size > this->mAllocSize)
		{
			intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);

			T* newVals =  TAlloc::template allocate<T>(newSize);
			if (this->mVals != NULL)
			{
				if (idx > 0) // Copy left of idx
					MoveArray(newVals, this->mVals, idx);
				if (idx < this->mSize) // Copy right of idx
					MoveArray(newVals + idx + size, this->mVals + idx, this->mSize - idx);
				if (this->mVals != (T*)&mFirstVal)
					 TAlloc::deallocate(this->mVals);
			}
			this->mVals = newVals;
			this->mAllocSize = newSize;
		}
		else if (idx != this->mSize)
		{
			intptr moveCount = this->mSize - idx;
			MoveArray(this->mVals + idx + size, this->mVals + idx, moveCount);
		}
		for (int i = 0; i < size; i++)
			new (&this->mVals[idx + i]) T(vals[i]);
		this->mSize += size;
	}

	bool Remove(T val)
	{
		for (intptr i = 0; i < this->mSize; i++)
		{
			if (this->mVals[i] == val)
			{
				RemoveAt(i);
				return true;
			}
		}

		return false;
	}

	typename SizedArrayBase<T, TAlloc>::iterator erase(typename SizedArrayBase<T, TAlloc>::iterator itr)
	{
		RemoveAt(itr.mPtr - this->mVals);
		return itr;
	}

	void push_back(T val)
	{
		if (this->mSize >= this->mAllocSize)
			Grow(this->mAllocSize + this->mAllocSize / 2 + 1);
		new (&this->mVals[this->mSize++]) T(val);
	}

	void pop_back()
	{
		BF_ASSERT(this->mSize > 0);
		this->mVals[this->mSize - 1].~T();
		--this->mSize;
	}

	void Add(T val)
	{
		if (this->mSize >= this->mAllocSize)
			Grow(this->mAllocSize + this->mAllocSize / 2 + 1);
		new (&this->mVals[this->mSize++]) T(val);
	}
};

// POD
template <typename T, typename TAlloc>
class SizedArrayBaseT<T, TAlloc, true> : public SizedArrayBase<T, TAlloc>
{
public:
	typedef T TStorage;
	T mFirstVal;

protected:
	void Grow(intptr newSize)
	{
		T* newVals =  TAlloc::template allocate<T>(newSize);
		if (this->mVals != NULL)
		{
			if (this->mSize > 0)
				memcpy(newVals, this->mVals, this->mSize * sizeof(T));
			if (this->mVals != &mFirstVal)
				 TAlloc::deallocate(this->mVals);
		}
		this->mVals = newVals;
		this->mAllocSize = newSize;
	}

	void EnsureFree(intptr freeCount)
	{
		if (this->mSize + freeCount > this->mAllocSize)
			Grow(std::max(this->mAllocSize + this->mAllocSize / 2 + 1, this->mSize + freeCount));
	}
	
public:
	using SizedArrayBase<T, TAlloc>::SizedArrayBase;

	SizedArrayBaseT() : SizedArrayBase<T, TAlloc>()
	{

	}

	SizedArrayBaseT(const SizedArrayBaseT& val)
	{
		this->mVals = NULL;
		this->mSize = 0;
		this->mAllocSize = 0;

		*this = val;
	}

	SizedArrayBaseT(SizedArrayBaseT&& val) : SizedArrayBase<T, TAlloc>(val)
	{

	}

	~SizedArrayBaseT()
	{
		if (this->mVals != &mFirstVal)
		{
			 TAlloc::deallocate(this->mVals);
		}
	}

	SizedArrayBaseT& operator=(const SizedArrayBaseT& val)
	{
		if (&val == this)
			return *this;
		this->mSize = 0;
		if (val.mSize > this->mAllocSize)
			Grow(val.mSize);
		memcpy(this->mVals, val.mVals, val.mSize * sizeof(T));
		this->mSize = val.mSize;
		return *this;
	}

	void resize(intptr size)
	{
		while (size < this->mSize)
			pop_back();
		if (size > this->mSize)
		{
			reserve(size);
			while (size > this->mSize)
				new (&this->mVals[this->mSize++]) T();
		}
	}

	void reserve(intptr size)
	{
		if (size > this->mAllocSize)
			Grow(size);
	}

	void SetSize(intptr size)
	{
		if (size > this->mAllocSize)
			Grow(size);

		this->mSize = size;
	}


	void RemoveAt(intptr idx)
	{
		BF_ASSERT((uintptr)idx < (uintptr)this->mSize);

		// If we're removing the last element then we don't have to move anything
		if (idx != this->mSize - 1)
		{
			intptr moveCount = this->mSize - idx - 1;
			memmove(this->mVals + idx, this->mVals + idx + 1, moveCount * sizeof(T));
		}
		this->mSize--;
	}

	void RemoveRange(intptr idx, intptr length)
	{
		BF_ASSERT(
			((uintptr)idx < (uintptr)this->mSize) &&
			((uintptr)length > 0) &&
			((uintptr)(idx + length) <= (uintptr)this->mSize));

		// If we're removing the last element then we don't have to move anything
		if (idx != this->mSize - length)
		{
			intptr moveCount = this->mSize - idx - length;
			memmove(this->mVals + idx, this->mVals + idx + length, moveCount * sizeof(T));
		}
		this->mSize -= length;
	}

	void Insert(intptr idx, T val)
	{
		BF_ASSERT((uintptr)idx <= (uintptr)this->mSize);
		if (this->mSize >= this->mAllocSize)
		{
			intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1;

			T* newVals =  TAlloc::template allocate<T>(newSize);
			if (this->mVals != NULL)
			{
				if (idx > 0) // Copy left of idx
					memmove(newVals, this->mVals, idx * sizeof(T));
				if (idx < this->mSize) // Copy right of idx
					memmove(newVals + idx + 1, this->mVals + idx, (this->mSize - idx) * sizeof(T));
				if (this->mVals != &mFirstVal)
					 TAlloc::deallocate(this->mVals);
			}
			this->mVals = newVals;
			this->mAllocSize = newSize;
		}
		else if (idx != this->mSize)
		{
			intptr moveCount = this->mSize - idx;
			memmove(this->mVals + idx + 1, this->mVals + idx, moveCount * sizeof(T));
		}
		this->mVals[idx] = val;
		this->mSize++;
	}

	void Insert(intptr idx, const T* vals, intptr size)
	{
		BF_ASSERT((uintptr)idx <= (uintptr)this->mSize);
		if (this->mSize + size > this->mAllocSize)
		{
			intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);

			T* newVals =  TAlloc::template allocate<T>(newSize);
			if (this->mVals != NULL)
			{
				if (idx > 0) // Copy left of idx
					memmove(newVals, this->mVals, idx * sizeof(T));
				if (idx < this->mSize) // Copy right of idx
					memmove(newVals + idx + size, this->mVals + idx, (this->mSize - idx) * sizeof(T));
				if (this->mVals != &mFirstVal)
					 TAlloc::deallocate(this->mVals);
			}
			this->mVals = newVals;
			this->mAllocSize = newSize;
		}
		else if (idx != this->mSize)
		{
			intptr moveCount = this->mSize - idx;
			memmove(this->mVals + idx + size, this->mVals + idx, moveCount * sizeof(T));
		}
		for (int i = 0; i < size; i++)
			this->mVals[idx + i] = vals[i];
		this->mSize += size;
	}

	bool Remove(T val)
	{
		for (intptr i = 0; i < this->mSize; i++)
		{
			if (this->mVals[i] == val)
			{
				RemoveAt(i);
				return true;
			}
		}

		return false;
	}

	typename SizedArrayBase<T, TAlloc>::iterator erase(typename SizedArrayBase<T, TAlloc>::iterator itr)
	{
		RemoveAt(itr.mPtr - this->mVals);
		return itr;
	}

	void push_back(T val)
	{
		if (this->mSize >= this->mAllocSize)
			Grow(this->mAllocSize + this->mAllocSize / 2 + 1);
		this->mVals[this->mSize++] = val;
	}

	void pop_back()
	{
		BF_ASSERT(this->mSize > 0);
		--this->mSize;
	}

	void Add(T val)
	{
		if (this->mSize >= this->mAllocSize)
			Grow(this->mAllocSize + this->mAllocSize / 2 + 1);
		this->mVals[this->mSize++] = val;
	}
};

template <typename T, typename TAlloc = AllocatorCLib >
class SizedArrayImpl : public SizedArrayBaseT<T, TAlloc, std::is_pod<T>::value>
{
public:
	typedef SizedArrayBaseT<T, TAlloc, std::is_pod<T>::value> _Base;
};

template <typename T, int TInternalSize, typename TAlloc = AllocatorCLib >
class SizedArray : public SizedArrayImpl<T, TAlloc>
{
public:
	typedef SizedArrayImpl<T, TAlloc> _Base;
	typedef typename _Base::_Base _BaseBase;

	typename _Base::TStorage mInternalBuffer[TInternalSize - 1];

public:
	using SizedArrayImpl<T, TAlloc>::SizedArrayImpl;
	using _Base::operator=;
	using _Base::operator==;
	using _Base::operator!=;

	SizedArray()
	{
		this->mVals = (T*)&this->mFirstVal;
		this->mSize = 0;
		this->mAllocSize = TInternalSize;
	}

	SizedArray(const Array<T>& arr)
	{
		this->mVals = (T*)&this->mFirstVal;
		this->mSize = 0;
		this->mAllocSize = TInternalSize;
		for (auto& val : arr)
			this->Add(val);
	}

	SizedArray(const SizedArray& val)
	{
		this->mVals = (T*)&this->mFirstVal;
		this->mSize = 0;
		this->mAllocSize = TInternalSize;

		_BaseBase::operator=(val);
	}

	SizedArray(const typename _Base::_Base& val)
	{
		this->mVals = (T*)&this->mFirstVal;
		this->mSize = 0;
		this->mAllocSize = TInternalSize;

		_BaseBase::operator=(val);
	}

	SizedArray(SizedArray&& val)
	{
		if (val.mVals == (T*)&val.mFirstVal)
		{
			this->mVals = (T*)&this->mFirstVal;
			this->mSize = 0;
			this->mAllocSize = TInternalSize;
			_BaseBase::operator=(val);
		}
		else
		{
			this->mVals = val.mVals;
			this->mSize = val.mSize;
			this->mAllocSize = val.mAllocSize;
			val.mVals = (T*)&val.mFirstVal;
			val.mSize = 0;
			val.mAllocSize = TInternalSize;
		}
	}

	SizedArray(std::initializer_list<T> il)
	{
		this->mVals = (T*)&this->mFirstVal;
		this->mSize = 0;
		this->mAllocSize = TInternalSize;
		for (auto& val : il)
			this->push_back(val);
	}

	_BaseBase& operator=(const SizedArray& val)
	{
		if (&val == this)
			return *this;
		return _BaseBase::operator=(val);
	}

	_BaseBase& operator=(std::initializer_list<T> il)
	{
		this->mSize = 0;
		for (auto& val : il)
			this->push_back(val);
		return *this;
	}
};

template <typename T, typename TAlloc>
class SizedArray<T, 1, TAlloc> : public SizedArrayImpl<T, TAlloc>
{
public:
	typedef SizedArrayImpl<T, TAlloc> _Base;
	typedef typename _Base::_Base _BaseBase;

	using _Base::SizedArrayImpl;
	using _Base::operator=;
	using _Base::operator==;
	using _Base::operator!=;

	SizedArray()
	{
		this->mVals = (T*)&this->mFirstVal;
		this->mSize = 0;
		this->mAllocSize = 1;
	}

	SizedArray(const SizedArray& val)
	{
		this->mVals = NULL;
		this->mSize = 0;
		this->mAllocSize = 0;

		_BaseBase::operator=(val);
	}

	SizedArray(const _BaseBase& val)
	{
		this->mVals = NULL;
		this->mSize = 0;
		this->mAllocSize = 0;

		_BaseBase::operator=(val);
	}

	SizedArray(SizedArray&& val)
	{
		if (val.mVals == (T*)&val.mFirstVal)
		{
			this->mVals = (T*)&this->mFirstVal;
			this->mSize = 0;
			this->mAllocSize = 1;
			_BaseBase::operator=(val);
		}
		else
		{
			this->mVals = val.mVals;
			this->mSize = val.mSize;
			this->mAllocSize = val.mAllocSize;

			val.mVals = NULL;
			val.mSize = 0;
			val.mAllocSize = 0;
		}
	}

	SizedArray(std::initializer_list<T> il)
	{
		this->mVals = (T*)&this->mFirstVal;
		this->mSize = 0;
		this->mAllocSize = 1;
		for (auto& val : il)
			this->push_back(val);
	}

	_BaseBase& operator=(const SizedArray& val)
	{
		if (&val == this)
			return *this;
		return _BaseBase::operator=(val);
	}
};

template <typename T, typename TAlloc>
class SizedArray<T, 0, TAlloc> : public SizedArrayImpl<T, TAlloc>
{
public:
	typedef SizedArrayImpl<T, TAlloc> _Base;
	typedef typename _Base::_Base _BaseBase;

	using _Base::SizedArrayImpl;
	using _Base::operator=;
	using _Base::operator==;
	using _Base::operator!=;

	SizedArray()
	{
		this->mVals = (T*)&this->mFirstVal;
		this->mSize = 0;
		this->mAllocSize = 1;
	}

	SizedArray(const SizedArray& val)
	{
		this->mVals = NULL;
		this->mSize = 0;
		this->mAllocSize = 0;

		_BaseBase::operator=(val);
	}

	SizedArray(const _BaseBase& val)
	{
		this->mVals = NULL;
		this->mSize = 0;
		this->mAllocSize = 0;

		_BaseBase::operator=(val);
	}

	SizedArray(SizedArray&& val)
	{
		if (val.mVals == val.mInternalBuffer)
		{
			this->mVals = (T*)&this->mFirstVal;
			this->mSize = 0;
			this->mAllocSize = 1;
			_BaseBase::operator=(val);
		}
		else
		{
			this->mVals = val.mVals;
			this->mSize = val.mSize;
			this->mAllocSize = val.mAllocSize;

			val.mVals = &val.mInternalBuffer;
			val.mSize = 0;
			val.mAllocSize = 1;
		}
	}

	SizedArray(std::initializer_list<T> il)
	{
		this->mVals = (T*)&this->mFirstVal;
		this->mSize = 0;
		this->mAllocSize = 1;
		for (auto& val : il)
			this->push_back(val);
	}

	_BaseBase& operator=(const SizedArray& val)
	{
		return _BaseBase::operator=(val);
	}
};

template <typename T>
static bool operator==(const ArrayBase<T>& arrA, const SizedArrayBase<T>& arrB)
{
	if (arrA.mSize != arrB.mSize)
		return false;
	for (intptr i = 0; i < arrA.mSize; i++)
		if (arrA.mVals[i] != arrB.mVals[i])
			return false;
	return true;
}

NS_BF_END;

namespace std
{
	template<typename T>
	struct hash<Beefy::SizedArrayImpl<T> >
	{
		size_t operator()(const Beefy::SizedArrayImpl<T>& val) const
		{
			return HashBytes((const uint8*)val.mVals, sizeof(T) * val.mSize);
		}
	};
}
