/*****************************************************************************
    This code will test whether a hash table can be de-allocated successfully.
 *****************************************************************************/
#include <stdio.h>
#include <string.h>
#include "pslib_strict.h"
#include "psTest.h"
#include "psHash.h"
#define NUM_HASH_TABLE_BUCKETS 100
psS32 imGlobal = 0;

typedef struct
{
    char *name;
}
ID;

static void IdFree(ID *id);

static ID *IdAlloc(const char *name)
{
    ID *id = psAlloc(sizeof(ID));
    psMemSetDeallocator(id,(psFreeFunc)IdFree);
    id->name = psStringCopy(name);

    return id;
}

static void IdFree(ID *id)
{
    imGlobal++;
    psFree(id->name);
}

psS32 main()
{
    psHash *myHashTable = NULL;
    psS32 testStatus      = true;
    psS32 currentId = psMemGetId();
    ID* id = NULL;
    ID* replaceId = NULL;
    psS32 memLeaks        = 0;
    psBool  retVal = false;

    // Allocate memory for Hash Table
    printPositiveTestHeader(stdout,"psHash functions","psHashAlloc");
    myHashTable = psHashAlloc(NUM_HASH_TABLE_BUCKETS);
    if(myHashTable == NULL) {
        psError(PS_ERR_UNKNOWN,true,"Unable to allocate psHash table");
        return 10;
    }
    printFooter(stdout,"psHash functions","psHashAlloc",true);

    // Add items to Hash table
    id = IdAlloc("IDA");
    printPositiveTestHeader(stdout,"psHash functions","psHashAdd");
    retVal = psHashAdd(myHashTable, "ENTRY00", id);
    psFree(id);
    if( !retVal) {
        psError(PS_ERR_UNKNOWN, true, "psHashAdd unable to add ENTRY00");
        return 1;
    }

    id = IdAlloc("IDB");
    retVal = psHashAdd(myHashTable, "ENTRY01", id);
    psFree(id);
    if (!retVal) {
        psError(PS_ERR_UNKNOWN, true, "psHashAdd unable to add ENTRY01");
        return 2;
    }

    id = IdAlloc("IDC");
    retVal = psHashAdd(myHashTable, "ENTRY02", id);
    psFree(id);
    if(!retVal) {
        psError(PS_ERR_UNKNOWN,true,"psHashAdd unable to add ENTRY02");
        return 3;
    }

    id = IdAlloc("IDD");
    retVal =psHashAdd(myHashTable, "ENTRY03", id);
    psFree(id);
    if(!retVal) {
        psError(PS_ERR_UNKNOWN,true,"psHashAdd unable to add ENTRY03");
        return 4;
    }
    printFooter(stdout,"psHash functions","psHashAdd",true);

    // Replace item with same key
    printPositiveTestHeader(stdout,"psHash replace item","psHashAdd");
    id = IdAlloc("IDE");
    retVal = psHashAdd(myHashTable,"ENTRY02",id);
    if(!retVal) {
        psError(PS_ERR_UNKNOWN,true,"psHashAdd unable to replace ENTRY02");
        return 5;
    }
    replaceId = psHashLookup(myHashTable,"ENTRY02");
    if(strcmp(replaceId->name,"IDE") != 0) {
        psError(PS_ERR_UNKNOWN,true,"psHashAdd did not replace ENTRY02 with correct item");
        return 6;
    }
    printFooter(stdout,"psHash replace item","psHashAdd",true);

    // Add with NULL hash table specified
    printNegativeTestHeader(stdout,"psHashAdd","NULL hash table","Hash table can not be NULL.",0);
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message: psHashAdd with null table");
    retVal = psHashAdd(NULL,"ENTRY04",id);
    if(retVal) {
        psError(PS_ERR_UNKNOWN,true,"psHashAdd added entry to NULL hash table.");
        return 20;
    }
    printFooter(stdout,"psHashAdd","NULL hash table",true);

    // Add with key to valid hash table NULL
    printNegativeTestHeader(stdout,"psHashAdd","NULL key","Hash key can not be NULL.",0);
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message: psHashAdd with null key");
    retVal = psHashAdd(myHashTable,NULL,id);
    if(retVal) {
        psError(PS_ERR_UNKNOWN,true,"psHashAdd added entry to hash table with NULL key.");
        return 21;
    }
    printFooter(stdout,"psHashAdd","NULL hash key",true);

    // Add NULL hash data to valid table and key
    printNegativeTestHeader(stdout,"psHashAdd","NULL hash data","Hash data can not be NULL.",0);
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message: psHashAdd with null data");
    retVal = psHashAdd(myHashTable,"ENTRY04",NULL);
    if(retVal) {
        psError(PS_ERR_UNKNOWN,true,"psHashAdd added entry to hash table with NULL data.");
        return 22;
    }
    printFooter(stdout,"psHashAdd","NULL hash data",true);

    // Free hash table
    printPositiveTestHeader(stdout,"psHash functions","psHashFree");
    psFree(id);

    psFree(myHashTable);

    if (imGlobal != 5) {
        fprintf(stderr, "%s: only (%d/4) entries were freed",
                __func__, imGlobal);
        testStatus = false;
    }

    printFooter(stdout,"psHash functions","psHashFree()",testStatus);

    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
    if (memLeaks != 0) {
        psAbort("Memory Leaks! (%d leaks)", memLeaks);
    }
    psMemCheckCorruption(1);

    return (!testStatus);
}
