#if !defined(PS_MEMORY_H)
#define PS_MEMORY_H

#include <stdio.h> 			// needed for FILE
/** \file psMemory.h
 *  \brief Memory alloc/free routines
 *  \ingroup SystemGroup
 */

// Structures ***********************************************************

/** A function that frees an allocated pointer */
typedef void (*psFreeFcn)(void* ptr);

/** Memory ID number */
typedef unsigned long psMemoryId;

/** Reference counter number */
typedef unsigned long psReferenceCount;

/** Book-keeping data for storage allocator.
 *  N.b. sizeof(psMemBlock) must be chosen such that if ptr is a pointer
 *  returned by malloc, then ((char *)ptr + sizeof(psMemBlock)) is properly
 *  aligned for all storage types.
 */
typedef struct {
    const void* startblock;             ///< initialised to p_psMEMMAGIC
    struct psMemBlock* previousBlock;   ///< previous block in allocation list
    struct psMemBlock* nextBlock;       ///< next block allocation list
    psFreeFcn freeFcn;                  ///< deallocator.  If NULL, use generic deallocation.
    size_t  userMemorySize;             ///< the size of the user-portion of the memory block
    const psMemoryId id;                ///< a unique ID for this allocation
    const char* file;                   ///< set from __FILE__ in e.g. p_psAlloc
    const int lineno;                   ///< set from __LINE__ in e.g. p_psAlloc
    pthread_mutex_t   refCounterMutex;  ///< mutex to ensure exclusive access to reference counter
    psReferenceCount refCounter;        ///< how many times pointer is referenced
    const void* endblock;               ///< initialised to p_psMEMMAGIC
} psMemBlock;

/// prototype of a basic callback used by memory functions 
typedef psMemoryId (*psMemAllocateCallback)(const psMemBlock *ptr);

/// prototype of memory free callback used by memory functions 
typedef psMemoryId (*psMemFreeCallback)(const psMemBlock *ptr);

/// prototype of a callback used in error conditions 
typedef void (*psMemProblemCallback)(const psMemBlock *ptr, const char *file, int lineno);

/// prototype of a callback used when memory runs out 
typedef void *(*psMemExhaustedCallback)(size_t size);

/** Functions **************************************************************/
/** \addtogroup SystemGroup System Utilities
 *  \{
 */

/** Set the fcn pointer to the appropriate ps*Free function.  If NULL, then a generic free functionality is
 * used (i.e., the old psFree functionality).
 */
void p_psMemSetDeallocator(void* ptr, psFreeFcn func);

/** retrieves the function pointer to the higher-level free function to be used.  If NULL, then the
 * traditional psFree functionality shall be used.
 */
freeFcn p_psMemGetDeallocator(void* ptr);

/// Memory allocation. Underlying private function called by macro psAlloc. 
void *p_psAlloc(size_t size,		///< Size required
		const char *file,	///< File of call
		int lineno)		///< Line number of call
;

/// Memory re-allocation.  Underlying private function called by macro psRealloc. 
void *p_psRealloc(void *ptr,		///< Pointer to re-allocate
		  size_t size,		///< Size required
		  const char *file,	///< File of call
		  int lineno)		///< Line number of call
;

/// Free memory.  Underlying private function called by macro psFree. 
void p_psFree(void *ptr,		///< Pointer to free
	      const char *file,		///< File of call
	      int lineno)		///< Line number of call
;

/// Check for memory leaks 
int psMemCheckLeaks(psMemoryId id0,	///< don't list blocks with id < id0
		    psMemBlock ***arr,	///< pointer to array of pointers to leaked blocks, or NULL
		    FILE *fd)		///< print list of leaks to fd (or NULL)
;

/// Check for memory corruption 
int psMemCheckCorruption(bool abort_on_error) ///< Abort on detecting corruption?
;

/// Return reference counter 
psReferenceCount psMemGetRefCounter(void *vptr)	///< Pointer to get refCounter for
;

/// Increment reference counter and return the pointer 
void *psMemIncrRefCounter(void *vptr)	///< Pointer to increment refCounter, and return
;

/// Decrement reference counter and return the pointer 
void *psMemDecrRefCounter(void *vptr)	///< Pointer to decrement refCounter, and return
;

/// Set callback for problems 
psMemProblemCallback psMemProblemCallbackSet(psMemProblemCallback func) ///< Function to run
;

/// Set callback for out-of-memory 
psMemExhaustedCallback psMemExhaustedCallbackSet(psMemExhaustedCallback func) ///< Function to run
;

/// Set call back for when a particular memory block is allocated 
psMemAllocateCallback psMemAllocateCallbackSet(psMemAllocateCallback func) ///< Function to run
;

/// Set call back for when a particular memory block is freed 
psMemFreeCallback psMemFreeCallbackSet(psMemFreeCallback func) ///< Function to run
;

/** Default callback functions */
psMemExhaustedCallback psMemExhaustedCallbackDefault;
psMemProblemCallback psMemProblemCallbackDefault;
psMemAllocateCallback psMemAllocateCallbackDefault;
psMemFreeCallback psMemFreeCallbackDefault;

/// get next memory ID 
psMemoryId psMemGetId(void)
;

/// set p_psMemAllocateID to id 
psMemoryId psMemAllocateCallbackSetID(psMemoryId id) ///< ID to set
;

/// set p_psMemFreeID to id 
psMemoryId psMemFreeCallbackSetID(psMemoryId id) ///< ID to set
;

/* \} */ // End of SystemGroup Functions

/// Memory allocation. psAlloc sends file and line number to p_psAlloc. 
#define psAlloc(size) p_psAlloc(size, __FILE__, __LINE__)

/// Memory re-allocation.  psRealloc sends file and line number to p_psRealloc. 
#define psRealloc(ptr, size) p_psRealloc(ptr, size, __FILE__, __LINE__)

/// Free memory.  psFree sends file and line number to p_psFree. 
#define psFree(size) p_psFree(size, __FILE__, __LINE__)

/*
 * Ensure that any program using malloc/realloc/free will fail to compile
 */
#if !defined(PS_ALLOW_MALLOC)
#  define malloc(S) for
#  define realloc(P,S) for
#  define calloc(S) for
#  define free(P) for
#endif

#endif
