#pragma once

#include "BFPlatform.h"

NS_BF_BEGIN;

class AllocatorCLib
{
public:
	template <typename T>
	T* allocate(intptr count)
	{
		return (T*)malloc(sizeof(T) * count);
	}

	void deallocate(void* ptr)
	{
		free(ptr);
	}

	void* rawAllocate(intptr size)
	{
		return malloc(size);
	}

	void rawDeallocate(void* ptr)
	{
		free(ptr);
	}

	bool deallocateAll()
	{
		return false;
	}
};

template <typename T, typename TAlloc = AllocatorCLib >
class ArrayBase : public TAlloc
{
public:
	typedef T value_type;
	typedef int int_cosize;

	T* mVals;
	int_cosize mSize;
	int_cosize 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) const
		{
			return mPtr < val2.mPtr;
		}

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

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

		bool operator>=(const iterator& val2) const
		{
			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*() const
		{
			return *mPtr;
		}

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

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

private:
	

public:
	ArrayBase()
	{
		mVals = NULL;
		mSize = 0;
		mAllocSize = 0;
	}
	
	ArrayBase(ArrayBase<T, TAlloc>&& val)
	{
		mVals = val.mVals;
		mSize = val.mSize;
		mAllocSize = val.mAllocSize;

		val.mVals = NULL;
		val.mSize = 0;
		val.mAllocSize = 0;
	}
		
	T& operator[](intptr idx)
	{
		BF_ASSERT((uintptr)idx < (uintptr)mSize);
		return mVals[idx];
	}

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

	bool operator==(const ArrayBase& arrB) const
	{
		if (mSize != arrB.mSize)
			return false;
		for (intptr i = 0; i < mSize; i++)
			if (mVals[i] != arrB.mVals[i])
				return false;
		return true;
	}

	bool operator!=(const ArrayBase& arrB) const
	{
		if (mSize != arrB.mSize)
			return true;
		for (intptr i = 0; i < mSize; i++)
			if (mVals[i] != arrB.mVals[i])
				return true;
		return true;
	}

	const_iterator begin() const
	{
		return mVals;
	}

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

	iterator begin()
	{
		return mVals;
	}

	iterator end()
	{
		return mVals + mSize;
	}

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

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

	intptr size() const
	{
		return mSize;
	}

	int Count() const
	{
		return (int)mSize;
	}

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

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

	intptr GetFreeCount()
	{
		return mAllocSize - mSize;
	}
	
	/*void Free()
	{
		if (mVals != NULL)
		{
			deallocate(mVals);
		}
		mVals = NULL;
		mAllocSize = 0;
		mSize = 0;
	}*/

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

	void GetSafe(intptr idx, T& val)
	{
		if ((idx < 0) || (idx >= mSize))
			return;
		val = mVals[idx];
	}

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

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

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

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

	intptr LastIndexOf(const T& val)
	{
		for (intptr i = mSize - 1; i >= 0; i--)
			if (mVals[i] == val)
				return i;
		return -1;
	}

	void MoveTo(ArrayBase<T>& dest)
	{
		dest.mVals = this->mVals;
		dest.mSize = this->mSize;
		dest.mAllocSize = this->mAllocSize;

		this->mVals = NULL;
		this->mSize = 0;
		this->mAllocSize = 0;
	}

	void Sort(std::function<bool(const T&, const T&)> pred)
	{
		std::sort(this->begin(), this->end(), pred);
	}
};

// NON-POD
template <typename T, typename TAlloc, bool TIsPod>
class ArrayImpl : public ArrayBase<T, TAlloc>
{
public:
	typedef int int_cosize;

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 SetBufferSize(intptr newSize)
	{
		T* newVals = TAlloc::template allocate<T>(newSize);
		if (this->mVals != NULL)
		{
			if (this->mSize > 0)
				MoveArray(newVals, this->mVals, this->mSize);
			TAlloc::deallocate(this->mVals);
		}
		this->mVals = newVals;
		this->mAllocSize = (int_cosize)newSize;
	}

	void EnsureFree(intptr freeCount)
	{
		if (this->mSize + freeCount > this->mAllocSize)
			SetBufferSize(BF_MAX(this->mAllocSize + this->mAllocSize / 2 + 1, this->mSize + freeCount));
	}

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

	ArrayImpl() : ArrayBase<T, TAlloc>()
	{

	}

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

		*this = val;
	}

	ArrayImpl(ArrayImpl&& val) : ArrayBase<T, TAlloc>(std::move(val))
	{

	}

	~ArrayImpl()
	{		
		for (intptr i = 0; i < this->mSize; i++)
			this->mVals[i].~T(); //-V595
		if (this->mVals != NULL)
		{
			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)
			SetBufferSize(size);
	}

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

		this->mSize = (int_cosize)size;
	}

	void Clear()
	{
		for (intptr i = 0; i < this->mSize; i++)
			this->mVals[i].~T();
		this->mSize = 0;
	}

	void Dispose()
	{
		Clear();
		delete this->mVals;
		this->mVals = NULL;
		this->mAllocSize = 0;
	}

	ArrayImpl& operator=(const ArrayImpl& val)
	{
		if (&val == this)
			return *this;
		for (intptr i = 0; i < this->mSize; i++)
			this->mVals[i].~T();
		this->mSize = 0;
		if (val.mSize > this->mAllocSize)
			SetBufferSize(val.mSize);
		Resize(val.mSize);			
		for (intptr i = 0; i < val.mSize; i++)
			new (&this->mVals[i]) T(val.mVals[i]);
		this->mSize = val.mSize;
		return *this;
	}

	ArrayImpl& operator=(ArrayImpl&& val)
	{
		if (this->mVals != NULL)
		{
			for (intptr i = 0; i < this->mSize; i++)
				this->mVals[i].~T();
			TAlloc::deallocate(this->mVals);
		}
		
		this->mVals = val.mVals;
		this->mSize = val.mSize;
		this->mAllocSize = val.mAllocSize;

		val.mVals = NULL;

		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--;
	}

	// 'Fast' because it's allowed to change item order
	void RemoveAtFast(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)
		{
			new (&this->mVals[idx]) T(std::move(this->mVals[this->mSize - 1]));
		}
		this->mSize--;
	}

	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 -= (int_cosize)length;
	}

	void Insert(intptr idx, const 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);
				TAlloc::deallocate(this->mVals);
			}
			this->mVals = newVals;
			this->mAllocSize = (int_cosize)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, 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
					MoveArray(newVals, this->mVals, idx);
				if (idx < this->mSize) // Copy right of idx
					MoveArray(newVals + idx + size, this->mVals + idx, this->mSize - idx);
				TAlloc::deallocate(this->mVals);
			}
			this->mVals = newVals;
			this->mAllocSize = (int_cosize)newSize;
		}
		else if (idx != this->mSize)
		{
			intptr moveCount = this->mSize - idx;
			MoveArray(this->mVals + idx + size, this->mVals + idx, moveCount);
		}
		for (intptr i = 0; i < size; i++)
			new (&this->mVals[idx + i]) T(vals[i]);
		this->mSize += (int_cosize)size;
	}

	void Insert(intptr idx, const T& val, intptr count)
	{
		BF_ASSERT((uintptr)idx <= (uintptr)this->mSize);
		if (this->mSize + count > this->mAllocSize)
		{
			intptr newSize = BF_MAX(this->mSize + count, 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(this->newVals, this->mVals, idx);
				if (idx < this->mSize) // Copy right of idx
					MoveArray(newVals + idx + count, this->mVals + idx, this->mSize - idx);
				TAlloc::deallocate(this->mVals);
			}
			this->mVals = newVals;
			this->mAllocSize = (int_cosize)newSize;
		}
		else if (idx != this->mSize)
		{
			intptr moveCount = this->mSize - idx;
			MoveArray(this->mVals + idx + count, this->mVals + idx, moveCount);
		}
		for (intptr i = 0; i < count; i++)
			new (&this->mVals[idx + i]) T(val);
		this->mSize += (int_cosize)count;
	}

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

		return false;
	}

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

	void push_back(const T& val)
	{
		if (this->mSize >= this->mAllocSize)
			SetBufferSize(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(const T& val)
	{
		if (this->mSize >= this->mAllocSize)
			SetBufferSize(this->mAllocSize + this->mAllocSize / 2 + 1);
		new (&this->mVals[this->mSize++]) T(val);
	}

	T& GetSafeRef(int idx)
	{
		BF_ASSERT(idx >= 0);
		while (idx >= this->mSize)
			this->Add(T());
		return this->mVals[idx];
	}
};

// POD
template <typename T, typename TAlloc>
class ArrayImpl<T, TAlloc, true> : public ArrayBase<T, TAlloc>
{
public:
	typedef int int_cosize;

protected:
	void SetBufferSize(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));
			TAlloc::deallocate(this->mVals);
		}
		this->mVals = newVals;
		this->mAllocSize = (int_cosize)newSize;
	}

	void EnsureFree(intptr freeCount)
	{
		if (this->mSize + freeCount > this->mAllocSize)
			SetBufferSize(BF_MAX(this->mAllocSize + this->mAllocSize / 2 + 1, this->mSize + freeCount));
	}

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

	ArrayImpl() : ArrayBase<T, TAlloc>::ArrayBase()
	{

	}

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

		*this = val;
	}

	ArrayImpl(ArrayImpl&& val) : ArrayBase<T, TAlloc>(std::move(val))
	{

	}

	~ArrayImpl()
	{				
		if (this->mVals != NULL)
		{
			TAlloc::deallocate(this->mVals);
		}
	}
	
	ArrayImpl& operator=(const ArrayImpl& val)
	{
		if (&val == this)
			return *this;
		this->mSize = 0;
		if (val.mSize > this->mAllocSize)
			SetBufferSize(val.mSize);
		memcpy(this->mVals, val.mVals, val.mSize * sizeof(T));
		this->mSize = val.mSize;
		return *this;
	}

	void Resize(intptr size)
	{
		if (size < this->mSize)
			this->mSize = (int_cosize)size;
		else if (size > this->mSize)
		{
			Reserve(size);
			memset(&this->mVals[this->mSize], 0, (size - this->mSize) * sizeof(T));
			this->mSize = (int_cosize)size;
		}		
	}

	void ResizeRaw(intptr size)
	{		
		if (size < this->mSize)
			this->mSize = (int_cosize)size;
		else if (size > this->mSize)
		{
			Reserve(size);
			this->mSize = (int_cosize)size;
		}		
	}

	void Reserve(intptr size)
	{
		if (size > this->mAllocSize)
			SetBufferSize(size);
	}

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

		this->mSize = (int_cosize)size;
	}

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

	void TrimExcess()
	{
		if (this->mSize > this->mAllocSize)
			SetBufferSize(this->mSize);
	}

	void Dispose()
	{
		Clear();
		delete this->mVals;
		this->mVals = NULL;
		this->mAllocSize = 0;
	}

	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--;
	}

	// 'Fast' because it's allowed to change item order
	void RemoveAtFast(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)
		{
			this->mVals[idx] = this->mVals[this->mSize - 1];
		}
		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 -= (int_cosize)length;
	}

	void Insert(intptr idx, const 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));
				TAlloc::deallocate(this->mVals);
			}
			this->mVals = newVals;
			this->mAllocSize = (int_cosize)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));
				TAlloc::deallocate(this->mVals);
			}
			this->mVals = newVals;
			this->mAllocSize = (int_cosize)newSize;
		}
		else if (idx != this->mSize)
		{
			intptr moveCount = this->mSize - idx;
			memmove(this->mVals + idx + size, this->mVals + idx, moveCount * sizeof(T));
		}
		memcpy(&this->mVals[idx], vals, size * sizeof(T));		
		this->mSize += (int_cosize)size;
	}

	void Insert(intptr idx, const T& val, 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));
				TAlloc::deallocate(this->mVals);
			}
			this->mVals = newVals;
			this->mAllocSize = (int_cosize)newSize;
		}
		else if (idx != this->mSize)
		{
			intptr moveCount = this->mSize - idx;
			memmove(this->mVals + idx + size, this->mVals + idx, moveCount * sizeof(T));
		}
		for (intptr i = 0; i < size; i++)
			this->mVals[idx + i] = val;
		this->mSize += (int_cosize)size;
	}

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

		return false;
	}

	bool RemoveAll(const T& val)
	{
		bool found = false;

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

		return found;
	}

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

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

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

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

	T& GetSafeRef(int idx)
	{
		BF_ASSERT(idx >= 0);
		while (idx >= this->mSize)
			this->Add(T());
		return this->mVals[idx];
	}

	T* GrowUninitialized(int addSize)
	{
		if (this->mSize + addSize > this->mAllocSize)
			EnsureFree(addSize);
		this->mSize += (int_cosize)addSize;
		if (addSize == 0)
			return NULL;
		return &this->mVals[this->mSize - addSize];
	}

	template <typename TAlt>
	int BinarySearchAlt(const TAlt& val, const std::function<int(const T& lhs, const TAlt& rhs)>& func)
	{
		int lo = 0;
		int hi = this->mSize - 1;

		while (lo <= hi)
		{
			int i = (lo + hi) / 2;
			const T& midVal = this->mVals[i];
			int c = func(midVal, val);
			if (c == 0) return i;
			if (c < 0)
				lo = i + 1;
			else
				hi = i - 1;
		}
		return ~lo;
	}
};

template <typename T, typename TAlloc = AllocatorCLib >
class Array : public ArrayImpl<T, TAlloc, std::is_pod<T>::value>
{
public:
	typedef ArrayImpl<T, TAlloc, std::is_pod<T>::value> _ArrayImpl;

	using ArrayImpl<T, TAlloc, std::is_pod<T>::value>::ArrayImpl;
	using _ArrayImpl::operator=;
	using _ArrayImpl::operator==;
	using _ArrayImpl::operator!=;
	
	Array() : _ArrayImpl()
	{

	}

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

		*this = val;
	}

	Array(Array&& val) : _ArrayImpl(std::move(val))
	{

	}

	_ArrayImpl& operator=(const Array& val)
	{
		return _ArrayImpl::operator=(val);
	}

	_ArrayImpl& operator=(Array&& val)
	{
		return _ArrayImpl::operator=(val);
	}
};

template <typename T>
class OwnedArray : public Array<T*>
{
public:
	typedef Array<T*> _Base;

	~OwnedArray()
	{
		for (auto item : *this)
			delete item;
	}

	void Clear()
	{
		for (auto item : *this)
			delete item;
		_Base::Clear();
	}

	void ClearWithoutDeleting()
	{
		_Base::Clear();
	}

	T* Alloc()
	{
		T* item = new T();
		_Base::push_back(item);
		return item;
	}

	template <typename T2>
	T2* Alloc()
	{
		T2* item = new T2();
		_Base::push_back(item);
		return item;
	}
};

NS_BF_END;

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