/** @file  tst_psCoord.c
*
*  @brief The code will ...
*
*
*  @author GLG, MHPCC
*
*  @version $Revision: 1.12 $ $Name: not supported by cvs2svn $
*  @date $Date: 2006-02-28 02:53:03 $
*
*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
*/

#include "psTest.h"
#include "pslib_strict.h"
static psS32 testPlaneTransformAlloc(void);
static psS32 testPlaneDistortAlloc(void);
static psS32 testPlaneAlloc(void);
static psS32 testPlaneTransformApply(void);
static psS32 testPlaneDistortApply(void);
static psS32 testPixelsTransform(void);

testDescription tests[] = {
                              {testPlaneTransformAlloc, 826, "psPlaneTransformAlloc()", 0, false},
                              {testPlaneDistortAlloc, 827, "psPlaneDistortAlloc()", 0, false},
                              {testPlaneAlloc, -1, "psPlaneAlloc()", 0, false},
                              {testPlaneTransformApply, 831, "psPlaneTransformApply()", 0, false},
                              {testPlaneDistortApply, 832, "psPlaneDistortApply()", 0, false},
                              {testPixelsTransform, 833, "psPixelsTransform()", 0, false},
                              {NULL}
                          };

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

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

#define ORDER_X 2
#define ORDER_Y 3
#define ORDER_Z 4
#define ORDER_T 5

psS32 testPlaneAlloc( void )
{
    // Allocate psPlane.
    psPlane *myP = psPlaneAlloc();

    // Verify returned value is not NULL
    if(myP == NULL) {
        psError(PS_ERR_UNKNOWN, true, "psPlaneAlloc() returned NULL not expected.");
        return 1;
    }

    // Verify returned transform has members set properly
    if (!isnan(myP->x)) {
        psError(PS_ERR_UNKNOWN,true,"myP->x is %d, should be %d", myP->x, NAN);
        return 2;
    }

    if (!isnan(myP->y)) {
        psError(PS_ERR_UNKNOWN,true,"myP->y is %d, should be %d", myP->y, NAN);
        return 3;
    }

    if (!isnan(myP->xErr)) {
        psError(PS_ERR_UNKNOWN,true,"myP->xErr is %d, should be %d", myP->xErr, NAN);
        return 4;
    }

    if (!isnan(myP->yErr)) {
        psError(PS_ERR_UNKNOWN,true,"myP->yErr is %d, should be %d", myP->yErr, NAN);
        return 5;
    }

    // Free psPlane
    psFree(myP);

    return 0;
}

psS32 testPlaneTransformAlloc( void )
{
    // Allocate psPlaneTransform with known x and y terms
    psPlaneTransform *myPT = psPlaneTransformAlloc(ORDER_X, ORDER_Y);

    // Verify returned value is not NULL
    if(myPT == NULL) {
        psError(PS_ERR_UNKNOWN,true,"Function returned NULL not expected.");
        return 1;
    }

    // Verify returned transform has members set properly
    if (myPT->x->nX != ORDER_X) {
        psError(PS_ERR_UNKNOWN,true,"myPT->x->nX is %d, should be %d",
                myPT->x->nX, ORDER_X);
        return 2;
    }
    if (myPT->y->nX != ORDER_X) {
        psError(PS_ERR_UNKNOWN,true,"myPT->y->nX is %d, should be %d",
                myPT->y->nX, ORDER_X);
        return 3;
    }
    if (myPT->x->nY != ORDER_Y) {
        psError(PS_ERR_UNKNOWN,true,"myPT->x->nY is %d, should be %d",
                myPT->x->nY, ORDER_Y);
        return 4;
    }
    if (myPT->y->nY != ORDER_Y) {
        psError(PS_ERR_UNKNOWN,true,"myPT->y->nY is %d, should be %d",
                myPT->y->nY, ORDER_Y);
        return 5;
    }

    // Free plane transform
    psFree(myPT);

    // Attempt to specify negative x order and verify NULL returned and errror message generated
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message: negative x order");
    myPT = psPlaneTransformAlloc(-1, 1);
    if (myPT != NULL) {
        psError(PS_ERR_UNKNOWN,true,"psPlaneTransformAlloc() did not return NULL.");
        return 6;
    }

    // Attempt to specify negative y order and verify NULL returned and error message generated
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message: negative y order");
    myPT = psPlaneTransformAlloc(1, -1);
    if (myPT != NULL) {
        psError(PS_ERR_UNKNOWN,true,"psPlaneTransformAlloc() did not return NULL.");
        return 7;
    }

    psFree(myPT);

    return 0;
}


psS32 testPlaneDistortAlloc( void )
{
    // Invoke function with known parameters
    psPlaneDistort *myPD = psPlaneDistortAlloc(ORDER_X, ORDER_Y, ORDER_Z, ORDER_T);

    // Verify NULL is not returned
    if(myPD == NULL) {
        psError(PS_ERR_UNKNOWN,true,"Return of NULL not expected");
        return 1;
    }
    // Verify the terms are properly set after allocation
    if (myPD->x->nX != ORDER_X) {
        psError(PS_ERR_UNKNOWN,true,"myPD->x->nX is %d, should be %d",
                myPD->x->nX, ORDER_X);
        return 2;
    }
    if (myPD->y->nX != ORDER_X) {
        psError(PS_ERR_UNKNOWN,true,"myPD->y->nX is %d, should be %d",
                myPD->y->nX, ORDER_X);
        return 3;
    }
    if (myPD->x->nY != ORDER_Y) {
        psError(PS_ERR_UNKNOWN,true,"myPD->x->nY is %d, should be %d",
                myPD->x->nY, ORDER_Y);
        return 4;
    }
    if (myPD->y->nY != ORDER_Y) {
        psError(PS_ERR_UNKNOWN,true,"myPD->y->nY is %d, should be %d",
                myPD->y->nY, ORDER_Y);
        return 5;
    }
    if (myPD->x->nZ != ORDER_Z) {
        psError(PS_ERR_UNKNOWN,true,"myPD->x->nZ is %d, should be %d",
                myPD->x->nZ, ORDER_Z);
        return 6;
    }
    if (myPD->y->nZ != ORDER_Z) {
        psError(PS_ERR_UNKNOWN,true,"myPD->y->nZ is %d, should be %d",
                myPD->y->nZ, ORDER_Z);
        return 7;
    }
    if (myPD->x->nT != ORDER_T) {
        psError(PS_ERR_UNKNOWN,true,"myPD->x->nT is %d, should be %d",
                myPD->x->nT, ORDER_T);
        return 8;
    }
    if (myPD->y->nT != ORDER_T) {
        psError(PS_ERR_UNKNOWN,true,"myPD->y->nT is %d, should be %d",
                myPD->y->nT, ORDER_T);
        return 9;
    }
    // Free psPlaneTransform
    psFree(myPD);

    // Invoke function with negative x order parameter
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for negative x order");
    myPD = psPlaneDistortAlloc(-1, 1, 1, 1);
    if (myPD != NULL) {
        psError(PS_ERR_UNKNOWN,true,"psPlaneDistortAlloc() did not return NULL.");
        return 10;
    }

    // Invoke function with negative y order parameter
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for negative y order");
    myPD = psPlaneDistortAlloc(1, -1, 1, 1);
    if (myPD != NULL) {
        psError(PS_ERR_UNKNOWN,true,"psPlaneDistortAlloc() did not return NULL.");
        return 11;
    }

    // Invoke function with negative z order parameter
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for negative z order");
    myPD = psPlaneDistortAlloc(1, 1, -1, 1);
    if (myPD != NULL) {
        psError(PS_ERR_UNKNOWN,true,"psPlaneDistortAlloc() did not return NULL.");
        return 12;
    }

    // Invoke function with negative t order parameter
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for negative w order");
    myPD = psPlaneDistortAlloc(1, 1, 1, -1);
    if (myPD != NULL) {
        psError(PS_ERR_UNKNOWN,true,"psPlaneDistortAlloc() did not return NULL.");
    }

    return 0;
}

#define N 10
// We do a simple identity transformation on a few x,y pairs.
psS32 testPlaneTransformApply( void )
{
    psPlane*          out = NULL;
    psPolynomial2D*  tmp2DPoly = NULL;
    psPlane*          rc;

    // Create input coordinate
    psPlane* in = psPlaneAlloc();
    if(in == NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL return from psPlaneAlloc");
        return 1;
    }

    // Create transform
    psPlaneTransform* pt = psPlaneTransformAlloc(2,2);

    // Set transform coefficients so the x coord input will equal x coord output, same for y
    pt->x->coeff[1][0] = 1.0;
    pt->y->coeff[0][1] = 1.0;

    // Apply transform for several points
    for (psS32 i = 0; i < N; i++) {
        in->x = (psF64) i;
        in->y = (psF64) (i + 5.0);
        in->xErr = 0.0;
        in->yErr = 0.0;

        // Verify function does not return NULL
        out = psPlaneTransformApply(out, pt, in);
        if( out  == NULL) {
            psError(PS_ERR_UNKNOWN,true,"Expected non-NULL return value");
            return 1;
        }
        // Verify the expected values are returned
        if (FLT_EPSILON < fabs(out->x - in->x)) {
            psError(PS_ERR_UNKNOWN,true,"out.x is %lf, should be %lf", out->x, in->x);
            return 3;
        }
        if (FLT_EPSILON < fabs(out->y - in->y)) {
            psError(PS_ERR_UNKNOWN,true,"out.y is %lf, should be %lf", out->y, in->y);
            return 4;
        }
    }
    psFree(out);

    // Verify return null and error message generater for null specified transform
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL psPlaneTransform");
    rc = psPlaneTransformApply(NULL, NULL, in);
    if (rc != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Function did not return NULL as expected for NULL psPlaneTransform.");
        return 5;
    }

    // Verify return null and error message generated for null psPlaneTransform x coeff
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL x coeff psPlaneTransform");
    tmp2DPoly = pt->x;
    pt->x = NULL;
    rc = psPlaneTransformApply(NULL, pt, in);
    if (rc != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Function did not return NULL as expected for NULL x coeff psPlaneTransform");
        return 6;
    }
    pt->x = tmp2DPoly;

    // Verify return null and error message generated for null psPlaneTransform y coeff
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL y coeff psPlaneTransform");
    tmp2DPoly = pt->y;
    pt->y = NULL;
    rc = psPlaneTransformApply(NULL, pt, in);
    if (rc != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Function did not return NULL as expected for NULL y coeff psPlaneTransform");
        return 7;
    }
    pt->y = tmp2DPoly;

    // Verify return null and error message generated for null psPlane coordinate
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL psPlane");
    rc = psPlaneTransformApply(NULL, pt, NULL);
    if (rc != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Function did not return NULL as expected for NULL psPlane");
        return 8;
    }

    psFree(pt);
    psFree(in);

    return 0;
}

#define COLOR 1.0
#define MAGNITUDE 1.0
// We do a simple identity transformation on a few x,y pairs.  For x and y,
// we add in the COLOR and MAGNITUDE.
psS32 testPlaneDistortApply( void )
{
    psPlane*         out       = NULL;
    psPlane*         rc        = NULL;
    psPolynomial4D* tmp4DPoly = NULL;

    // Allocate input coordinate
    psPlane* in = psPlaneAlloc();

    // Create psPlaneTransform structure
    psPlaneDistort*  pt = psPlaneDistortAlloc(2, 2, 2, 2);

    pt->x->coeff[1][0][0][0] = 1.0;
    pt->x->coeff[0][0][1][0] = 1.0;
    pt->x->coeff[0][0][0][1] = 1.0;

    pt->y->coeff[0][1][0][0] = 1.0;
    pt->y->coeff[0][0][1][0] = 1.0;
    pt->y->coeff[0][0][0][1] = 1.0;

    for (psS32 i = 0; i < N; i++) {
        in->x = (psF64) i;
        in->y = (psF64) (i + 5.0);
        in->xErr = 0.0;
        in->yErr = 0.0;
        out = psPlaneDistortApply(out, pt, in, COLOR, MAGNITUDE);

        // Verify return value is not NULL
        if(out == NULL) {
            psError(PS_ERR_UNKNOWN,true,"Expected non-NULL return");
            return 1;
        }
        // Verify return value contains expected values
        if (FLT_EPSILON < fabs(out->x - COLOR - MAGNITUDE - in->x)) {
            psError(PS_ERR_UNKNOWN,true,"out->x is %lf, should be %lf",
                    out->x, in->x + COLOR + MAGNITUDE);
            return 2;
        }
        if (FLT_EPSILON < fabs(out->y - COLOR - MAGNITUDE - in->y)) {
            psError(PS_ERR_UNKNOWN,true,"out->y is %lf, should be %lf",
                    out->y, in->y + COLOR + MAGNITUDE);
            return 3;
        }
    }
    psFree(out);

    // Attempt to invoke psPlaneDistortApply with NULL psPlaneDistort
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for null psPlaneDistort");
    rc = psPlaneDistortApply(NULL, NULL, in, COLOR, MAGNITUDE);
    if (rc != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL.");
        return 4;
    }

    // Attempt to invoke psPlaneDistortApply with NULL psPlaneDistort x member
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for null x member psPlaneDistort");
    tmp4DPoly = pt->x;
    pt->x = NULL;
    rc = psPlaneDistortApply(NULL, pt, in, COLOR, MAGNITUDE);
    if (rc != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL.");
        return 5;
    }
    pt->x = tmp4DPoly;

    // Attempt to invoke psPlaneDistortApply with NULL psPlaneDistort y member
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for null y member psPlaneDistort");
    tmp4DPoly = pt->y;
    pt->y = NULL;
    rc = psPlaneDistortApply(NULL, pt, in, COLOR, MAGNITUDE);
    if (rc != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL.");
        return 6;
    }
    pt->y = tmp4DPoly;

    // Attempt to invoke psPlaneDistortApply with NULL input psPlane
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for null input psPlane");
    rc = psPlaneDistortApply(NULL, pt, NULL, COLOR, MAGNITUDE);
    if (rc != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL");
        return 7;
    }

    psFree(pt);
    psFree(in);

    return 0;
}

psS32 testPixelsTransform(void)
{
    psPixels *input = NULL;
    psPixels *output = NULL;
    psPlaneTransform *trans = psPlaneTransformAlloc(1, 3);

    //Return NULL for NULL input pixels
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
    output = psPixelsTransform(output, input, trans);
    if (output != NULL) {
        psError(PS_ERR_BAD_PARAMETER_NULL, false,
                "psPixelsTransform failed to return NULL for NULL input pixels.\n");
        return 1;
    }
    //Return NULL for NULL input PlaneTransform
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
    output = psPixelsTransform(output, input, NULL);
    if (output != NULL) {
        psError(PS_ERR_BAD_PARAMETER_NULL, false,
                "psPixelsTransform failed to return NULL for NULL psPlaneTransform.\n");
        return 2;
    }

    input = psPixelsAlloc(2);
    input->n = 2;
    input->data[0].x = 1.0;
    input->data[0].y = 1.0;
    input->data[1].x = 1.0;
    input->data[1].y = 6.0;
    trans->x->coeff[0][0] = 0;
    trans->x->coeff[1][0] = 1.0;
    trans->y->coeff[0][0] = 0;
    trans->y->coeff[0][0] = 0;
    trans->y->coeff[0][2] = 0.5;

    //Verify that the output pixels are what we expected
    output = psPixelsTransform(output, input, trans);
    int nExpected = 9;
    if (output->n != nExpected) {
        psError(PS_ERR_BAD_PARAMETER_SIZE, false,
                "psPixelsTransform failed to return the expected number of pixels.\n");
        printf("\n output returned with %ld pixels\n\n", output->n);
        for (int i = 0; i < output->n; i++) {
            printf("  (%6.2lf, %6.2lf) pixel %d\n", output->data[i].x, output->data[i].y, i+1);
        }
        return 3;
    }

    psFree(trans);
    psFree(input);
    psFree(output);

    return 0;
}

