/** @file  tst_psImage.c
 *
 *  @brief Contains the tests for psImage.[ch]
 *
 *
 *  @author Robert DeSonia, MHPCC
 *
 *  @version $Revision: 1.34 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2005-05-19 05:03:48 $
 *
 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
 */

#include <math.h>
#include <float.h>
#include <string.h>
#include <stdlib.h>

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

static psS32 testImageAlloc(void);
static psS32 testRegion(void);

testDescription tests[] = {
                              {testImageAlloc,546,"psImageAlloc",0,false},
                              {testImageAlloc,548,"psImageFree",0,true},
                              {testRegion,790,"psRegionSet",0,false},
                              {testRegion,791,"psRegionFromString",0,true},
                              {NULL}
                          };

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

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

psS32 testImageAlloc(void)
{
    psImage* image = NULL;
    psU32 sizes = 6;
    psU32 numCols[] = {
                          0,1,1,100,100,150
                      };
    psU32 numRows[] = {
                          0,1,100,1,150,100
                      };
    psU32 types = 12;
    psElemType type[] = { PS_TYPE_S8, PS_TYPE_S16, PS_TYPE_S32, PS_TYPE_S64,
                          PS_TYPE_U8, PS_TYPE_U16, PS_TYPE_U32, PS_TYPE_U64,
                          PS_TYPE_F32, PS_TYPE_F64, PS_TYPE_C32, PS_TYPE_C64};

    psLogMsg(__func__,PS_LOG_INFO,"#546 - psImageAlloc shall allocate memory for a psImage structure");

    for (psU32 t=0;t<types;t++) {
        psLogMsg(__func__,PS_LOG_INFO,"Testing psImage with type %xh",type[t]);

        for (psU32 i=0;i<sizes;i++) {

            if (numRows[i] == 0 || numCols[i] == 0) {
                psLogMsg(__func__,PS_LOG_INFO,"Following should be an error.");
            }

            image = psImageAlloc(numCols[i],numRows[i],type[t]);

            if (image == NULL) {
                if (numRows[i] == 0 || numCols[i] == 0) {
                    continue;
                }
                psError(PS_ERR_UNKNOWN, true,"psImageAlloc returned NULL for type %x, size %dx%d.",
                        type[t], numCols[i], numRows[i]);
                psFree(image);
                return 1;
            }

            if (image->type.dimen != PS_DIMEN_IMAGE || image->type.type != type[t]) {
                psError(PS_ERR_UNKNOWN, true,"psImageAlloc allocated wrong dimen/type (%d/%d), should be "
                        "PS_IMAGE_DIMEN/%d", image->type.dimen, image->type.type, type[t]);
                psFree(image);
                return 2;
            }

            if (image->numCols != numCols[i] || image->numRows != numRows[i]) {
                psError(PS_ERR_UNKNOWN, true,"psImageAlloc allocated wrong size %dx%d, should be %dx%d (type = %d)",
                        image->numCols, image->numRows, numCols[i], numRows[i],type[t]);
                psFree(image);
                return 3;
            }

            if (image->col0 != 0 || image->row0 != 0) {
                psError(PS_ERR_UNKNOWN, true,"psImageAlloc returned row0/col0 of %d/%d.  Should be 0/0.",
                        image->row0, image->row0);
                psFree(image);
                return 4;
            }

            if (image->parent != NULL) {
                psError(PS_ERR_UNKNOWN, true,"psImageAlloc returned non-NULL parent");
                psFree(image);
                return 5;
            }

            if (image->children != NULL) {
                psError(PS_ERR_UNKNOWN, true,"psImageAlloc returned non-NULL children array");
                psFree(image);
                return 7;
            }

            switch (type[t]) {
            case PS_TYPE_U16: {
                    psU32 rows = numRows[i];
                    psU32 cols = numCols[i];

                    for (psS32 r=0;r<rows;r++) {
                        for (psS32 c=0;c<cols;c++) {
                            image->data.U16[r][c] = 2*c+r;
                        }
                    }
                    for (psS32 r=0;r<rows;r++) {
                        for (psS32 c=0;c<cols;c++) {
                            if (image->data.U16[r][c] != 2*c+r) {
                                psError(PS_ERR_UNKNOWN, true,"Could not set all pixels in uint16 image at (%d,%d)",c,r);
                                psFree(image);
                                return 8;
                            }
                        }
                    }
                }
                break;
            case PS_TYPE_F32: {
                    psU32 rows = numRows[i];
                    psU32 cols = numCols[i];

                    for (psS32 r=0;r<rows;r++) {
                        for (psS32 c=0;c<cols;c++) {
                            image->data.F32[r][c] = 2.0f*c+r;
                        }
                    }
                    for (psS32 r=0;r<rows;r++) {
                        for (psS32 c=0;c<cols;c++) {
                            if (fabsf(image->data.F32[r][c] - (2.0f*c+r)) > FLT_EPSILON) {
                                psError(PS_ERR_UNKNOWN, true,"Could not set all pixels in float image at (%d,%d)",c,r);
                                psFree(image);
                                return 8;
                            }
                        }
                    }
                }
                break;
            case PS_TYPE_F64: {
                    psU32 rows = numRows[i];
                    psU32 cols = numCols[i];

                    for (psS32 r=0;r<rows;r++) {
                        for (psS32 c=0;c<cols;c++) {
                            image->data.F64[r][c] = 2.0f*c+r;
                        }
                    }
                    for (psS32 r=0;r<rows;r++) {
                        for (psS32 c=0;c<cols;c++) {
                            if (fabs(image->data.F64[r][c] - (2.0f*c+r)) > DBL_EPSILON) {
                                psError(PS_ERR_UNKNOWN, true,"Could not set all pixels in double image at (%d,%d)",c,r);
                                psFree(image);
                                return 8;
                            }
                        }
                    }
                }
                break;
            case PS_TYPE_C32: {
                    psU32 rows = numRows[i];
                    psU32 cols = numCols[i];

                    for (psS32 r=0;r<rows;r++) {
                        for (psS32 c=0;c<cols;c++) {
                            image->data.C32[r][c] = r + I * c;
                        }
                    }
                    for (psS32 r=0;r<rows;r++) {
                        for (psS32 c=0;c<cols;c++) {
                            if (fabsf(crealf(image->data.C32[r][c]) - r) > FLT_EPSILON ||
                                    fabsf(cimagf(image->data.C32[r][c]) - c) > FLT_EPSILON ) {
                                psError(PS_ERR_UNKNOWN, true,"Could not set all pixels in complex image at (%d,%d)",c,r);
                                psFree(image);
                                return 8;
                            }
                        }
                    }
                }
                break;
            default: {
                    // ignore type and just use as byte bucket.
                    psU32 rows = numRows[i];
                    psU32 cols = numCols[i]*PSELEMTYPE_SIZEOF(type[t]);

                    for (psS32 r=0;r<rows;r++) {
                        for (psS32 c=0;c<cols;c++) {
                            image->data.U8[r][c] = (uint8_t)(r + c);
                        }
                    }
                    for (psS32 r=0;r<rows;r++) {
                        for (psS32 c=0;c<cols;c++) {
                            if (image->data.U8[r][c] != (uint8_t)(r + c)) {
                                psError(PS_ERR_UNKNOWN, true,"Could not set all pixels in image (type=%d) at (%d,%d)",
                                        type[t],c,r);
                                psFree(image);
                                return 8;
                            }
                        }
                    }
                }
            }
            psFree(image);
        }
    }

    // #548: Verify no memory leaks or corruption are detected after a valid psImage structure with multiple
    // children is freed.
    image = psImageAlloc(100,100,PS_TYPE_F32);
    psImageSubset(image,(psRegion) {
                      50,0,70,20
                  }
                 );
    psImageSubset(image,(psRegion) {
                      70,20,90,40
                  }
                 );

    psFree(image);

    return 0;
}

static psS32 testRegion(void)
{
    int testNum = 0;


    // Testpoint #790

    psRegion region = psRegionSet(1,2,3,4);

    testNum++;
    if (region.x0 != 1 || region.x1 != 2 || region.y0 != 3 || region.y1 != 4) {
        psError(PS_ERR_UNKNOWN, false,
                "The region attributes are not set properly (%s)",
                psRegionToString(region));
        return testNum;
    }

    // Testpoint #791

    region = psRegionFromString("[1:2,3:4]");

    testNum++;
    if (region.x0 != 1 || region.x1 != 2 || region.y0 != 3 || region.y1 != 4) {
        psError(PS_ERR_UNKNOWN, false,
                "The region attributes are not set properly (%s)",
                psRegionToString(region));
        return testNum;
    }

    region = psRegionFromString("[1:2,3:]");

    testNum++;
    if (! isnan(region.x0)) {
        psError(PS_ERR_UNKNOWN, false,
                "psRegionFromString returned a non-NULL pointer given a malformed string.");
        return testNum;
    }

    return 0;
}
