/** @file  tst_psArray.c
 *
 *  @brief Test driver for psArray integer functions
 *
 *  This test driver contains the following tests for psArray test point 1:
 *     A)  Create void pointer array
 *     B)  Add data to void pointer array
 *     C)  Reallocate void pointer array bigger
 *     D)  Reallocate void pointer array smaller
 *     E)  Reallocate with null pointer
 *     F)  Remove item from array
 *     G)  Remove invalid item from array
 *     H)  Remove item from null array
 *     I)  Remove null item from array
 *     J)  Free void pointer array
 *
 *  @author  Ross Harman, MHPCC
 *
 *  @version $Revision: 1.4 $  $Name: not supported by cvs2svn $
 *  @date  $Date: 2005-10-01 02:22:26 $
 *
 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
 *
 */

#include <math.h>

#include "pslib_strict.h"
#include "psTest.h"

typedef struct
{
    psS32 x;
    float y;
}
testStruct;

static int testStructCompare(const void **a, const void **b)
{
    testStruct* first = (testStruct*)*a;
    testStruct* second = (testStruct*)*b;

    if(first->x < second->x)
        return -1;
    else if (first->x > second->x)
        return 1;
    else
        return 0;
}

static psS32 testArray( void );
static psS32 testArray01( void );
static psS32 testArrayAdd( void );
static psS32 testArrayGetSet( void );


testDescription tests[] = {
                              {testArray, -1, "psArray", 0, false},
                              {testArray01, -2, "psArray", 0, false},
                              {testArrayAdd, 788, "psArrayAdd", 0, false},
                              {testArrayGetSet, -3, "psArrayGetSet", 0, false},
                              {NULL}
                          };

psS32 main( psS32 argc, char* argv[] )
{
    psLogSetLevel( PS_LOG_INFO );

    return ( ! runTestSuite( stderr, "psArray", tests, argc, argv ) );
}


psS32 testArray(void)
{
    // Create array of pointers
    testStruct *mySt[10];
    psArray *psArr1 = NULL;

    // Test A - Create void pointer array
    printPositiveTestHeader(stderr,"psArray", "Create void pointer array");
    psArray *psArr = psArrayAlloc(5);
    if (psArr->nalloc != 5) {
        psError(PS_ERR_UNKNOWN, true,"psArray didn't have proper number of elements.");
        return 1;
    }
    printFooter(stderr, "psArray", "Create void pointer array", true);


    // Test B - Add data to void pointer array
    printPositiveTestHeader(stderr, "psArray", "Add data to void pointer array");
    for(psS32 i = 0; i < 5; i++) {
        testStruct *ts = psAlloc(sizeof(testStruct));
        ts->x = 10*i;
        ts->y = 10.1*i;
        mySt[i] = ts;
        psArr->data[i] = ts;
        psMemIncrRefCounter(ts);
    }

    for(psS32 i = 0; i < 5; i++) {
        testStruct *ts = (testStruct*)psArr->data[i];
        fprintf(stderr,"ts[%d].x = %d ts[%d].y = %.2f\n", i, ts->x, i, ts->y);
        if (fabsf(ts->x - 10*i) > 0.01f || fabsf(ts->y - 10.1*i) > 0.01f) {
            psError(PS_ERR_UNKNOWN, true,"Couldn't properly get elements from array.");
            return 2;
        }
    }
    fprintf(stderr,"array size = %ld\n", psArr->nalloc);
    if (psArr->nalloc != 5) {
        psError(PS_ERR_UNKNOWN, true,"Array Size wrong");
        return 3;
    }
    fprintf(stderr,"array population = %ld\n", psArr->n);
    if (psArr->n != 5) {
        psError(PS_ERR_UNKNOWN, true,"Array population wrong");
        return 4;
    }
    printFooter(stderr, "psArray", "Add data to void pointer array", true);


    // Test C - Reallocate void pointer array bigger
    printPositiveTestHeader(stderr,"psArray", "Reallocate void pointer array bigger");
    psArr = psArrayRealloc(psArr,10);
    fprintf(stderr,"Adding more elements to void pointer array...\n");
    for(psS32 i = 5; i < 10; i++) {
        testStruct *ts = psAlloc(sizeof(testStruct));
        ts->x = 10*i;
        ts->y = 10.1*i;
        mySt[i] = ts;
        psArr->data[i] = ts;
        psArr->n++;
        psMemIncrRefCounter(ts);
    }
    for(psS32 i = 0; i < 10; i++) {
        testStruct *ts = (testStruct*)psArr->data[i];
        fprintf(stderr,"ts[%d].x = %d ts[%d].y = %.2f\n", i, ts->x, i, ts->y);
        if (fabsf(ts->x - 10*i) > 0.01f || fabsf(ts->y - 10.1*i) > 0.01f) {
            psError(PS_ERR_UNKNOWN, true,"Couldn't properly get elements from array.");
            return 5;
        }
    }
    fprintf(stderr,"array size = %ld\n", psArr->nalloc);
    if (psArr->nalloc != 10) {
        psError(PS_ERR_UNKNOWN, true,"Array Size wrong");
        return 6;
    }
    fprintf(stderr,"array population = %ld\n", psArr->n);
    if (psArr->n != 10) {
        psError(PS_ERR_UNKNOWN, true,"Array Population wrong");
        return 7;
    }
    printFooter(stderr, "psArray", "Reallocate void pointer array bigger", true);

    // Test D - Reallocate void pointer array smaller
    printPositiveTestHeader(stderr,"psArray","Reallocate void pointer array smaller");
    psArr = psArrayRealloc(psArr,3);
    for(psS32 i = 0; i < 3; i++) {
        testStruct *ts = (testStruct*)psArr->data[i];
        fprintf(stderr,"ts[%d].x = %d ts[%d].y = %.2f\n", i, ts->x, i, ts->y);
        if (fabsf(ts->x - 10*i) > 0.01f || fabsf(ts->y - 10.1*i) > 0.01f) {
            psError(PS_ERR_UNKNOWN, true,"Couldn't properly get elements from array.");
            return 8;
        }
    }
    fprintf(stderr,"array size = %ld\n", psArr->nalloc);
    if (psArr->nalloc != 3) {
        psError(PS_ERR_UNKNOWN, true,"Array Size wrong");
        return 9;
    }
    fprintf(stderr,"array population = %ld\n", psArr->n);
    if (psArr->n != 3) {
        psError(PS_ERR_UNKNOWN, true,"Array Population wrong");
        return 10;
    }
    printFooter(stderr, "psArray", "Reallocate integer void pointer smaller", true);

    // Test E - Reallocate with a null
    printNegativeTestHeader(stderr,"psArray","Reallocate with a null array",
                            "Error message generator", 0 );
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message.");
    psArr1 = psArrayRealloc(NULL,5);
    if ( psArr1 != NULL ) {
        fprintf(stderr,"ERROR: Return is not NULL\n");
    }
    printFooter(stderr,"psArray","Reallocate with a null array",true);


    // Test F - Remove item from array
    printPositiveTestHeader(stderr, "psArray", "Remove valid item");
    if( !psArrayRemove(psArr,mySt[0]) ) {
        psError(PS_ERR_UNKNOWN,true,"Unable to remove valid item");
        return 100;
    }
    fprintf(stderr,"Array size after removal = %ld\n",psArr->n);
    if ( psArr->n != 2 ) {
        psError(PS_ERR_UNKNOWN, true, "Items in array not decremented after removal");
        return 101;
    }
    for(psS32 i = 0; i < psArr->n; i++) {
        testStruct *ts = (testStruct*)psArr->data[i];
        fprintf(stderr,"ts[%d].x = %d ts[%d].y = %.2f\n", i, ts->x, i, ts->y);
        if(fabsf(ts->x - 10*(i+1)) > 0.01f || fabsf(ts->y - 10.1*(i+1)) > 0.01f) {
            psError(PS_ERR_UNKNOWN, true, "Elements not as expected after remove.");
            return 102;
        }
    }
    printFooter(stderr,"psArray","Remove valid item", true);

    // Test G - Remove invalid item from array
    printPositiveTestHeader(stderr, "psArray", "Remove invalid item from array");
    if( psArrayRemove(psArr,mySt[9]) ) {
        psError(PS_ERR_UNKNOWN,true,"Removed invalid item from array");
        return 103;
    }
    printFooter(stderr,"psArray","Remove invalid item from array",true);

    // Test H - Remove item from null array
    printPositiveTestHeader(stderr, "psArray", "Remove item from null array");
    if ( psArrayRemove(NULL,mySt[1]) ) {
        psError(PS_ERR_UNKNOWN,true,"Removed valid item from null array");
        return 104;
    }
    printFooter(stderr,"psArray","Remove item from null array",true);

    // Test I - Remove null item from array
    printPositiveTestHeader(stderr, "psArray", "Remove null item from array");
    if( psArrayRemove(psArr,NULL) ) {
        psError(PS_ERR_UNKNOWN,true,"Remove null item from array",true);
        return 105;
    }
    printFooter(stderr,"psArray","Remove null item from array",true);

    // Test J - Free void pointer array
    printPositiveTestHeader(stderr, "psArray", "Free void pointer array");
    psFree(psArr);
    for(psS32 i = 0; i < 10; i++) {
        psFree(mySt[i]);
    }

    printFooter(stderr, "psArray" ,"Free void pointer array", true);

    return 0;
}

psS32 testArray01(void)
{
    // Create array of pointers
    testStruct *mySt[10];

    // Test A - Create void pointer array
    printPositiveTestHeader(stderr,"psArray", "Create void pointer array");
    psArray *psArr = psArrayAlloc(10);
    if (psArr->nalloc != 10) {
        psError(PS_ERR_UNKNOWN, true,"psArray didn't have proper number of elements.");
        return 1;
    }
    printFooter(stderr, "psArray", "Create void pointer array", true);

    // Test B - Add data to void pointer array
    printPositiveTestHeader(stderr, "psArray", "Add data to void pointer array");
    for(psS32 i = 0; i < 10; i++) {
        testStruct *ts = psAlloc(sizeof(testStruct));
        ts->x = 10*(10-i);
        ts->y = 10.1*(10-i);
        mySt[i] = ts;
        psArr->data[i] = ts;
        psMemIncrRefCounter(ts);
    }

    for(psS32 i = 0; i < 10; i++) {
        testStruct *ts = (testStruct*)psArr->data[i];
        fprintf(stderr,"ts[%d].x = %d ts[%d].y = %.2f\n", i, ts->x, i, ts->y);
        if (fabsf(ts->x - 10*(10-i)) > 0.01f || fabsf(ts->y - 10.1*(10-i)) > 0.01f) {
            psError(PS_ERR_UNKNOWN, true,"Couldn't properly get elements from array.");
            return 2;
        }
    }
    fprintf(stderr,"array size = %ld\n", psArr->nalloc);
    if (psArr->nalloc != 10) {
        psError(PS_ERR_UNKNOWN, true,"Array Size wrong");
        return 3;
    }
    fprintf(stderr,"array population = %ld\n", psArr->n);
    if (psArr->n != 10) {
        psError(PS_ERR_UNKNOWN, true,"Array population wrong");
        return 4;
    }
    printFooter(stderr, "psArray", "Add data to void pointer array", true);

    // Test C - Sort data in array
    printPositiveTestHeader(stderr,"psArray","Sort data in array");
    psArr = psArraySort(psArr,testStructCompare);
    for(psS32 i = 0; i < 10; i++) {
        testStruct *ts = (testStruct*)psArr->data[i];
        fprintf(stderr,"ts[%d].x = %d ts[%d].y = %.2f\n", i, ts->x, i, ts->y);
        if (fabsf(ts->x - 10*(i+1)) > 0.01f || fabsf(ts->y - 10.1*(i+1)) > 0.01f) {
            psError(PS_ERR_UNKNOWN, true,"Couldn't properly get elements from array.");
            return 5;
        }
    }
    printFooter(stderr,"psArray","Sort data in array",true);

    // Test D - Attempt to sort null array
    printPositiveTestHeader(stderr,"psArray","Attempt to sort array");
    psArray* tempArr = psArraySort(NULL,testStructCompare);
    if(tempArr != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Array sort did not return null when sorting null array");
        return 6;
    }
    printFooter(stderr,"psArray","Attempt to sort array",true);

    // Test E - Free void pointer array
    printPositiveTestHeader(stderr, "psArray", "Free void pointer array");
    psFree(psArr);
    for(psS32 i = 0; i < 10; i++) {
        psFree(mySt[i]);
    }
    if( psMemCheckLeaks(0, NULL, stderr, false) != 0) {
        psError(PS_ERR_UNKNOWN,true,"Memory leaks detected.");
        return 110;
    }
    psS32 nBad = psMemCheckCorruption(0);
    if(nBad) {
        fprintf(stderr,"ERROR: Found %d bad memory blocks\n", nBad);
        return 111;
    }
    printFooter(stderr, "psArray" ,"Free void pointer array", true);

    return 0;
}

psS32 testArrayAdd( void )
{
    int subtest = 0;
    float* data;
    int nalloc = 5;
    int n = 0;
    int delta = 5;

    // allocate the array.
    psArray* arr = psArrayAlloc(nalloc);
    arr->n = n;

    // test arrayAdd until n == nalloc
    while (n < nalloc) {
        data = psAlloc(sizeof(float));
        arr = psArrayAdd(arr, delta*2, data); // make delta unique versus next delta used.

        subtest++;
        if (psMemGetRefCounter(data) != 2) {
            // in response of Bug #302
            psError(PS_ERR_UNKNOWN, true,
                    "psArrayAdd did not increment the data reference count.");
            return subtest;
        }

        subtest++;
        if (arr->nalloc != nalloc) {
            psError(PS_ERR_UNKNOWN,true,
                    "psArrayAdd expanded the psArray unnecessarily.  n=%d",
                    n);
            return subtest;
        }

        subtest++;
        if (arr->n != ++n) {
            psError(PS_ERR_UNKNOWN,true,
                    "psArrayAdd did not increment the size of the psArray. n=%d",
                    n);
            return subtest;
        }

        subtest++;
        if (arr->data[n-1] != data) {
            psError(PS_ERR_UNKNOWN,true,
                    "psArrayAdd didn't set the element to data. n=%d",
                    n);
            return subtest;
        }

        psFree(data);
    }

    // now try to add an element when the array is full.
    data = psAlloc(sizeof(float));
    arr = psArrayAdd(arr, delta, data);

    // make sure the array was expanded
    subtest++;
    if (arr->nalloc != nalloc+delta) {
        psError(PS_ERR_UNKNOWN,true,
                "psArrayAdd did not expand the psArray when it was already full."
                " old nalloc=%d, nalloc=%d, delta=%d",
                nalloc, arr->nalloc, delta);
        return subtest;
    }
    nalloc = arr->nalloc;

    subtest++;
    if (arr->n != ++n) {
        psError(PS_ERR_UNKNOWN,true,
                "psArrayAdd did not increment psArray.n by 1 after expanding it.");
        return subtest;
    }

    subtest++;
    if (arr->data[n-1] != data) {
        psError(PS_ERR_UNKNOWN,true,
                "psArrayAdd didn't set the second element to data.");
        return subtest;
    }

    psFree(data);

    // make the array full again (operation tested already)
    while (arr->n < arr->nalloc) {
        data = psAlloc(sizeof(float));
        arr = psArrayAdd(arr, 0, data);
        psFree(data);
    }
    nalloc = arr->nalloc;
    n = arr->n;

    // now add to full array with delta = 0; verify that the array is
    // expanded by 10
    data = psAlloc(sizeof(float));
    arr = psArrayAdd(arr, 0, data);
    psFree(data);

    subtest++;
    if (arr->nalloc != nalloc+10) {
        psError(PS_ERR_UNKNOWN,true,
                "psArrayAdd did not expand the psArray by 10 when delta < 1."
                " old nalloc=%d, nalloc=%d",
                nalloc, arr->nalloc);
        return subtest;
    }

    psFree(arr);

    return 0;  // the value that indicates success is part of the testDescription
}

psS32 testArrayGetSet( void )
{
    psArray *test;
    int *p1 = psAlloc(sizeof(int));
    int *p2 = psAlloc(sizeof(int));
    int *p3 = psAlloc(sizeof(int));
    test = psArrayAlloc(5);
    test->n = 0;
    *p1 = 10;
    *p2 = 4;
    *p3 = 666;

    if ( !psArraySet(test, 0, p1) )
        fprintf(stderr, "ArraySet failed to set S32 at position 0\n");
    if ( psArraySet(test, 10, p1) )
        fprintf(stderr, "ArraySet Improperly set S32 at out of range position\n");
    if ( !psArraySet(test, 1, p2) )
        fprintf(stderr, "ArraySet Failed to set S32 at position 1\n");
    if ( psArrayGet(test, 0) != p1 )
        fprintf(stderr, "ArrayGet Failed to return the correct S32 from position 0\n");
    if ( psArrayGet(test, -1) != p2)
        fprintf(stderr, "ArrayGet Failed to return the correct S32 from tail using -1\n");
    psFree(p1); // free ref from psArrayGet
    psFree(p2); // free ref from psArrayGet

    if ( psArraySet(test, -6, p3) )
        fprintf(stderr, "ArraySet failed to fail using an out of range negative number\n");

    psFree(test);
    psFree(p1);
    psFree(p2);
    psFree(p3);

    return 0;
}
