#include <stdio.h>
#include <string.h>
#include <pslib.h>

#include "tap.h"
#include "pstap.h"

int main (void)
{
    plan_tests(143);

    diag("psVectorAlloc() tests");

    {
        psMemId id = psMemGetId();
        psVector *psVec = psVectorAlloc(5, PS_TYPE_S32);

        ok(psVec != NULL, "psVector successfully allocated");
        skip_start(psVec == NULL, 4, "Skipping 4 tests because psVectorAlloc() failed");
        ok(psVec->nalloc == 5, "Vector size = %ld", psVec->nalloc);
        ok(psVec->n == 0, "Vector population = %ld", psVec->n);
        ok(psVec->type.type == PS_TYPE_S32, "Vector type = %d", psVec->type.type);
        ok(psVec->type.dimen == PS_DIMEN_VECTOR, "Vector dimen = %d", psVec->type.dimen);
        skip_end();

        psFree(psVec);
        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    }

    {
        psVector* vecZero = psVectorAlloc(0, PS_TYPE_S32);

        ok(vecZero != NULL, "zero-length vector allocated");
        skip_start(vecZero == NULL, 2, "Skipping 2 tests because psVectorAlloc() failed");
        ok(vecZero->nalloc == 0, "Vector size = %ld", vecZero->nalloc);
        ok(vecZero->n == 0, "Vector population = %ld", vecZero->n);
        skip_end();

        psFree(vecZero);
    }

    {
        psVector* vecBogus = psVectorAlloc(10, 0);

        ok(vecBogus == NULL, "psVectorAlloc() doesn't not accept bogus type");
        psErr *err = psErrorLast();

        skip_start(err == NULL, 2, "Skipping 2 tests because psVectorAlloc() didn't fail as expect");
        ok(strstr(err->name, "psVectorAlloc"), "alloc failure - got error name %s", err->name);
        ok(err->code == PS_ERR_BAD_PARAMETER_TYPE, "alloc failure - got error code %d", err->code);
        skip_end();

        psFree(vecBogus);
    }


    diag("psVectorRealloc() tests");

    {
        // create new psVector
        psVector *psVec = psVectorAlloc(5, PS_TYPE_S32);

        // generate first part of vector
        for(psS32 i = 0; i < 5; i++) {
            psVec->data.S32[i] = i*10;
            psVec->n++;
        }

        // Test C - Reallocate S32 vector bigger
        psVec = psVectorRealloc(psVec,10);
        ok(psVec != NULL, "test vector reallocated to bigger size");
        skip_start(psVec == NULL, 4, "Skipping 4 tests because psVectorRealloc() failed");
        ok(psVec->nalloc == 10, "Vector size = %ld", psVec->nalloc);
        ok(psVec->n == 5, "Vector population = %ld", psVec->n);
        ok(psVec->type.type == PS_TYPE_S32, "Vector type = %d", psVec->type.type);
        ok(psVec->type.dimen == PS_DIMEN_VECTOR, "Vector dimen = %d", psVec->type.dimen);
        skip_end();

        // generate test data values
        for(psS32 i = 5; i < 10; i++) {
            psVec->data.S32[i] = i*10;
            psVec->n++;
        }

        // test data values
        skip_start(psVec == NULL, 10, "Skipping 10 tests because psVectorRealloc() failed");
        for(psS32 i = 0; i < 10; i++) {
            ok (psVec->data.S32[i] == i*10, "Elem %d = %d, expected %d", i, psVec->data.S32[i], i*10);
        }
        skip_end();

        psFree(psVec);
    }

    {
        // Test D - Reallocate S32 vector smaller
        psVector *psVec = psVectorAlloc(5, PS_TYPE_S32);
        psVec->n = 5;

        // generate test data values
        for(psS32 i = 0; i < 5; i++) {
            psVec->data.S32[i] = i*10;
            psVec->n++;
        }

        psVec = psVectorRealloc(psVec, 3);

        ok(psVec != NULL, "test vector reallocated to smaller size");
        skip_start(psVec == NULL, 5, "Skipping 6 tests because psVectorRealloc() failed");
        ok(psVec->nalloc == 3, "Vector size = %ld", psVec->nalloc);
        ok(psVec->n == 3, "Vector population = %ld", psVec->n);

        // check that the test data values survived the realloc
        for(psS32 i = 0; i < 3; i++) {
            ok (psVec->data.S32[i] == i*10, "Elem %d = %d, expected %d", i, psVec->data.S32[i], i*10);
        }
        skip_end();
    }

    {
        // reallocate to 0 length
        psVector *psVec = psVectorAlloc(5, PS_TYPE_S32);
        psVec = psVectorRealloc(psVec,0);

        ok(psVec != NULL, "test vector reallocated to zero length");
        skip_start(psVec == NULL, 2, "Skipping 2 tests because psVectorRealloc() failed");
        ok(psVec->nalloc == 0, "Vector size = %ld", psVec->nalloc);
        ok(psVec->n == 0, "Vector population = %ld", psVec->n);
        skip_end();

        // Test E - Free S32 vector
        // XXX not really a test...
        psFree(psVec);
    }

    {
        psVector *vecBogus = psVectorAlloc(5, PS_TYPE_S32);
        vecBogus = psVectorRealloc(NULL, 6);

        ok(vecBogus == NULL, "psVectorRealloc() doesn't not accept bogus type");
        psErr *err = psErrorLast();

        skip_start(err == NULL, 2, "Skipping 2 tests because psVectorRealloc() didn't fail as expect");
        ok(strstr(err->name, "psVectorRealloc"), "alloc failure - got error name %s", err->name);
        ok(err->code == PS_ERR_BAD_PARAMETER_NULL, "alloc failure - got error code %d", err->code);
        skip_end();

        psFree(vecBogus);
    }


    diag("psVectorExtend() tests");

    {
        // create new psVector
        psVector *psVec = psVectorAlloc(5, PS_TYPE_S32);
        psVec = psVectorExtend(psVec, 0, 2);

        ok(psVec != NULL, "test vector extended");
        skip_start(psVec == NULL, 2, "Skipping 2 tests because psVectorExtend() failed");
        ok(psVec->nalloc == 5, "Vector size = %ld", psVec->nalloc);
        ok(psVec->n == 2, "Vector population = %ld", psVec->n);
        skip_end();

        psFree(psVec);
    }

    {
        psVector *psVec = psVectorAlloc(5, PS_TYPE_S32);
        psVec = psVectorExtend(psVec, 0, 2);
        psVec = psVectorExtend(psVec, 0, 2);

        ok(psVec != NULL, "test vector extended");
        skip_start(psVec == NULL, 2, "Skipping 2 tests because psVectorExtend() failed");
        ok(psVec->nalloc == 15, "Vector size = %ld", psVec->nalloc);
        ok(psVec->n == 4,"Vector population = %ld", psVec->n);
        skip_end();

        psFree(psVec);
    }

    {
        psVector *psVec = psVectorAlloc(5, PS_TYPE_S32);
        psVec = psVectorExtend(psVec, 0, 2);
        psVec = psVectorExtend(psVec, 0, 2);
        psVec = psVectorExtend(psVec, 0, -2);

        ok(psVec != NULL, "test vector extended");
        skip_start(psVec == NULL, 2, "Skipping 2 tests because psVectorExtend() failed");
        ok(psVec->nalloc == 15 ,"Vector size = %ld", psVec->nalloc);
        ok(psVec->n == 2, "Vector population = %ld", psVec->n);
        skip_end();

        psFree(psVec);
    }

    {
        psVector *psVec = psVectorAlloc(5, PS_TYPE_S32);
        psVec = psVectorExtend(psVec, 0, 2);
        psVec = psVectorExtend(psVec, 0, 2);
        psVec = psVectorExtend(psVec, 0, -2);
        psVec = psVectorExtend(psVec, 0, -20);

        ok(psVec != NULL, "test vector extended");
        skip_start(psVec == NULL, 2, "Skipping 2 tests because psVectorExtend() failed");
        ok(psVec->nalloc == 15, "Vector size = %ld", psVec->nalloc);
        ok(psVec->n == 0, "Vector population = %ld", psVec->n);
        skip_end();

        psFree(psVec);
    }


    diag("psVectorInit() tests");

    {
        psVector *vec = psVectorAlloc(1, PS_TYPE_U8);
        ok(psVectorInit(vec, 1 ), "U8 Case");
        psFree(vec);
    }

    {
        psVector *vec = psVectorAlloc(1, PS_TYPE_U16);
        ok(!psVectorInit(vec, PS_MAX_U64), "VectorInit failed.  U16 Case");
        psFree(vec);
    }

    {
        psVector *vec = psVectorAlloc(1, PS_TYPE_U32);
        ok(psVectorInit(vec, 10), "U32 Case");
        psFree(vec);
    }

    {
        psVector *vec = psVectorAlloc(1, PS_TYPE_U64);
        ok(psVectorInit(vec, -1), "U64 Case");
        psFree(vec);
    }

    {
        psVector *vec = psVectorAlloc(1, PS_TYPE_S8);
        ok(!psVectorInit(vec, PS_MAX_S16), "VectorInit failed.  S8 Case");
        psFree(vec);
    }

    {
        psVector *vec = psVectorAlloc(1, PS_TYPE_S16);
        ok(psVectorInit(vec, -100), "S16 Case");
        psFree(vec);
    }

    {
        psVector *vec = psVectorAlloc(1, PS_TYPE_S32);
        ok(psVectorInit(vec, 1), "S32 Case");
        psFree(vec);
    }

    {
        // XXX is this test ok?
        psVector *vec = psVectorAlloc(1, PS_TYPE_S64);
        ok(psVectorInit(vec, PS_MAX_U64), "S64 Case");
        psFree(vec);
    }

    {
        psVector *vec = psVectorAlloc(1, PS_TYPE_F32);
        ok(psVectorInit(vec, 1.1), "F32 Case");
        psFree(vec);
    }

    {
        psVector *vec = psVectorAlloc(1, PS_TYPE_F64);
        ok(psVectorInit(vec, 1.4 ), "F64 Case");
        psFree(vec);
    }

    {
        psVector *vec = psVectorAlloc(1, PS_TYPE_C32);
        psC32 vC32 = 1.23 + 1.19I;
        ok(psVectorInit(vec, vC32), "C32 Case");
        psFree(vec);
    }

    {
        psVector *vec = psVectorAlloc(1, PS_TYPE_C64);
        psC64 vC64 = 2.13 + 2.31I;
        ok(psVectorInit(vec, vC64), "C64 Case");
        psFree(vec);
    }


    diag("psVectorCreate() tests");

    {
        psVector *test = psVectorCreate(NULL, 0.0, 10.0, 1.0, PS_TYPE_S32);
        for (int i = 0; i < 10; i++) {
            ok(test->data.S32[i] == i, "Vector data matches. i = %d, data=%d",
               i, test->data.S32[i]);
        }
        psFree(test);
    }

    {
        psVector *input = psVectorAlloc(5, PS_TYPE_F32);
        psVector *test = psVectorCreate(input, 0.0, 5.0, 0.5, PS_TYPE_F32);

        for (int i = 0; i < 10; i++) {
            ok(test->data.F32[i] == i * 0.5,
               "Vector data matches. i = %d, data=%f", i, test->data.F32[i]);
        }

        psFree(input);
    }

    //  Test PS_TYPE_F64
    //       PS_TYPE_S64
    {
        psVector *test = psVectorCreate(NULL, 0.0, 10.0, 1.0, PS_TYPE_S64);
        for (int i = 0; i < 10; i++)
        {
            ok(test->data.S64[i] == i,
               "Vector data does not match. i = %d, data=%ld",
               i, (long)test->data.S64[i]);
        }
        psFree(test);
    }

    {
        psVector *input = psVectorAlloc(5, PS_TYPE_F64);
        psVector *test = psVectorCreate(input, 0.0, 5.0, 0.5, PS_TYPE_F64);

        for (int i = 0; i < 10; i++)
        {
            ok(test->data.F64[i] == i * 0.5,
               "Vector data does not match. i = %d, data=%f",
               i, test->data.F64[i]);
        }
        psFree(input);
    }

    //  Test PS_TYPE_U16
    //       PS_TYPE_S16
    {
        psVector *test = psVectorCreate(NULL, 0.0, 10.0, 1.0, PS_TYPE_S16);
        for (int i = 0; i < 10; i++)
        {
            ok(test->data.S16[i] == i, "Vector data matches. i = %d, data=%d",
               i, test->data.S16[i]);
        }
        psFree(test);
    }

    {
        psVector *input = psVectorAlloc(5, PS_TYPE_U16);
        psVector *test = psVectorCreate(input, 0.0, 20.0, 2.0, PS_TYPE_U16);

        for (int i = 0; i < 10; i++)
        {
            ok(test->data.U16[i] == i * 2.0,
               "Vector data matches. i = %d, data=%d", i, test->data.U16[i]);
        }
        psFree(input);
    }


    diag("psVectorSet() tests");

    {
        psVector *vec = psVectorAlloc(5, PS_TYPE_S32);

        ok(psVectorSet(vec, 0, 10),
           "VectorSet set S32 at position 0");

        psFree(vec);
    }

    {
        psVector *vec = psVectorAlloc(5, PS_TYPE_S32);

        ok(!psVectorSet(vec, 10, 10),
           "VectorSet failes to set S32 at out of range position");

        psFree(vec);
    }

    {
        psVector *vec = psVectorAlloc(5, PS_TYPE_S32);

        ok(psVectorSet(vec, 1, 4) == true,
           "VectorSet set S32 at position 1");

        psFree(vec);
    }


    diag("psVectorGet() tests");

    {
        psVector *vec = psVectorAlloc(10, PS_TYPE_S32);
        psVectorSet(vec, 0, 10);

        ok((psS32)psVectorGet(vec, 0) == 10,
           "VectorGet returned the correct S32 from position 0");

        psFree(vec);
    }

    {
        psVector *vec = psVectorAlloc(5, PS_TYPE_S32);
        psVectorSet(vec, -1, 4);

        ok((psS32)psVectorGet(vec, -1) == 4,
           "VectorGet returned the correct S32 from tail using -1");

        psFree(vec);
    }


    diag("psVectorCountPixelMask() tests");

    {
        ok(psVectorCountPixelMask(NULL, 1) == -1,
           "psVectorCountPixelMask returned -1 for NULL Vector input");
    }

    {
        psVector *vec = psVectorAlloc(5, PS_TYPE_S32);
        vec->data.S32[0] = 0;
        vec->data.S32[1] = 1;
        vec->data.S32[2] = 0;
        vec->data.S32[3] = 1;
        vec->data.S32[4] = 0;
        vec->n = 5;

        ok(psVectorCountPixelMask(vec, 1) == -1,
           "returned -1 for wrong type of Vector input");

        psFree(vec);
    }

    {
        psVector *vec = psVectorAlloc(5, PS_TYPE_U8);
        vec->data.U8[0] = 0;
        vec->data.U8[1] = 1;
        vec->data.U8[2] = 0;
        vec->data.U8[3] = 1;
        vec->data.U8[4] = 0;
        vec->n = 5;
        long numPix = psVectorCountPixelMask(vec, 1);

        ok(numPix != -1, "returned -1 for correct Vector input");
        ok(numPix == 2, "returned pixel count %d", numPix);

        psFree(vec);
    }


    diag("psVectorLength() tests");

    {
        psVector *vector = psVectorAlloc(5, PS_TYPE_F32);

        ok(psVectorLength(vector) == 0,
           "returned the correct length of vector");

        psFree(vector);
    }

    {
        psVector *vector = psVectorAlloc(5, PS_TYPE_F32);
        vector->n = 5;

        ok(psVectorLength(vector) == 5,
           "returned the correct length of vector");

        psFree(vector);
    }

    {
        psVector *vector = psVectorAlloc(6, PS_TYPE_F32);
        vector->n = 6;

        ok(psVectorLength(vector) == 6,
           "returned the correct length of vector");

        psFree(vector);
    }

    {
        ok(psVectorLength(NULL) == -1, "returned -1 for a NULL input vector");
    }

    {
        psArray *array = psArrayAlloc(5);

        ok(psVectorLength((psVector*)array) == -1,
           "returned -1 for an invalid input vector");

        psFree(array);
    }


    diag("psVectorCopy() tests");

    {

        ok(psVectorCopy(NULL, NULL, PS_TYPE_F32) == NULL,
           "returned a NULL vector for NULL input");
    }

    {
        psVector *in = psVectorAlloc(5, PS_TYPE_F32);
        in->n = 5;

        for (int i = 0; i < 5; i++) {
            in->data.F32[i] = i;
        }

        //Try copy of different type
        psVector *copy = psVectorCopy(NULL, in, PS_TYPE_F64);
        ok(copy != NULL, "returned a non NULL vector for correct input");
        skip_start(copy == NULL, 1, "Skipping 1 test because psVectorCopy() failed");
        ok(copy->data.F64[2] == 2.0,
           "copy->data.f64[2] = %lf, in->data.f32[2] = %f",
           copy->data.F64[2], in->data.F32[2]);
        skip_end();

        psFree(in);
        psFree(copy);
    }

    // XXX psVectorRecycle needs it's own tests
    // copy = psVectorRecycle(copy, in->n + 2, PS_TYPE_F64);

    {
        // Try copy of same type and non-NULL outVector of different type and
        // size.
        psVector *in = psVectorAlloc(5, PS_TYPE_F32);
        in->n = 5;
        for (int i = 0; i < 5; i++)
        {
            in->data.F32[i] = i;
        }
        psVector *copy = psVectorAlloc(5, PS_TYPE_F64);
        copy = psVectorCopy(copy, in, in->type.type);

        ok(copy != NULL, "returned a NULL vector for correct input");
        skip_start(copy == NULL, 2,
                   "Skipping 1 test because psVectorCopy() failed");
        ok(copy->type.type == PS_TYPE_F32, "copy has the correct data type");
        ok(copy->data.F32[2] == 2.0, "copy has the correct data value");
        skip_end();

        psFree(in);
        psFree(copy);
    }

    return exit_status();
}
