#if !defined(PS_ARRAY_H)
#define PS_ARRAY_H

/**
 * Concatenate two macro arguments
 */
#define P_PS_CONCAT(A, B) A ## B	//!< Expands to AB
#define PS_CONCAT(A, B) A ## B		//!< Also Expands to AB
#define PS_CONCAT2(A, B) PS_CONCAT(A, B) //!< Also expands to AB
#define PS_CONCAT3(A, B, C) A ## B ## C //!< Expands to ABC
#define PS_CONCAT4(A, B, C, D) A ## B ## C ## D //!< Expands to ABCD

#include <stdlib.h>
/**
 * Declare TYPEArray
 */
#define PS_DECLARE_ARRAY_TYPE(TYPE) \
    typedef struct { \
	int size;			/* number of allocated elements */ \
	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, int s); \
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 */\
                                                   int s) /* initial number of elements */ \
{ \
    PS_CONCAT(TYPE, Array) *arr = psAlloc(sizeof(PS_CONCAT(TYPE, Array))); \
    \
    if (n > s) { \
	psLogMsg(PS_STRING(TYPE) "ArrayAlloc", PS_LOG_ERROR, "Too few elements requested in array (%d < %d)\n", s, n); \
	s = n; \
    } \
    \
    arr->size = s; \
    arr->n = n; \
    \
    if (s == 0) { \
	arr->arr = NULL; \
    } else { \
	arr->arr = psAlloc(s*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, n); \
    } \
    \
    if (n <= arr->size) { \
	if (arr->n < n) { \
	    arr->n = n; \
	} \
    } else { \
        arr->arr = psRealloc(arr->arr, n*sizeof(TYPE)); \
	arr->size = 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, int s) \
    { \
	PS_CONCAT(TYPE, PtrArray) *arr = PS_CONCAT4(my_,TYPE,PtrArray,Alloc)(n, s); \
	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->size; i++) { \
	    PS_CONCAT(TYPE, Free)(psMemDecrRefCounter(arr->arr[i])); \
	} \
	PS_CONCAT4(my_, TYPE, PtrArray, Free)(arr); \
    }

#endif
