#if !defined(PS_ARRAY_H)
#define PS_ARRAY_H

/**
 * Declare TYPEArray
 */
#define PS_DECLARE_ARRAY_TYPE(TYPE) \
    typedef struct { \
	psType type;			/* Type of data.  THIS STRUCT ELEMENT MUST BE FIRST IN THE STRUCT */\
	int nalloc;			/* Total number of elements available */\
	int n;				/* Number of elements in use */\
	TYPE *arr;			/* the actual data */ \
    } PS_CONCAT(TYPE, Array); \
    \
PS_CONCAT(TYPE, Array) *PS_CONCAT3(TYPE,Array,Alloc)(int n); \
PS_CONCAT(TYPE, Array) *PS_CONCAT3(TYPE,Array,Realloc)(PS_CONCAT(TYPE, Array) *arr, int n); \
void PS_CONCAT3(TYPE,Array,Free)(PS_CONCAT(TYPE, Array) *arr)

/*****************************************************************************/
/**
 * Generate the code TYPEArray's constructors/destructors
 */
#define PS_CREATE_ARRAY_TYPE(TYPE) \
    P_PS_CREATE_ARRAY_TYPE(,,TYPE)

#define P_PS_CREATE_ARRAY_TYPE(QUAL,PREFIX,TYPE) \
\
QUAL PS_CONCAT(TYPE, Array) *PS_CONCAT4(PREFIX,TYPE,Array,Alloc)(int n) /* initial dimension */ \
{ \
    PS_CONCAT(TYPE, Array) *arr = psAlloc(sizeof(PS_CONCAT(TYPE, Array))); \
    \
    arr->nalloc = arr->n = n; \
    \
    if (n == 0) { \
	arr->arr = NULL; \
    } else { \
	arr->arr = psAlloc(n*sizeof(TYPE)); \
    } \
    \
    return arr; \
} \
\
QUAL PS_CONCAT(TYPE, Array) *PS_CONCAT4(PREFIX,TYPE,Array,Realloc)(PS_CONCAT(TYPE, Array) *arr, int n) \
{ \
    if (arr == NULL) { \
	return PS_CONCAT4(PREFIX,TYPE,Array,Alloc)(n); \
    } \
    \
    if (n <= arr->nalloc) { \
	if (arr->n < n) { \
	    arr->n = n; \
	} \
    } else { \
        arr->arr = psRealloc(arr->arr, n*sizeof(TYPE)); \
	arr->nalloc = n; \
    } \
    \
    return arr; \
} \
\
QUAL void PS_CONCAT4(PREFIX,TYPE,Array,Free)(PS_CONCAT(TYPE,Array) *arr) \
{ \
    if (arr == NULL) { \
	return; \
    } \
    \
    psFree(arr->arr); \
    psFree(arr); \
}

/*****************************************************************************/
/**
 * Declare array of pointers
 */
#define PS_DECLARE_ARRAY_PTR_TYPE(TYPE) \
    typedef TYPE *P_PS_CONCAT(TYPE, Ptr); \
    PS_DECLARE_ARRAY_TYPE(PS_CONCAT(TYPE, Ptr))

/**
 * Create constructors/destructors for array of pointers
 */
#define PS_CREATE_ARRAY_PTR_TYPE(TYPE); \
    P_PS_CREATE_ARRAY_TYPE(static, my_, P_PS_CONCAT(TYPE, Ptr)) \
    \
    PS_CONCAT(TYPE, PtrArray) *PS_CONCAT3(TYPE,PtrArray,Alloc)(int n) \
    { \
	PS_CONCAT(TYPE, PtrArray) *arr = PS_CONCAT4(my_,TYPE,PtrArray,Alloc)(n); \
	for (int i = 0; i < arr->n; i++) { \
	    arr->arr[i] = psMemIncrRefCounter(PS_CONCAT(TYPE, Alloc)());\
	} \
        \
	return arr; \
    } \
    \
    PS_CONCAT(TYPE, PtrArray) *PS_CONCAT3(TYPE,PtrArray,Realloc)(PS_CONCAT(TYPE, PtrArray) *arr, int n) \
    { \
	for (int i = n; i < arr->n; i++) { \
	    PS_CONCAT(TYPE, Free)(psMemDecrRefCounter(arr->arr[i]));\
	} \
        \
	return PS_CONCAT4(my_,TYPE,PtrArray,Realloc)(arr, n); \
    } \
    \
    void PS_CONCAT3(TYPE,PtrArray,Free)(PS_CONCAT2(TYPE,PtrArray) *arr) \
    { \
	if (arr == NULL) { return; } \
	for (int i = 0; i < arr->nalloc; i++) { \
	    PS_CONCAT(TYPE, Free)(psMemDecrRefCounter(arr->arr[i])); \
	} \
	PS_CONCAT4(my_, TYPE, PtrArray, Free)(arr); \
    }

#endif
