#ifndef _DEPOOLARRAY_H
#define _DEPOOLARRAY_H
/*-------------------------------------------------------------------------
 * drawElements Memory Pool 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 Memory pool array class.
 *//*--------------------------------------------------------------------*/

#include "deDefs.h"
#include "deMemPool.h"

enum
{
	DE_ARRAY_ELEMENTS_PER_PAGE_LOG2	= 4		/*!< \internal 16 */
};

/*--------------------------------------------------------------------*//*!
 * \internal
 * \brief Type-independent version of the array template class.
 *//*--------------------------------------------------------------------*/
typedef struct dePoolArray_s
{
	deMemPool*		pool;				/*!< Pool from which all memory is allocated from.	*/

	int				elementSize;		/*!< Size of the element (in bytes).				*/
	int				numElements;		/*!< Number of elements in the array.				*/
	int				capacity;			/*!< Number of allocated elements in the array.		*/

	int				pageTableCapacity;	/*!< Size of the page table.						*/
	void**			pageTable;			/*!< Pointer to the page table.						*/
} dePoolArray;

DE_BEGIN_EXTERN_C

dePoolArray*	dePoolArray_create			(deMemPool* pool, int elementSize);
deBool			dePoolArray_reserve			(dePoolArray* arr, int capacity);
deBool			dePoolArray_setSize			(dePoolArray* arr, int size);

void			dePoolArray_selfTest		(void);

DE_END_EXTERN_C

/*--------------------------------------------------------------------*//*!
 * \brief Declare a template pool array class.
 * \param TYPENAME	Type name of the declared array.
 * \param VALUETYPE	Type of the value contained in the array.
 *
 * This macro declares a pool array with all the necessary functions for
 * operating with it. All allocated memory is taken from the memory pool
 * given to the constructor.
 *
 * The array is implemented by having a set of pages (which store the
 * elements) and a page table with pointers to each of them. The pages
 * are allocated individually whenever they are needed, but the page
 * table grows exponentially. This keeps the memory overhead for large
 * arrays very small. On the other hand, the relative overhead for very
 * small arrays is prohibitive (the minimum allocation is 16 elements).
 *
 * The functions for operating the array are:
 * \todo [petri] Figure out how to comment these in Doxygen-style.
 *
 * \code
 * Array*   Array_create            (deMemPool* pool);
 * int      Array_getNumElements    (const Array* array);
 * deBool   Array_reserve           (Array* array, int size);
 * deBool   Array_setSize           (Array* array, int size);
 * void		Array_reset				(Array* array);
 * Element  Array_get               (Array* array, int ndx);
 * deBool   Array_set               (Array* array, int ndx, Element elem);
 * deBool   Array_pushBack          (Array* array, Element elem);
 * Element  Array_popBack           (Array* array);
 * void     Array_swap              (Array* array, int aNdx, int bNdx);
 * \endcode
*//*--------------------------------------------------------------------*/
#define DE_DECLARE_POOL_ARRAY(TYPENAME, VALUETYPE)		\
    \
typedef struct TYPENAME##_s					\
{											\
	deMemPool*			pool;				\
											\
	int					elementSize;		\
	int					numElements;		\
	int					capacity;			\
											\
	int					pageTableCapacity;	\
	DE_PTR_TYPE(VALUETYPE)*	pageTable;		\
} TYPENAME; /* NOLINT(TYPENAME) */			\
\
DE_INLINE TYPENAME*	TYPENAME##_create			(deMemPool* pool);															\
DE_INLINE int		TYPENAME##_getNumElements	(const TYPENAME* arr)									DE_UNUSED_FUNCTION;	\
DE_INLINE deBool	TYPENAME##_reserve			(DE_PTR_TYPE(TYPENAME) arr, int capacity)				DE_UNUSED_FUNCTION;	\
DE_INLINE deBool	TYPENAME##_setSize			(DE_PTR_TYPE(TYPENAME) arr, int size)					DE_UNUSED_FUNCTION;	\
DE_INLINE void		TYPENAME##_reset			(DE_PTR_TYPE(TYPENAME) arr)								DE_UNUSED_FUNCTION;	\
DE_INLINE VALUETYPE	TYPENAME##_get				(const TYPENAME* arr, int ndx)							DE_UNUSED_FUNCTION;	\
DE_INLINE void		TYPENAME##_set				(DE_PTR_TYPE(TYPENAME) arr, int ndx, VALUETYPE elem)	DE_UNUSED_FUNCTION;	\
DE_INLINE deBool	TYPENAME##_pushBack			(DE_PTR_TYPE(TYPENAME) arr, VALUETYPE elem)				DE_UNUSED_FUNCTION;	\
DE_INLINE VALUETYPE	TYPENAME##_popBack			(DE_PTR_TYPE(TYPENAME) arr)								DE_UNUSED_FUNCTION;	\
DE_INLINE deBool	TYPENAME##_copy				(DE_PTR_TYPE(TYPENAME) dst, const TYPENAME* src)		DE_UNUSED_FUNCTION;	\
DE_INLINE void		TYPENAME##_swap				(DE_PTR_TYPE(TYPENAME) arr, int aNdx, int bNdx)			DE_UNUSED_FUNCTION;	\
\
DE_INLINE TYPENAME* TYPENAME##_create (deMemPool* pool)    \
{    \
	return (TYPENAME*)dePoolArray_create(pool, sizeof(VALUETYPE));    \
}    \
\
DE_INLINE int TYPENAME##_getNumElements (const TYPENAME* arr)    \
{    \
	return arr->numElements;    \
}    \
\
DE_INLINE deBool TYPENAME##_reserve (DE_PTR_TYPE(TYPENAME) arr, int capacity)    \
{    \
	if (capacity > arr->capacity)    \
		return dePoolArray_reserve((dePoolArray*)arr, capacity);    \
	return  DE_TRUE;    \
}    \
\
DE_INLINE deBool TYPENAME##_setSize (DE_PTR_TYPE(TYPENAME) arr, int size)    \
{    \
	if (size > arr->capacity)    \
		return dePoolArray_setSize((dePoolArray*)arr, size);    \
\
	arr->numElements = size;    \
	return DE_TRUE;    \
}    \
\
DE_INLINE void TYPENAME##_reset (DE_PTR_TYPE(TYPENAME) arr)    \
{    \
	arr->numElements = 0;    \
}    \
\
DE_INLINE VALUETYPE TYPENAME##_get (const TYPENAME* arr, int ndx)    \
{    \
	DE_ASSERT(ndx >= 0 && ndx < arr->numElements);    \
	{    \
		int pageNdx	= (ndx >> DE_ARRAY_ELEMENTS_PER_PAGE_LOG2);    \
		int subNdx	= ndx & ((1 << DE_ARRAY_ELEMENTS_PER_PAGE_LOG2) - 1);    \
		return ((VALUETYPE*)arr->pageTable[pageNdx])[subNdx];    \
	}    \
}    \
\
DE_INLINE void TYPENAME##_set (DE_PTR_TYPE(TYPENAME) arr, int ndx, VALUETYPE elem)    \
{    \
	DE_ASSERT(ndx >= 0 && ndx < arr->numElements);    \
	{    \
		int pageNdx	= (ndx >> DE_ARRAY_ELEMENTS_PER_PAGE_LOG2);    \
		int subNdx	= ndx & ((1 << DE_ARRAY_ELEMENTS_PER_PAGE_LOG2) - 1);    \
		((VALUETYPE*)arr->pageTable[pageNdx])[subNdx] = elem;    \
	}    \
}    \
\
DE_INLINE deBool TYPENAME##_pushBack (DE_PTR_TYPE(TYPENAME) arr, VALUETYPE elem)    \
{    \
	if ((arr->numElements + 1 >= arr->capacity) && !TYPENAME##_reserve(arr, arr->numElements + 1)) \
		return DE_FALSE; \
	arr->numElements++; \
	TYPENAME##_set(arr, arr->numElements - 1, elem); \
	return DE_TRUE;    \
}    \
\
DE_INLINE VALUETYPE TYPENAME##_popBack (DE_PTR_TYPE(TYPENAME) arr)    \
{    \
	int ndx		= arr->numElements - 1; \
	int pageNdx	= (ndx >> DE_ARRAY_ELEMENTS_PER_PAGE_LOG2);    \
	int subNdx	= ndx & ((1 << DE_ARRAY_ELEMENTS_PER_PAGE_LOG2) - 1);    \
	DE_ASSERT(arr->numElements > 0); \
	arr->numElements--; \
	/* \note We access a value which is out-of-bounds, but we know it to be safe. */ \
	return ((VALUETYPE*)arr->pageTable[pageNdx])[subNdx];    \
}    \
\
DE_INLINE deBool TYPENAME##_copy (DE_PTR_TYPE(TYPENAME) dst, const TYPENAME* src)		\
{																			\
	DE_ASSERT(dst && src);													\
	{																		\
		int numElements = src->numElements;									\
		int ndx;															\
		if (!TYPENAME##_setSize(dst, numElements))							\
			return DE_FALSE;												\
		for (ndx = 0; ndx < numElements; ndx++)								\
			TYPENAME##_set(dst, ndx, TYPENAME##_get(src, ndx));				\
	}																		\
	return DE_TRUE;															\
}																			\
\
DE_INLINE void TYPENAME##_swap (DE_PTR_TYPE(TYPENAME) arr, int aNdx, int bNdx)	\
{	\
	VALUETYPE tmp = TYPENAME##_get(arr, aNdx);	\
	TYPENAME##_set(arr, aNdx, TYPENAME##_get(arr, bNdx));	\
	TYPENAME##_set(arr, bNdx, tmp);	\
}	\
\
struct TYPENAME##Dummy_s { int dummy; }

/*--------------------------------------------------------------------*//*!
 * \brief Declare a sort function for an array.
 * \param TYPENAME	Type name of the declared array.
 * \param VALUETYPE	Type of the value contained in the array.
 * \param SORTNAME	Name for this specific sort.
 * \param CMPFUNC	Comparison function for sorting.
 *
 * This macro declares a sort function for an array declared using
 * DE_DECLARE_POOL_ARRAY macro.
 *
 * Sorting algorithm is heap sort since it requires constant amount of
 * auxiliary space and is in-place sort. Worst-case run-time is O(n log n)
 * and sort is NOT stable.
 *
 * CMPFUNC is used to compare elements in array. It must accept two
 * parameters and return negative integer if first is smaller than, 0 if
 * both are equal and positive integer if first is larger than second.
 *
 * The functions for sorting array are:
 * \todo [petri] Figure out how to comment these in Doxygen-style.
 *
 * \code
 * void		Array_sortName			(Array* array);
 * void		Array_sortNameHeapify	(Array* array);
 * void		Array_sortNameShiftDown	(Array* array, int start, int end);
 * \endcode
*//*--------------------------------------------------------------------*/
#define DE_DECLARE_POOL_ARRAY_SORT(TYPENAME, VALUETYPE, SORTNAME, CMPFUNC)	\
\
DE_INLINE void TYPENAME##_##SORTNAME##ShiftDown (DE_PTR_TYPE(TYPENAME) arr, int startNdx, int endNdx)	\
{	\
	int rootNdx = startNdx;	\
	\
	while (rootNdx * 2 + 1 <= endNdx)	\
	{	\
		int childNdx = rootNdx * 2 + 1;	\
		\
		if ((childNdx + 1 <= endNdx) && (CMPFUNC(TYPENAME##_get(arr, childNdx), TYPENAME##_get(arr, childNdx + 1)) < 0))	\
			childNdx += 1;	\
		\
		if (CMPFUNC(TYPENAME##_get(arr, rootNdx), TYPENAME##_get(arr, childNdx)) < 0)	\
		{	\
			TYPENAME##_swap(arr, rootNdx, childNdx);	\
			rootNdx = childNdx;	\
		}	\
		else	\
			break;	\
	}	\
}	\
\
DE_INLINE void TYPENAME##_##SORTNAME##Heapify (DE_PTR_TYPE(TYPENAME) arr)	\
{	\
	int startNdx = (TYPENAME##_getNumElements(arr) - 2) / 2;	\
	\
	while (startNdx >= 0)	\
	{	\
		TYPENAME##_##SORTNAME##ShiftDown(arr, startNdx, TYPENAME##_getNumElements(arr) - 1);	\
		startNdx -= 1;	\
	}	\
}	\
\
DE_INLINE void TYPENAME##_##SORTNAME (DE_PTR_TYPE(TYPENAME) arr)	\
{	\
	int endNdx = TYPENAME##_getNumElements(arr) - 1;	\
	\
	TYPENAME##_##SORTNAME##Heapify(arr);	\
	\
	while (endNdx > 0)	\
	{	\
		TYPENAME##_swap(arr, endNdx, 0);	\
		endNdx -= 1;	\
		TYPENAME##_##SORTNAME##ShiftDown(arr, 0, endNdx);	\
	}	\
}	\
\
struct TYPENAME##SORTNAME##Dummy_s { int dummy; }

/* Basic array types. */

DE_DECLARE_POOL_ARRAY(deIntArray, int);
DE_DECLARE_POOL_ARRAY(deInt8Array, deInt8);
DE_DECLARE_POOL_ARRAY(deUint8Array, deUint8);
DE_DECLARE_POOL_ARRAY(deInt16Array, deInt16);
DE_DECLARE_POOL_ARRAY(deUint16Array, deUint16);
DE_DECLARE_POOL_ARRAY(deInt32Array, deInt32);
DE_DECLARE_POOL_ARRAY(deUint32Array, deUint32);

#endif /* _DEPOOLARRAY_H */