/** @file  tst_psPixels.c
 *
 *  @brief Contains the tests for psPixels.[ch]
 *
 *  @author Robert DeSonia, MHPCC
 *
 *  @version $Revision: 1.5 $
 *           $Name: not supported by cvs2svn $
 *  @date $Date: 2006-04-01 02:43:57 $
 *
 *  Copyright 2005 Maui High Performance Computing Center, University of Hawaii
 */

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

static psS32 testPixelsAlloc(void);
static psS32 testPixelsRealloc(void);
static psS32 testPixelsCopy(void);
static int testPixelsToMask(void);
static int testPixelsFromMask(void);
static int testPixelsConcatenate(void);
static psS32 testPixelsGetSet(void);
static psS32 testPixelsLength( void );

testDescription tests[] = {
                              {testPixelsAlloc,860,"psPixelsAlloc",0,false},
                              {testPixelsRealloc,862,"psPixelsRealloc",0,false},
                              {testPixelsCopy,863,"psPixelsCopy",0,false},
                              {testPixelsToMask,864,"psPixelsToMask",0,false},
                              {testPixelsFromMask,865,"psPixelsFromMask",0,false},
                              {testPixelsConcatenate,866,"psPixelsConcatenate",0,false},
                              {testPixelsGetSet,867,"psPixelsGet/Set",0,false},
                              {testPixelsLength,666,"psPixelsLength",0,false},
                              {NULL}
                          };

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

    if ( ! runTestSuite(stderr,"psPixels",tests,argc,argv) ) {
        psError(PS_ERR_UNKNOWN,true,"One or more tests failed");
        return 1;
    }
    return 0;
}

psS32 testPixelsAlloc(void)
{

    psPixels* p0 = psPixelsAlloc(0);

    if (p0 == NULL) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsAlloc failed to allocate a zero-sized psPixels");
        return 1;
    }
    if (p0->data != NULL) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsAlloc failed to set data to NULL for a zero-sized psPixels");
        return 2;
    }
    if (p0->n != p0->nalloc) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsAlloc failed to set n = 0");
        return 3;
    }
    if (p0->nalloc != 0) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsAlloc failed to set nalloc = 0");
        return 4;
    }

    psPixels* p1 = psPixelsAlloc(1);

    if (p1 == NULL) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsAlloc failed to allocate a one-sized psPixels");
        return 11;
    }
    if (p1->data == NULL) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsAlloc failed to allocate data for a one-sized psPixels");
        return 12;
    }
    p1->data[0].x = 1;
    p1->data[0].y = 2;
    p1->n++;
    if (p1->n != p1->nalloc) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsAlloc failed to set n = %ld", p1->nalloc);
        return 13;
    }
    if (p1->nalloc != 1) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsAlloc failed to set nalloc = 1 (%ld)",
                p1->nalloc);
        return 14;
    }

    psPixels* p2 = psPixelsAlloc(10);

    if (p2 == NULL) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsAlloc failed to allocate a one-sized psPixels");
        return 11;
    }
    if (p2->data == NULL) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsAlloc failed to allocate data for a one-sized psPixels");
        return 12;
    }
    for (int i = 0; i < 10; i++) {
        p2->data[i].x = i;
        p2->data[i].y = 100+i;
        p2->n++;
    }

    if (p2->n != p2->nalloc) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsAlloc failed to set n = %ld", p2->nalloc);
        return 13;
    }
    if (p2->nalloc != 10) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsAlloc failed to set nalloc = 1 (%ld)",
                p2->nalloc);
        return 14;
    }


    psFree(p2);
    psFree(p1);
    psFree(p0);

    return 0;
}

psS32 testPixelsRealloc(void)
{

    // first, tests that reallocing a NULL just allocates
    psPixels* p0 = psPixelsRealloc(NULL,10);
    p0->n = 10;

    if (p0 == NULL) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsRealloc failed to allocate a zero-sized psPixels");
        return 1;
    }
    if (p0->data == NULL) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsRealloc failed to set data to NULL for a zero-sized psPixels");
        return 2;
    }
    if (p0->n != p0->nalloc) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsRealloc failed to set n = 0");
        return 3;
    }
    if (p0->nalloc != 10) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsRealloc failed to set nalloc = 10");
        return 4;
    }
    for (int i = 0; i < 10; i++) {
        p0->data[i].x = i;
        p0->data[i].y = 100+i;
    }
    p0->n = 10;

    psPixels* p1 = psPixelsRealloc(p0, 20);
    //    p1->n = 20;

    if (p1 != p0) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsRealloc failed to return a resized input psPixels");
        return 11;
    }
    if (p1->data == NULL) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsRealloc failed to set data to non-NULL");
        return 12;
    }
    if (p1->n != 10) {
        psError(PS_ERR_UNKNOWN, true,
                "resizing up didn't preserve the size");
        return 13;
    }
    if (p1->nalloc != 20) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsRealloc failed to set nalloc = 0");
        return 14;
    }
    for (int i = 0; i < 10; i++) {
        if (p0->data[i].x != i || p0->data[i].y != 100+i) {
            psError(PS_ERR_UNKNOWN, true,
                    "The value of pixel %d was not preserved (%f,%f) vs (%d,%d).",
                    i, p0->data[i].x, p0->data[i].y, i, 100+i);
            return 15;
        }
    }
    for (int i = 10; i < 20; i++) {
        p1->data[i].x = i;
        p1->data[i].y = 100+i;
    }
    p1->n = 20;


    psPixels* p2 = psPixelsRealloc(p1,5);
    p2->n = 5;
    if (p2 != p1) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsRealloc didn't return input psPixels.");
        return 21;
    }
    if (p2->data == NULL) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsRealloc failed to allocate data");
        return 22;
    }

    if (p2->n != 5) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsRealloc failed to set n=nalloc after shrinking");
        return 23;
    }
    if (p2->nalloc != 5) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsRealloc failed to set nalloc properly");
        return 24;
    }

    psPixels* p3 = psPixelsRealloc(p2,0);

    if (p3 != p2) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsRealloc didn't return input psPixels.");
        return 31;
    }
    if (p3->data != NULL) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsRealloc failed to deallocate data when size=0");
        return 32;
    }

    if (p3->n != 0) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsRealloc failed to set n=nalloc after shrinking");
        return 33;
    }
    if (p3->nalloc != 0) {
        psError(PS_ERR_UNKNOWN, true,
                "psPixelsRealloc failed to set nalloc properly");
        return 34;
    }

    psFree(p3);

    return 0;
}

psS32 testPixelsCopy(void)
{
    psPixels* in1 = psPixelsAlloc(10);

    for (int i = 0; i < 10; i++) {
        in1->data[i].x = i;
        in1->data[i].y = 100+i;
    }
    in1->n = 10;

    psPixels* out1 = psPixelsCopy(NULL, in1);
    if (out1 == in1) {
        psError(PS_ERR_UNKNOWN, true,
                "output == input?");
        return 1;
    }
    if (out1 == NULL) {
        psError(PS_ERR_UNKNOWN, true,
                "output == NULL?");
        return 2;
    }

    if (out1->n != in1->n) {
        psError(PS_ERR_UNKNOWN, true,
                "out->n != in->n");
        return 3;
    }
    for (long i = 0; i < out1->n; i++) {
        if (out1->data[i].x != in1->data[i].x || out1->data[i].y != in1->data[i].y) {
            psError(PS_ERR_UNKNOWN, true,
                    "failed to copy the values correctly at index %ld", i);
            return 4;
        }
    }

    // now test what happens when copying a 0-length list.
    psPixels* in2 = psPixelsAlloc(0);
    psPixels* out2 = psPixelsCopy(out1, in2);
    if (out2 != out1) {
        psError(PS_ERR_UNKNOWN, true,
                "failed to recycle.");
        return 10;
    }
    if (out2->n != in2->n) {
        psError(PS_ERR_UNKNOWN, true,
                "failed to set size when copying 0-length pixel list.");
        return 11;
    }

    // Attempt to copy from NULL input
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL input");
    out2 = psPixelsCopy(out2,NULL);
    if (out2 != NULL) {
        psError(PS_ERR_UNKNOWN, true,
                "Copying a NULL should create a NULL.");
        return 20;
    }

    psFree(in1);
    psFree(in2);

    return 0;
}

int testPixelsToMask(void)
{
    // create a pixel list for
    psPixels* pixels = psPixelsAlloc(10);
    for (int i = 0; i < 10; i++) {
        pixels->data[i].x = i;
        pixels->data[i].y = i;
    }
    pixels->n = 10;

    psImage* mask = psPixelsToMask(NULL,pixels, psRegionSet(0,10,0,10), 1);

    if (mask == NULL) {
        psError(PS_ERR_UNKNOWN, true,
                "Resulting mask was null.");
        return 1;
    }
    if (mask->type.type != PS_TYPE_MASK) {
        psError(PS_ERR_UNKNOWN, true,
                "mask type was not PS_TYPE_MASK.");
        return 2;
    }

    for (int row=0;row<10;row++) {
        psMaskType* rowData = mask->data.PS_TYPE_MASK_DATA[row];
        for (int col=0;col<10;col++) {
            if ( (col==row && rowData[col] != 1) ||
                    (col!=row && rowData[col] != 0) ) {
                psError(PS_ERR_UNKNOWN,true,
                        "Mask has unexpected value, %d, at (%d,%d)",
                        rowData[col], col, row);
                return 10;
            }
        }
    }

    // test when input psPixels is NULL.
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error for NULL pixels");
    mask = psPixelsToMask(mask, NULL, psRegionSet(0,10,0,10), 1);
    if (mask != NULL) {
        psError(PS_ERR_UNKNOWN, true,
                "Resulting mask was not null though input psPixels was NULL.");
        return 20;
    }


    // Test for invalid region
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error for invalid range");
    mask = psPixelsToMask(mask,pixels,psRegionSet(10,0,10,0),1);
    if(mask != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not expect non-NULL for invalid range");
        return 21;
    }
    // Test for invalid region
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error for invalid range");
    mask = psPixelsToMask(mask,pixels,psRegionSet(0,-1,0,10),1);
    if(mask != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not expect non-NULL for invalid range");
        return 22;
    }

    psFree(mask);
    psFree(pixels);

    return 0;
}

int testPixelsFromMask(void)
{
    const int numRows = 10;
    const int numCols = 20;
    psImage* mask = psImageAlloc(numCols,numRows,PS_TYPE_MASK);
    for (int row=0;row<numRows;row++) {
        for (int col=0;col<numCols;col++) {
            mask->data.PS_TYPE_MASK_DATA[row][col] = (row*2 == col) ? 1 : 0;
        }
    }

    psPixels* pixels = psPixelsFromMask(NULL, mask, 1);

    if (pixels == NULL) {
        psError(PS_ERR_UNKNOWN, false,
                "resulting psPixels was NULL?");
        return 1;
    }

    if (pixels->n != numRows) {
        psError(PS_ERR_UNKNOWN, false,
                "wrong number of pixels in list.  Got %ld, should be %d.",
                pixels->n, numRows);
        return 2;
    }

    for (long i = 0; i < pixels->n; i++) {
        if (mask->data.PS_TYPE_MASK_DATA[(int)pixels->data[i].y][(int)pixels->data[i].x] != 1) {
            psError(PS_ERR_UNKNOWN, false,
                    "Item in psPixels list (%f,%f) didn't coorespond to a masked value in image",
                    pixels->data[i].x, pixels->data[i].y);
            return 3;
        }
    }

    // Attempt to create pixels from NULL mask
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error for NULL mask");
    pixels = psPixelsFromMask(pixels, NULL, 1);

    if (pixels != NULL) {
        psError(PS_ERR_UNKNOWN, false,
                "given a NULL image, got a non-NULL mask?");
        return 4;
    }

    psFree(mask);

    return 0;
}

int testPixelsConcatenate(void)
{
    // create a pixel list for
    psPixels* pixels = psPixelsAlloc(10);
    psPixels* pixels1 = psPixelsAlloc(10);
    for (int i = 0; i < 10; i++) {
        pixels->data[i].x = i;
        pixels->data[i].y = i;
        if (i%2) {
            pixels1->data[i].x = -i;
        } else {
            pixels1->data[i].x = i;
        }
        pixels1->data[i].y = i;
    }
    pixels->n = 10;
    pixels1->n = 10;
    p_psPixelsPrint(stdout, pixels, "pixels");
    p_psPixelsPrint(stdout, pixels1, "pixels1");

    psPixels* pixels2 = psPixelsConcatenate(NULL, pixels);
    p_psPixelsPrint(stdout, pixels2, "pixels2");
    if (pixels2 == NULL) {
        psError(PS_ERR_UNKNOWN, true,
                "pixels2 == NULL?");
        return 1;
    }
    if (pixels == pixels2) {
        psError(PS_ERR_UNKNOWN, true,
                "pixels == pixels2?  Should have made a copy.");
        return 2;
    }
    if (pixels2->n != pixels->n) {
        psError(PS_ERR_UNKNOWN, true,
                "pixels2->n != pixels->n");
        return 3;
    }
    for (long i = 0; i < pixels2->n; i++) {
        if (pixels2->data[i].x != pixels->data[i].x || pixels2->data[i].y != pixels->data[i].y) {
            psError(PS_ERR_UNKNOWN, true,
                    "failed to copy the values correctly at index %ld", i);
            return 4;
        }
    }

    // add pixels that are half unique.
    psPixels* pixels3 = psPixelsConcatenate(pixels2, pixels1);
    p_psPixelsPrint(stdout, pixels3, "pixels3");
    if (pixels3 != pixels2) {
        psError(PS_ERR_UNKNOWN, true,
                "return expected to be the out parameter.");
        return 10;
    }
    if (pixels3->n != 15) {
        psError(PS_ERR_UNKNOWN, true,
                "pixels3->n != 15, == %ld", pixels3->n);
        return 11;
    }

    // Attempt to concatenate with NULL pixels
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL pixels");
    if(psPixelsConcatenate(pixels2,NULL) != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not expect return to be non-NULL for NULL input pixels");
        return 12;
    }

    psFree(pixels3);
    psFree(pixels1);
    psFree(pixels);

    return 0;
}

psS32 testPixelsGetSet(void)
{
    psPixels *in = psPixelsAlloc(5);
    in->n = 0;
    psPixelCoord value;
    psPixelCoord out;
    value.x = 3;
    value.y = 13;
    if ( psPixelsSet(in, 2, value) ) {
        psError(PS_ERR_LOCATION_INVALID, true, "psPixelSet set to Invalid location\n");
        return 1;
    }
    if ( !psPixelsSet(in, 0, value) ) {
        psError(PS_ERR_UNKNOWN, true, "psPixelsSet failed to set pixels at location 0\n");
        return 2;
    }
    if ( !psPixelsSet(in, 1, value) ) {
        psError(PS_ERR_UNKNOWN, true, "psPixelsSet failed to set pixels at location 0\n");
        return 3;
    }
    out = psPixelsGet(in, 1);
    if ( out.x != 3 || out.y != 13 ) {
        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psPixelsGet return incorrect values %f,%f",
                out.x, out.y);
        return 4;
    }
    value.x = 1;
    value.y = 2;
    if ( !psPixelsSet(in, 0, value) ) {
        psError(PS_ERR_UNKNOWN, true, "psPixelsSet failed to set pixels at location 0\n");
        return 5;
    }
    out = psPixelsGet(in, 0);
    if (out.x != 1 || out.y != 2) {
        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "psPixelsGet return incorrect values %f,%f",
                out.x, out.y);
        return 6;
    }
    psFree(in);
    return 0;
}

psS32 testPixelsLength( void )
{
    psPixels *pixels = psPixelsAlloc(5);

    if (psPixelsLength(pixels) != 0) {
        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
                "psPixelsLength failed to return the correct length of pixels.\n");
        return 1;
    }
    pixels->n = 5;
    if (psPixelsLength(pixels) != 5) {
        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
                "psPixelsLength failed to return the correct length of pixels.\n");
        return 2;
    }
    pixels->n++;
    if (psPixelsLength(pixels) != 6) {
        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
                "psPixelsLength failed to return the correct length of pixels.\n");
        return 3;
    }
    psFree(pixels);

    psPixels *emptyPixels = NULL;
    psVector *vector = psVectorAlloc(5, PS_TYPE_F32);

    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
    if (psPixelsLength(emptyPixels) != -1) {
        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
                "psPixelsLength failed to return -1 for a NULL input pixels.\n");
        return 4;
    }
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
    if (psPixelsLength((psPixels*)vector) != -1) {
        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
                "psPixelsLength failed to return -1 for an invalid input pixels.\n");
        return 5;
    }
    psFree(vector);

    return 0;
}

