#include <stdio.h>
#include <stdlib.h>
#include <check.h>

#include "libharkio3.h"

harkio_Config* goodConfig(void)
{
    harkio_Config *cfg = harkio_Config_new();

    harkio_Config_setSynchronousAverage(cfg, 16);
    harkio_Config_setTSPpath(cfg, "/home/tsp.wav");
    harkio_Config_setTSPoffset(cfg, 2);
    harkio_Config_setPeakSearchFrom(cfg, 0);
    harkio_Config_setPeakSearchTo(cfg, 100);
    harkio_Config_setNfft(cfg, 1024);
    harkio_Config_setComment(cfg, "Test file");
    harkio_Config_setSamplingRate(cfg, 16000);

    return cfg;
}


harkio_Neighbors* goodNeighbors(harkio_Positions *ps)
{
    harkio_Neighbors *nbrs = harkio_Neighbors_new(ps);
    harkio_Neighbors_calcNeighbors(nbrs, NearestNeighbor, 3, 1.5);
    return nbrs;
}

harkio_Positions* goodMicrophones(void)
{
    harkio_Positions *ps = harkio_Positions_new("microphone", -1, 0, NULL);
    float coord[][3] = {{0.01, 0.01, 0.01}, {0.02, 0.02, 0.02}, {0.03, 0.03, 0.03}};
    int i;

    for (i=0; i<3; i++) {
        harkio_Positions_append(ps, harkio_Position_new(i + 1, Cartesian, coord[i], ""));
    }
    return ps;
}

START_TEST(test_newdelete)
{
    fprintf(stderr, "START TEST test_newdelete\n");
    harkio_TransferFunction *tf = harkio_TransferFunction_new();
    ck_assert(tf->handle == NULL);

//    ck_assert(tf->cfg    != NULL);
//   ck_assert(tf->poses->size == 0);
//   ck_assert(tf->mics->size  == 0);
//    ck_assert(tf->nbrs->size  == 0);

    ck_assert(tf->loctfs == NULL);
    ck_assert(tf->septfs == NULL);

    harkio_TransferFunction_delete(&tf);
    ck_assert(tf == NULL);

    harkio_TransferFunction_delete(&tf);
    ck_assert(tf == NULL);

    harkio_TransferFunction_delete(NULL);
    fprintf(stderr, "END TEST test_newdelete\n");
}
END_TEST

START_TEST(test_setget)
{
    fprintf(stderr, "START TEST test_setget\n");
    harkio_Positions *ps = harkio_Positions_new(NULL, -1, 0, NULL);
    float coord[][3] = {{0.1,  0.1, 0.1},
        {0.15, 0.1, 0.1},
        {0.2,  0.2, 0.2}
    };
    int i;

    for (i=0; i<3; i++) {
        char *str = (char *) malloc(1024);
        sprintf(str, "/home/tsp%d.wav", i+1);
        harkio_Positions_append(ps, harkio_Position_new(i + 1, Cartesian, coord[i], str));
        free(str);
    }

    harkio_TransferFunction *tf = harkio_TransferFunction_new();

    harkio_TransferFunction_setConfig(tf, goodConfig());
    harkio_TransferFunction_setNeighbors(tf, goodNeighbors(ps));
    harkio_TransferFunction_setMicrophones(tf, goodMicrophones());
    harkio_TransferFunction_setPositions(tf, ps);

    harkio_Config *cfg1 = goodConfig();
    harkio_Neighbors *nbrs1 = goodNeighbors(ps);
    harkio_Positions *mics1 = goodMicrophones();

    ck_assert_int_eq(1, harkio_Config_isEqual(cfg1, harkio_TransferFunction_getConfig(tf)));
    fprintf(stderr, "??????????\n");
    ck_assert_int_eq(1, harkio_Neighbors_isEqual(nbrs1, harkio_TransferFunction_getNeighbors(tf)));
    fprintf(stderr, "??????????\n");
    ck_assert_int_eq(1, harkio_Positions_isEqual(mics1, harkio_TransferFunction_getMicrophones(tf)));
    fprintf(stderr, "??????????\n");
    ck_assert_int_eq(1, harkio_Positions_isEqual(ps, harkio_TransferFunction_getPositions(tf)));
    fprintf(stderr, "??????????\n");

    harkio_Config_delete(&cfg1);
    harkio_Neighbors_delete(&nbrs1);
    harkio_Positions_delete(&mics1);
    harkio_Config_delete(&tf->cfg);
    harkio_TransferFunction_delete(&tf);

    // NULL check
    ck_assert(NULL == harkio_TransferFunction_getNeighbors(NULL));
    ck_assert(NULL == harkio_TransferFunction_getConfig(NULL));
    fprintf(stderr, "??????????\n");
    ck_assert(NULL == harkio_TransferFunction_getMicrophones(NULL));
    ck_assert(NULL == harkio_TransferFunction_getPositions(NULL));
    fprintf(stderr, "??????????\n");
    fprintf(stderr, "END TEST test_setget\n");
}
END_TEST

START_TEST(test_setgetTF)
{
    fprintf(stderr, "START TEST test_setgetTF\n");
    harkio_Positions *ps = harkio_Positions_new(NULL, 0, 0, NULL);
    float coord[][3] = {{0.1,  0.1, 0.1},
        {0.15, 0.1, 0.1},
        {0.2,  0.2, 0.2}
    };
    int i;
    harkio_Matrix *ltfs[3];
    harkio_Matrix *stfs[3];

    for (i=0; i<3; i++) {
        char *str = (char *) malloc(1024);
        sprintf(str, "/home/tsp%d.wav", i+1);
        harkio_Positions_append(ps, harkio_Position_new(i + 1, Cartesian, coord[i], str));
        free(str);
        ltfs[i] = harkio_Matrix_eye("int32", i + 1);
        stfs[i] = harkio_Matrix_ones("int32", i + 1, i + 1);
    }

    harkio_TransferFunction *tf = harkio_TransferFunction_new();

    harkio_TransferFunction_setPositions(tf, ps);
    harkio_TransferFunction_setLocTFs(tf, ltfs);
    harkio_TransferFunction_setSepTFs(tf, stfs);

    ck_assert_int_eq(1, harkio_Positions_isEqual(ps, harkio_TransferFunction_getPositions(tf)));

    for (i=0; i<3; i++) {
        harkio_Matrix *tmpl = harkio_Matrix_eye("int32", i + 1);
        harkio_Matrix *tmps = harkio_Matrix_ones("int32", i + 1, i + 1);

        ck_assert(tmpl != NULL);
        ck_assert(tmps != NULL);

        ck_assert(harkio_Matrix_isEqual(harkio_TransferFunction_getTFById(tf, i + 1, loctf), tmpl) == EXIT_SUCCESS);
        ck_assert(harkio_Matrix_isEqual(harkio_TransferFunction_getTFById(tf, i + 1, septf), tmps) == EXIT_SUCCESS);
        harkio_Matrix_delete(&tmpl);
        harkio_Matrix_delete(&tmps);

        ck_assert(harkio_Matrix_isEqual(tmpl,
                                        harkio_TransferFunction_getTFByPos(tf, harkio_Positions_getById(ps, i), 0.1, loctf)));
        ck_assert(harkio_Matrix_isEqual(tmps,
                                        harkio_TransferFunction_getTFByPos(tf, harkio_Positions_getById(ps, i), 0.1, septf)));
    }

    ck_assert(NULL == harkio_TransferFunction_getTFByPos(NULL, NULL, 0, 0));

    fprintf(stderr, "deleting tf\n");
    harkio_TransferFunction_delete(&tf);
    fprintf(stderr, "assertion \n");
    ck_assert(tf == NULL);
    fprintf(stderr, "assertion done\n");

    fprintf(stderr, "END TEST test_setgetTF\n");
}
END_TEST

START_TEST(test_readwrite)
{
    fprintf(stderr, "START TEST test_readwrite\n");
    harkio_Positions *ps = harkio_Positions_new("tsp", -1, 0, NULL);


    float coord[][3] = {{0.1,  0.1, 0.1},
        {0.15, 0.1, 0.1},
        {0.2,  0.2, 0.2}
    };
    int i;
    int ret;
    char *signature = "HHHHHHHHHHHAAAAAAAAAAAARRRRRRRRRRRRRRRRKKKKKKKKKKK";

    harkio_Positions_append(ps, harkio_Position_new(0, Cartesian, coord[0], "/home/tsp1.wav"));
    harkio_Positions_append(ps, harkio_Position_new(1, Cartesian, coord[1], "/home/tsp2.wav"));
    harkio_Positions_append(ps, harkio_Position_new(2, Cartesian, coord[2], "/home/tsp3.wav"));


    harkio_Matrix *m1[3] = {harkio_Matrix_eye("int32", 5),
                            harkio_Matrix_eye("int32", 5),
                            harkio_Matrix_eye("int32", 5)
                           };
    harkio_Matrix *m2[3] = {harkio_Matrix_ones("int32", 5, 5),
                            harkio_Matrix_ones("int32", 5, 5),
                            harkio_Matrix_ones("int32", 5, 5)
                           };


    harkio_TransferFunction *tf = harkio_TransferFunction_new();
    harkio_TransferFunction *tf2 = NULL;

    harkio_TransferFunction_setConfig(tf, goodConfig());
    harkio_TransferFunction_setNeighbors(tf, goodNeighbors(ps));
    harkio_TransferFunction_setMicrophones(tf, goodMicrophones());
    harkio_TransferFunction_setPositions(tf, ps);
    harkio_TransferFunction_setLocTFs(tf, m1);
    harkio_TransferFunction_setSepTFs(tf, m2);

    ret = harkio_TransferFunction_write(NULL, NULL, NULL);
    ck_assert_int_eq(ret, EXIT_FAILURE);
    ret = harkio_TransferFunction_write(tf, NULL, NULL);
    ck_assert_int_eq(ret, EXIT_FAILURE);
    tf2 = harkio_TransferFunction_fromFile(NULL, NULL);
    ck_assert(tf2 == NULL);


    fprintf(stderr, "writing to test.zip ...\n");
    ret = harkio_TransferFunction_write(tf, "test.zip", NULL);
    ck_assert_int_eq(ret, EXIT_SUCCESS);

    fprintf(stderr, "writing to test2.zip with signature %s ...\n", signature);
    ret = harkio_TransferFunction_write(tf, "test2.zip", signature);
    ck_assert_int_eq(ret, EXIT_SUCCESS);


    fprintf(stderr, "reading from test.zip ...\n");
    tf2 = harkio_TransferFunction_fromFile("test.zip", NULL);
    ck_assert(tf2 != NULL);
    harkio_TransferFunction_delete(&tf2);

    fprintf(stderr, "reading from test2.zip with signature %s...\n", signature);
    tf2 = harkio_TransferFunction_fromFile("test2.zip", NULL);
    ck_assert(tf2 == NULL);
    tf2 = harkio_TransferFunction_fromFile("test2.zip", signature);
    ck_assert(tf2 != NULL);

    ck_assert_int_eq(1, harkio_Config_isEqual(harkio_TransferFunction_getConfig(tf2),
                     harkio_TransferFunction_getConfig(tf)));

    fprintf(stderr, "comparing nbrs in test_readwrite...\n");
    ck_assert_int_eq(1, harkio_Neighbors_isEqual(harkio_TransferFunction_getNeighbors(tf2),
                     harkio_TransferFunction_getNeighbors(tf)));

    fprintf(stderr, "comparing mics in test_readwrite...\n");
    ck_assert_int_eq(1, harkio_Positions_isEqual(harkio_TransferFunction_getMicrophones(tf2),
                     harkio_TransferFunction_getMicrophones(tf)));
    fprintf(stderr, "comparing poses in test_readwrite...\n");
    ck_assert_int_eq(1, harkio_Positions_isEqual(harkio_TransferFunction_getPositions(tf2),
                     harkio_TransferFunction_getPositions(tf)));


    fprintf(stderr, "loctfs / septfs check\n");
    for (i=0; i<tf->poses->size; i++) {
        harkio_Matrix *m1 = harkio_Matrix_eye("int32", 5);
        harkio_Matrix *m2 = harkio_Matrix_ones("int32", 5, 5);

        fprintf(stderr, "loctfs[%d]\n", i);
        harkio_Matrix_print(tf2->loctfs[i]);
        harkio_Matrix_print(m1);
        ck_assert_int_eq(EXIT_SUCCESS, harkio_Matrix_isEqual(tf2->loctfs[i], m1));
        fprintf(stderr, "sepfs[%d]\n", i);
        harkio_Matrix_print(tf2->septfs[i]);
        harkio_Matrix_print(m2);
        ck_assert_int_eq(EXIT_SUCCESS, harkio_Matrix_isEqual(tf2->septfs[i], m2));

        fprintf(stderr, "delete\n");
        harkio_Matrix_delete(&m1);
        harkio_Matrix_delete(&m2);
    }
    harkio_TransferFunction_delete(&tf2);
    harkio_TransferFunction_delete(&tf);

    fprintf(stderr, "error case: reading from kinect_loc.dat with signature %s...\n", signature);
    tf2 = harkio_TransferFunction_fromFile("../testdata/kinect_loc.dat", NULL);
    ck_assert(tf2 == NULL);

    fprintf(stderr, "END TEST test_readwrite\n");
}
END_TEST


START_TEST(test_compat)
{
    fprintf(stderr, "START TEST test_compat\n");
    char *locpath = "../testdata/kinect_loc.dat";
    char *seppath = "../testdata/kinect_sep.tff";

    char *fakepath = "NOT EXISTING FILE";

    harkio_TransferFunction *tf;

    // NULL
    tf = harkio_Compat_TransferFunction_readlibharkio2(NULL, NULL);
    ck_assert(tf == NULL);
    tf = harkio_Compat_SeparationMatrix_readlibharkio2(NULL, NULL);
    ck_assert(tf == NULL);

    // FILE error
    tf = harkio_Compat_TransferFunction_readlibharkio2(fakepath, NULL);
    ck_assert(tf == NULL);
    tf = harkio_Compat_TransferFunction_readlibharkio2(NULL, fakepath);
    ck_assert(tf == NULL);
    tf = harkio_Compat_TransferFunction_readlibharkio2(fakepath, fakepath);
    ck_assert(tf == NULL);
    tf = harkio_Compat_SeparationMatrix_readlibharkio2(fakepath, NULL);
    ck_assert(tf == NULL);
    tf = harkio_Compat_SeparationMatrix_readlibharkio2(NULL, fakepath);
    ck_assert(tf == NULL);
    tf = harkio_Compat_SeparationMatrix_readlibharkio2(fakepath, fakepath);
    ck_assert(tf == NULL);


    // locpath is given
    // tf = harkio_TransferFunction_new();
    tf = harkio_Compat_TransferFunction_readlibharkio2(locpath, NULL);
    ck_assert(tf != NULL);
    ck_assert(tf->mics->frame == -1);
    ck_assert(tf->poses->frame == -1);
    ck_assert(tf->cfg->TSPoffset == 0);
    ck_assert(tf->cfg->TSPLength == 16384);
    harkio_TransferFunction_delete(&tf);


    // seppath is given
    // tf = harkio_TransferFunction_new();
    tf = harkio_Compat_TransferFunction_readlibharkio2(NULL, seppath);
    ck_assert(tf != NULL);
    ck_assert(tf->mics->frame == -1);
    ck_assert(tf->poses->frame == -1);
    ck_assert(tf->cfg->TSPoffset == 0);
    ck_assert(tf->cfg->TSPLength == 16384);
    harkio_TransferFunction_delete(&tf);

    // both are given
    // tf = harkio_TransferFunction_new();
    tf = harkio_Compat_TransferFunction_readlibharkio2(locpath, seppath);
    ck_assert(tf != NULL);
    ck_assert(tf->mics->frame == -1);
    ck_assert(tf->poses->frame == -1);
    ck_assert(tf->cfg->TSPoffset == 0);
    ck_assert(tf->cfg->TSPLength == 16384);
    harkio_TransferFunction_delete(&tf);

    // convert separation matrix
    char *sm1path  = "../testdata/m.sm";
    char *sm2path  = "../testdata/s.zip";
    tf = harkio_Compat_SeparationMatrix_readlibharkio2(sm1path, sm2path);
    ck_assert(tf != NULL);
    harkio_TransferFunction_delete(&tf);

    fprintf(stderr, "END TEST test_compat\n");
}
END_TEST


Suite* str_suite(void)
{
    Suite *suite = suite_create("libharkio3_transferfunction");

    TCase *tcase1 = tcase_create("new / delete");
    tcase_add_test(tcase1, test_newdelete);
    suite_add_tcase(suite, tcase1);

    TCase *tcase2 = tcase_create("read / write");
    tcase_add_test(tcase2, test_readwrite);
    suite_add_tcase(suite, tcase2);

    TCase *tcase3 = tcase_create("setter / getter");
    tcase_add_test(tcase3, test_setget);
    tcase_add_test(tcase3, test_setgetTF);
    suite_add_tcase(suite, tcase3);

    TCase *tcase4 = tcase_create("utilities");
    tcase_add_test(tcase4, test_compat);
    // tcase_add_test(tcase4, test_compare);
    suite_add_tcase(suite, tcase4);

    return suite;
}

int main(int argc, char *argv[])
{
    harkio_Log_setLevel(harkio_Log_Debug);
    int number_failed;
    Suite *suite = str_suite();
    SRunner *runner = srunner_create(suite);
    srunner_run_all(runner, CK_NORMAL);
    number_failed = srunner_ntests_failed(runner);
    srunner_free(runner);
    return number_failed;
}
