#ifndef _DEPOOLARRAY_HPP
#define _DEPOOLARRAY_HPP
/*-------------------------------------------------------------------------
 * drawElements C++ Base Library
 * -----------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Array template backed by memory pool.
 *//*--------------------------------------------------------------------*/

#include "deDefs.hpp"
#include "deMemPool.hpp"
#include "deInt32.h"

#include <iterator>

namespace de
{

//! Self-test for PoolArray
void PoolArray_selfTest (void);

template<typename T, deUint32 Alignment>
class PoolArrayConstIterator;

template<typename T, deUint32 Alignment>
class PoolArrayIterator;

/*--------------------------------------------------------------------*//*!
 * \brief Array template backed by memory pool
 *
 * \note Memory in PoolArray is not contiguous so pointer arithmetic
 *       to access next element(s) doesn't work.
 * \todo [2013-02-11 pyry] Make elements per page template argument.
 *//*--------------------------------------------------------------------*/
template<typename T, deUint32 Alignment = (sizeof(T) > 4 ? 4 : sizeof(T))>
class PoolArray
{
public:
	typedef PoolArrayIterator<T, Alignment>			Iterator;
	typedef PoolArrayConstIterator<T, Alignment>	ConstIterator;

	explicit		PoolArray			(MemPool* pool);
					PoolArray			(MemPool* pool, const PoolArray<T, Alignment>& other);
					~PoolArray			(void);

	void			clear				(void);

	void			reserve				(deUintptr capacity);
	void			resize				(deUintptr size);
	void			resize				(deUintptr size, const T& value);

	deUintptr		size				(void) const			{ return m_numElements;		}
	bool			empty				(void) const			{ return m_numElements == 0;}

	void			pushBack			(const T& value);
	T				popBack				(void);

	const T&		at					(deIntptr ndx) const	{ return *getPtr(ndx);		}
	T&				at					(deIntptr ndx)			{ return *getPtr(ndx);		}

	const T&		operator[]			(deIntptr ndx) const	{ return at(ndx);			}
	T&				operator[]			(deIntptr ndx)			{ return at(ndx);			}

	Iterator		begin				(void)					{ return Iterator(this, 0);								}
	Iterator		end					(void)					{ return Iterator(this, (deIntptr)m_numElements);		}

	ConstIterator	begin				(void) const			{ return ConstIterator(this, 0);						}
	ConstIterator	end					(void) const			{ return ConstIterator(this, (deIntptr)m_numElements);	}

private:
	enum
	{
		ELEMENTS_PER_PAGE_LOG2	= 4			//!< 16 elements per page.
	};

					PoolArray			(const PoolArray<T, Alignment>& other); // \note Default copy ctor is not allowed, use PoolArray(pool, copy) instead.

	T*				getPtr				(deIntptr ndx) const;

	MemPool*		m_pool;

	deUintptr		m_numElements;			//!< Number of elements in the array.
	deUintptr		m_capacity;				//!< Number of allocated elements in the array.

	deUintptr		m_pageTableCapacity;	//!< Size of the page table.
	void**			m_pageTable;			//!< Pointer to the page table.
};

template<typename T, deUint32 Alignment>
class PoolArrayIteratorBase
{
public:
						PoolArrayIteratorBase		(deUintptr ndx) : m_ndx(ndx) {}
						~PoolArrayIteratorBase		(void) {}

	deIntptr			getNdx						(void) const throw() { return m_ndx;	}

protected:
	deIntptr			m_ndx;
};

template<typename T, deUint32 Alignment>
class PoolArrayConstIterator : public PoolArrayIteratorBase<T, Alignment>
{
public:
											PoolArrayConstIterator		(void);
											PoolArrayConstIterator		(const PoolArray<T, Alignment>* array, deIntptr ndx);
											PoolArrayConstIterator		(const PoolArrayIterator<T, Alignment>& iterator);
											~PoolArrayConstIterator		(void);

	// \note Default assignment and copy-constructor are auto-generated.

	const PoolArray<T, Alignment>*			getArray	(void) const throw() { return m_array;	}

	// De-reference operators.
	const T*								operator->	(void) const throw()			{ return (*m_array)[this->m_ndx];		}
	const T&								operator*	(void) const throw()			{ return (*m_array)[this->m_ndx];		}
	const T&								operator[]	(deUintptr offs) const throw()	{ return (*m_array)[this->m_ndx+offs];	}

	// Pre-increment and decrement.
	PoolArrayConstIterator<T, Alignment>&	operator++	(void)	{ this->m_ndx += 1; return *this;	}
	PoolArrayConstIterator<T, Alignment>&	operator--	(void)	{ this->m_ndx -= 1; return *this;	}

	// Post-increment and decrement.
	PoolArrayConstIterator<T, Alignment>	operator++	(int)	{ PoolArrayConstIterator<T, Alignment> copy(*this); this->m_ndx +=1; return copy; }
	PoolArrayConstIterator<T, Alignment>	operator--	(int)	{ PoolArrayConstIterator<T, Alignment> copy(*this); this->m_ndx -=1; return copy; }

	// Compound assignment.
	PoolArrayConstIterator<T, Alignment>&	operator+=	(deIntptr offs)	{ this->m_ndx += offs; return *this; }
	PoolArrayConstIterator<T, Alignment>&	operator-=	(deIntptr offs)	{ this->m_ndx -= offs; return *this; }

	// Assignment from non-const.
	PoolArrayConstIterator<T, Alignment>&	operator=	(const PoolArrayIterator<T, Alignment>& iter);

private:
	const PoolArray<T, Alignment>*			m_array;
};

template<typename T, deUint32 Alignment>
class PoolArrayIterator : public PoolArrayIteratorBase<T, Alignment>
{
public:
										PoolArrayIterator	(void);
										PoolArrayIterator	(PoolArray<T, Alignment>* array, deIntptr ndx);
										~PoolArrayIterator	(void);

	// \note Default assignment and copy-constructor are auto-generated.

	PoolArray<T, Alignment>*			getArray	(void) const throw() { return m_array;	}

	// De-reference operators.
	T*									operator->	(void) const throw()			{ return (*m_array)[this->m_ndx];		}
	T&									operator*	(void) const throw()			{ return (*m_array)[this->m_ndx];		}
	T&									operator[]	(deUintptr offs) const throw()	{ return (*m_array)[this->m_ndx+offs];	}

	// Pre-increment and decrement.
	PoolArrayIterator<T, Alignment>&	operator++	(void)	{ this->m_ndx += 1; return *this;	}
	PoolArrayIterator<T, Alignment>&	operator--	(void)	{ this->m_ndx -= 1; return *this;	}

	// Post-increment and decrement.
	PoolArrayIterator<T, Alignment>		operator++	(int)	{ PoolArrayIterator<T, Alignment> copy(*this); this->m_ndx +=1; return copy; }
	PoolArrayIterator<T, Alignment>		operator--	(int)	{ PoolArrayIterator<T, Alignment> copy(*this); this->m_ndx -=1; return copy; }

	// Compound assignment.
	PoolArrayIterator<T, Alignment>&	operator+=	(deIntptr offs)	{ this->m_ndx += offs; return *this; }
	PoolArrayIterator<T, Alignment>&	operator-=	(deIntptr offs)	{ this->m_ndx -= offs; return *this; }

private:
	PoolArray<T, Alignment>*			m_array;
};

// Initializer helper for array.
template<typename T>
struct PoolArrayElement
{
	static void constructDefault	(void* ptr)					{ new (ptr) T();	}	//!< Called for non-initialized memory.
	static void	constructCopy		(void* ptr, const T& val)	{ new (ptr) T(val);	}	//!< Called for non-initialized memory when initial value is provided.
	static void destruct			(T* ptr)					{ ptr->~T();		}	//!< Called when element is destructed.
};

// Specialization for basic types.
#define DE_SPECIALIZE_POOL_ARRAY_ELEMENT_BASIC_TYPE(TYPE)							\
template<> struct PoolArrayElement<TYPE> {											\
	static void constructDefault	(void*)					{}						\
	static void constructCopy		(void* ptr, TYPE val)	{ *(TYPE*)ptr = val; }	\
	static void destruct			(TYPE*)					{}						\
}

DE_SPECIALIZE_POOL_ARRAY_ELEMENT_BASIC_TYPE(deUint8);
DE_SPECIALIZE_POOL_ARRAY_ELEMENT_BASIC_TYPE(deUint16);
DE_SPECIALIZE_POOL_ARRAY_ELEMENT_BASIC_TYPE(deUint32);
DE_SPECIALIZE_POOL_ARRAY_ELEMENT_BASIC_TYPE(deUint64);
DE_SPECIALIZE_POOL_ARRAY_ELEMENT_BASIC_TYPE(deInt8);
DE_SPECIALIZE_POOL_ARRAY_ELEMENT_BASIC_TYPE(deInt16);
DE_SPECIALIZE_POOL_ARRAY_ELEMENT_BASIC_TYPE(deInt32);
DE_SPECIALIZE_POOL_ARRAY_ELEMENT_BASIC_TYPE(deInt64);

// PoolArray<T> implementation.

template<typename T, deUint32 Alignment>
PoolArray<T, Alignment>::PoolArray (MemPool* pool)
	: m_pool				(pool)
	, m_numElements			(0)
	, m_capacity			(0)
	, m_pageTableCapacity	(0)
	, m_pageTable			(0)
{
	DE_ASSERT(deIsPowerOfTwo32(Alignment));
}

template<typename T, deUint32 Alignment>
PoolArray<T, Alignment>::~PoolArray (void)
{
	// Clear resets values to T()
	clear();
}

template<typename T, deUint32 Alignment>
inline void PoolArray<T, Alignment>::clear (void)
{
	resize(0);
}

template<typename T, deUint32 Alignment>
inline void PoolArray<T, Alignment>::resize (deUintptr newSize)
{
	if (newSize < m_numElements)
	{
		// Destruct elements that are no longer active.
		for (deUintptr ndx = newSize; ndx < m_numElements; ndx++)
			PoolArrayElement<T>::destruct(getPtr(ndx));

		m_numElements = newSize;
	}
	else if (newSize > m_numElements)
	{
		deUintptr prevSize = m_numElements;

		reserve(newSize);
		m_numElements = newSize;

		// Fill new elements with default values
		for (deUintptr ndx = prevSize; ndx < m_numElements; ndx++)
			PoolArrayElement<T>::constructDefault(getPtr(ndx));
	}
}

template<typename T, deUint32 Alignment>
inline void PoolArray<T, Alignment>::resize (deUintptr newSize, const T& value)
{
	if (newSize < m_numElements)
		resize(newSize); // value is not used
	else if (newSize > m_numElements)
	{
		deUintptr prevSize = m_numElements;

		reserve(newSize);
		m_numElements = newSize;

		// Fill new elements with copies of value
		for (deUintptr ndx = prevSize; ndx < m_numElements; ndx++)
			PoolArrayElement<T>::constructCopy(getPtr(ndx), value);
	}
}

template<typename T, deUint32 Alignment>
inline void PoolArray<T, Alignment>::reserve (deUintptr capacity)
{
	if (capacity >= m_capacity)
	{
		void*		oldPageTable			= DE_NULL;
		deUintptr	oldPageTableSize		= 0;

		deUintptr	newCapacity				= (deUintptr)deAlignPtr((void*)capacity, 1 << ELEMENTS_PER_PAGE_LOG2);
		deUintptr	reqPageTableCapacity	= newCapacity >> ELEMENTS_PER_PAGE_LOG2;

		if (m_pageTableCapacity < reqPageTableCapacity)
		{
			deUintptr		newPageTableCapacity	= max(2*m_pageTableCapacity, reqPageTableCapacity);
			void**			newPageTable			= (void**)m_pool->alloc(newPageTableCapacity * sizeof(void*));
			deUintptr		i;

			for (i = 0; i < m_pageTableCapacity; i++)
				newPageTable[i] = m_pageTable[i];

			for (; i < newPageTableCapacity; i++)
				newPageTable[i] = DE_NULL;

			// Grab information about old page table for recycling purposes.
			oldPageTable		= m_pageTable;
			oldPageTableSize	= m_pageTableCapacity * sizeof(T*);

			m_pageTable			= newPageTable;
			m_pageTableCapacity	= newPageTableCapacity;
		}

		// Allocate new pages.
		{
			deUintptr	elementSize		= (deUintptr)deAlignPtr((void*)(deUintptr)sizeof(T), Alignment);
			deUintptr	pageAllocSize	= elementSize << ELEMENTS_PER_PAGE_LOG2;
			deUintptr	pageTableNdx	= m_capacity >> ELEMENTS_PER_PAGE_LOG2;

			// Allocate new pages from recycled old page table.
			for (;;)
			{
				void*		newPage			= deAlignPtr(oldPageTable, Alignment);
				deUintptr	alignPadding	= (deUintptr)newPage - (deUintptr)oldPageTable;

				if (oldPageTableSize < pageAllocSize+alignPadding)
					break; // No free space for alloc + alignment.

				DE_ASSERT(m_pageTableCapacity > pageTableNdx);
				DE_ASSERT(!m_pageTable[pageTableNdx]);
				m_pageTable[pageTableNdx++] = newPage;

				oldPageTable		 = (void*)((deUint8*)newPage + pageAllocSize);
				oldPageTableSize	-= pageAllocSize+alignPadding;
			}

			// Allocate the rest of the needed pages from the pool.
			for (; pageTableNdx < reqPageTableCapacity; pageTableNdx++)
			{
				DE_ASSERT(!m_pageTable[pageTableNdx]);
				m_pageTable[pageTableNdx] = m_pool->alignedAlloc(pageAllocSize, Alignment);
			}

			m_capacity = pageTableNdx << ELEMENTS_PER_PAGE_LOG2;
			DE_ASSERT(m_capacity >= newCapacity);
		}
	}
}

template<typename T, deUint32 Alignment>
inline void PoolArray<T, Alignment>::pushBack (const T& value)
{
	resize(size()+1);
	at(size()-1) = value;
}

template<typename T, deUint32 Alignment>
inline T PoolArray<T, Alignment>::popBack (void)
{
	T val = at(size()-1);
	resize(size()-1);
	return val;
}

template<typename T, deUint32 Alignment>
inline T* PoolArray<T, Alignment>::getPtr (deIntptr ndx) const
{
	DE_ASSERT(inBounds<deIntptr>(ndx, 0, (deIntptr)m_numElements));
	deUintptr	pageNdx		= ((deUintptr)ndx >> ELEMENTS_PER_PAGE_LOG2);
	deUintptr	subNdx		= (deUintptr)ndx & ((1 << ELEMENTS_PER_PAGE_LOG2) - 1);
	deUintptr	elemSize	= (deUintptr)deAlignPtr((void*)(deUintptr)sizeof(T), Alignment);
	T*			ptr			= (T*)((deUint8*)m_pageTable[pageNdx] + (subNdx*elemSize));
	DE_ASSERT(deIsAlignedPtr(ptr, Alignment));
	return ptr;
}

// PoolArrayIteratorBase implementation

template<typename T, deUint32 Alignment>
inline bool operator== (const PoolArrayIteratorBase<T, Alignment>& a, const PoolArrayIteratorBase<T, Alignment>& b)
{
	// \todo [2013-02-08 pyry] Compare array ptr.
	return a.getNdx() == b.getNdx();
}

template<typename T, deUint32 Alignment>
inline bool operator!= (const PoolArrayIteratorBase<T, Alignment>& a, const PoolArrayIteratorBase<T, Alignment>& b)
{
	// \todo [2013-02-08 pyry] Compare array ptr.
	return a.getNdx() != b.getNdx();
}

template<typename T, deUint32 Alignment>
inline bool operator< (const PoolArrayIteratorBase<T, Alignment>& a, const PoolArrayIteratorBase<T, Alignment>& b)
{
	return a.getNdx() < b.getNdx();
}

template<typename T, deUint32 Alignment>
inline bool operator> (const PoolArrayIteratorBase<T, Alignment>& a, const PoolArrayIteratorBase<T, Alignment>& b)
{
	return a.getNdx() > b.getNdx();
}

template<typename T, deUint32 Alignment>
inline bool operator<= (const PoolArrayIteratorBase<T, Alignment>& a, const PoolArrayIteratorBase<T, Alignment>& b)
{
	return a.getNdx() <= b.getNdx();
}

template<typename T, deUint32 Alignment>
inline bool operator>= (const PoolArrayIteratorBase<T, Alignment>& a, const PoolArrayIteratorBase<T, Alignment>& b)
{
	return a.getNdx() >= b.getNdx();
}

// PoolArrayConstIterator<T> implementation

template<typename T, deUint32 Alignment>
inline PoolArrayConstIterator<T, Alignment>::PoolArrayConstIterator (void)
	: PoolArrayIteratorBase<T, Alignment>	(0)
	, m_array								(DE_NULL)
{
}

template<typename T, deUint32 Alignment>
inline PoolArrayConstIterator<T, Alignment>::PoolArrayConstIterator (const PoolArray<T, Alignment>* array, deIntptr ndx)
	: PoolArrayIteratorBase<T, Alignment>	(ndx)
	, m_array								(array)
{
}

template<typename T, deUint32 Alignment>
inline PoolArrayConstIterator<T, Alignment>::PoolArrayConstIterator (const PoolArrayIterator<T, Alignment>& iter)
	: PoolArrayIteratorBase<T, Alignment>	(iter)
	, m_array								(iter.getArray())
{
}

template<typename T, deUint32 Alignment>
inline PoolArrayConstIterator<T, Alignment>::~PoolArrayConstIterator (void)
{
}

// Arithmetic operators.

template<typename T, deUint32 Alignment>
inline PoolArrayConstIterator<T, Alignment> operator+ (const PoolArrayConstIterator<T, Alignment>& iter, deIntptr offs)
{
	return PoolArrayConstIterator<T, Alignment>(iter->getArray(), iter->getNdx()+offs);
}

template<typename T, deUint32 Alignment>
inline PoolArrayConstIterator<T, Alignment> operator+ (deUintptr offs, const PoolArrayConstIterator<T, Alignment>& iter)
{
	return PoolArrayConstIterator<T, Alignment>(iter->getArray(), iter->getNdx()+offs);
}

template<typename T, deUint32 Alignment>
PoolArrayConstIterator<T, Alignment> operator- (const PoolArrayConstIterator<T, Alignment>& iter, deIntptr offs)
{
	return PoolArrayConstIterator<T, Alignment>(iter.getArray(), iter.getNdx()-offs);
}

template<typename T, deUint32 Alignment>
deIntptr operator- (const PoolArrayConstIterator<T, Alignment>& iter, const PoolArrayConstIterator<T, Alignment>& other)
{
	return iter.getNdx()-other.getNdx();
}

// PoolArrayIterator<T> implementation.

template<typename T, deUint32 Alignment>
inline PoolArrayIterator<T, Alignment>::PoolArrayIterator (void)
	: PoolArrayIteratorBase<T, Alignment>	(0)
	, m_array								(DE_NULL)
{
}

template<typename T, deUint32 Alignment>
inline PoolArrayIterator<T, Alignment>::PoolArrayIterator (PoolArray<T, Alignment>* array, deIntptr ndx)
	: PoolArrayIteratorBase<T, Alignment>	(ndx)
	, m_array								(array)
{
}

template<typename T, deUint32 Alignment>
inline PoolArrayIterator<T, Alignment>::~PoolArrayIterator (void)
{
}

// Arithmetic operators.

template<typename T, deUint32 Alignment>
inline PoolArrayIterator<T, Alignment> operator+ (const PoolArrayIterator<T, Alignment>& iter, deIntptr offs)
{
	return PoolArrayIterator<T, Alignment>(iter.getArray(), iter.getNdx()+offs);
}

template<typename T, deUint32 Alignment>
inline PoolArrayIterator<T, Alignment> operator+ (deUintptr offs, const PoolArrayIterator<T, Alignment>& iter)
{
	return PoolArrayIterator<T, Alignment>(iter.getArray(), iter.getNdx()+offs);
}

template<typename T, deUint32 Alignment>
PoolArrayIterator<T, Alignment> operator- (const PoolArrayIterator<T, Alignment>& iter, deIntptr offs)
{
	return PoolArrayIterator<T, Alignment>(iter.getArray(), iter.getNdx()-offs);
}

template<typename T, deUint32 Alignment>
deIntptr operator- (const PoolArrayIterator<T, Alignment>& iter, const PoolArrayIterator<T, Alignment>& other)
{
	return iter.getNdx()-other.getNdx();
}

} // de

// std::iterator_traits specializations
namespace std
{

template<typename T, deUint32 Alignment>
struct iterator_traits<de::PoolArrayConstIterator<T, Alignment> >
{
	typedef deIntptr					difference_type;
	typedef T							value_type;
	typedef const T*					pointer;
	typedef const T&					reference;
	typedef random_access_iterator_tag	iterator_category;
};

template<typename T, deUint32 Alignment>
struct iterator_traits<de::PoolArrayIterator<T, Alignment> >
{
	typedef deIntptr					difference_type;
	typedef T							value_type;
	typedef T*							pointer;
	typedef T&							reference;
	typedef random_access_iterator_tag	iterator_category;
};

} // std

#endif // _DEPOOLARRAY_HPP