#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "psLib.h"

/*****************************************************************************/
/*
 * My callbacks
 */
static long my_MemAllocateCB(const psMemBlock *ptr)
{
    static int incr = 0;		// "p_psMemAllocateID += incr"

    assert (ptr != NULL);		// prevent compiler warnings
    
    fprintf(stderr, "Allocating block %ld (%s:%d)\n",
	    ptr->id, ptr->file, ptr->lineno);
    
    incr = 2;				// See every other allocation

    return incr;
}

static void *my_MemExhaustedCB(size_t size)
{
    psError(__func__, ENOMEM, 1, "Bye bye sweet world\n");
    
    return NULL;
}

static psMemProblemCB default_MemProblemCB = NULL;

static void my_MemProblemCB(const psMemBlock *ptr,
			    const char *file, int lineno)
{
    fprintf(stderr, "I foresee trouble:\n");

    default_MemProblemCB(ptr, file, lineno);
}
    
/*****************************************************************************/

int main(void)
{
    /*
     * Survive any aborts that are generated
     * This doesn't seem to work on mac os/x 10.2.8
     */
    if (signal(SIGABRT, SIG_IGN) == SIG_ERR) {
	perror("Installing a SIGABRT handler");
    }
    /*
     * Install my callbacks
     */
    (void)psMemAllocateCBSet(my_MemAllocateCB);
    default_MemProblemCB = psMemProblemCBSet(my_MemProblemCB);
    (void)psMemExhaustedCBSet(my_MemExhaustedCB);
    /*
     * Request callback on first allocation
     */
    (void)psMemAllocateIDSet(1);
    /*
     * Start allocating
     */
    char *str = psAlloc(10);
    double *x = psAlloc(1);
    int *i = psAlloc(1);
    *i = 1;
    *x = 1.234;

    psFree(i);
    
    str = psRealloc(str, 20);
    psFree(str);
#if 0
    /*
     * An illegal double-free
     */
    psFree(str);
#endif

#if 0
    /*
     * Free memory not allocated with psAlloc
     */
    psFree(psAlloc(1000) + 500);
#endif

#if 1
    /*
     * Ask for more memory than we can get
     */
    psAlloc((size_t)-1 - sizeof(psMemBlock));
#endif

#if 0
    /*
     * Increment refCounter on non-malloced pointer
     */
    psMemGetRefCounter(malloc(1));
#endif

#if 0
    /*
     * Mess with the refCounter
     */
    (void)psMemIncrRefCounter(x);
    (void)psMemIncrRefCounter(x);
    (void)psMemDecrRefCounter(x);	// now == 2
#endif

#if 0
    /*
     * A memory leak
     */
    {
	char *str2 = psAlloc(20);
	
	sprintf(str2, "XXX");
    }
#else
    psFree(x);
#endif

#if 1
    /*
     * Corrupt some memory
     */
    char *c = psAlloc(1);
    c[-4] = 0;
#endif

    /*************************************************************************/
    /*
     * Check for memory corruption
     */
    if(psMemCheckCorruption(1)) {
	psError(__func__, PS_ERR_MEMORY_CORRUPTION, 1, "Detected corrupted memory\n\n");
    }
    /*
     * Check for memory leaks
     */
    psMemBlock **leaks = NULL;
    int nleak = psMemCheckLeaks(0, &leaks, NULL);

    if (nleak > 0) {
	fprintf(stderr, "Memory is leaking (%d blocks):\n", nleak);
	fprintf(stderr, "   %20s:line ID\n", "file");
	for (int i = 0; i < nleak; i++) {
	    fprintf(stderr, "   %20s:%-4d %ld\n",
		    leaks[i]->file, leaks[i]->lineno, leaks[i]->id);
	}
	psFree(leaks);
    }

    if ((nleak = psMemCheckLeaks(0, NULL, NULL)) > 0) {
	fprintf(stderr, "Memory is still leaking (%d blocks):\n", nleak);
	psMemCheckLeaks(0, NULL, stderr);
    }

    return 0;
}
