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

#include "libharkio3.h"

START_TEST(test_new)
{

    harkio_XML *xml = harkio_XML_newFromXML("../../tests/tsp.xml", NULL);
    harkio_Config *cfg;
    ck_assert(xml != NULL);
    ck_assert_int_eq(xml->poses->size, 5);
    ck_assert_int_eq(xml->nbrs->size, 5);

    // <config>
    //     <SynchronousAverage>16</SynchronousAverage>
    //     <TSPpath>/usr/bin/harktool4_utils/16384.little_endian.wav</TSPpath>
    //     <TSPOffset>0</TSPOffset>
    //     <PeakSearch from="0" to="100"/>
    //     <nfft>1024</nfft>
    // </config>

    cfg = harkio_XML_getConfig(xml);
    ck_assert_int_eq(harkio_Config_getSynchronousAverage(cfg),
                     16);
    ck_assert_int_eq(harkio_Config_getTSPoffset(cfg),
                     0);
    ck_assert_int_eq(harkio_Config_getPeakSearchFrom(cfg),
                     0);
    ck_assert_int_eq(harkio_Config_getPeakSearchTo(cfg),
                     100);
    ck_assert_int_eq(harkio_Config_getNfft(cfg),
                     1024);
    ck_assert_int_eq(strcmp(harkio_Config_getTSPpath(cfg), "/usr/bin/harktool4_utils/16384.little_endian.wav"), 0);

    harkio_XML_delete(&xml);

    xml = harkio_XML_newFromXML("dummy", NULL);
    ck_assert(xml == NULL);
    xml = harkio_XML_newFromXML("CMakeLists.txt", NULL); // not xml file
    ck_assert(xml == NULL);
}
END_TEST

START_TEST(test_newFromData)
{
    int i, ret;
    harkio_XML *xml;
    harkio_Positions *ps = harkio_Positions_new("tsp", 1, 0, NULL);
    float coords[][3] = {{0, 0, 0}, {0, 0, 1}, {0, 0, 3}, {0, 0, 3.5}, {0, 0, 10.95}, {0, 0, 11}};
    for (i=0; i<6; i++) {
        harkio_Positions_append(ps, harkio_Position_new(i, Cartesian, coords[i], "/home"));
    }
    harkio_Neighbors *nbrs = harkio_Neighbors_new(ps);
    ret = harkio_Neighbors_calcNeighbors(nbrs, NearestNeighbor, 3, 1.5);
    ck_assert_int_eq(ret, EXIT_SUCCESS);

    xml = harkio_XML_newFromData(ps, nbrs, NULL);
    ck_assert(NULL != xml);

    // harkio_XML_write(xml, stderr);

    harkio_XML_delete(&xml);
}
END_TEST

START_TEST(test_newFromBuffer)
{
    int i=0;
    harkio_XML *xml;
    char buffer1[65535];
    char *buffer2;
    FILE *fp;

    // load file
    fp = fopen("../../tests/tsp.xml", "r");
    while ((buffer1[i++] = fgetc(fp)) != EOF);
    buffer1[i-1] = '\0';
    fclose(fp);

    // from buffer success
    xml = harkio_XML_newFromXMLbuffer(buffer1, strlen(buffer1), NULL);
    ck_assert(NULL != xml);

    // to buffer success
    buffer2 = harkio_XML_writeBuffer(xml);
    fprintf(stderr, "--%s--%s--", buffer1, buffer2);
    ck_assert_int_eq(strcmp(buffer1, buffer2), 0);
    harkio_XML_delete(&xml);
    free(buffer2);

    // from buffer fail
    buffer1[0] = 'x';
    xml = harkio_XML_newFromXMLbuffer(buffer1, strlen(buffer1), NULL);
    ck_assert(NULL == xml);
    harkio_XML_delete(&xml);

    // harkio_XML_write(xml, stderr);

}
END_TEST

START_TEST(test_delete)
{
    harkio_XML *xml = harkio_XML_newFromXML("../../tests/tsp.xml", NULL);
    harkio_XML_delete(&xml);
    ck_assert(xml == NULL);
    harkio_XML_delete(&xml);
    ck_assert(xml == NULL);
    harkio_XML_delete(NULL);
}
END_TEST

START_TEST(test_write)
{
    int ret;
    char buffer[4096] = "";
    FILE *fp;
    harkio_XML *xml = harkio_XML_newFromXML("../../tests/tsp.xml", NULL);

    // ret = harkio_XML_write(xml, stderr);
    // ck_assert_int_eq(ret, EXIT_SUCCESS);

    fp = fopen("test_write.xml", "w");
    ret = harkio_XML_write(xml, fp);
    ck_assert_int_eq(ret, EXIT_SUCCESS);

    ret = harkio_XML_write(xml, NULL);
    ck_assert_int_eq(ret, EXIT_FAILURE);
    harkio_XML_delete(&xml);
    fclose(fp);

    // diff
    if ((fp = popen("diff ../../tests/tsp.xml test_write.xml", "r")) == NULL) {
        ck_abort_msg("diff command failed");
    }
    int diffout = 0;
    do {
        fgets(buffer, 4096, fp);
        if (diffout == 0 && strlen(buffer) > 0)
            diffout = 1;
        fprintf(stderr, "%s", buffer);
    } while (!feof(fp));
    ck_assert(diffout == 0);

    system("echo ----- cat test_write.xml ------");
    system("cat test_write.xml");
    system("echo -------------------------------");


//system( "cat ../../tests/tsp.xml");

    pclose(fp);
//    ck_assert_msg(strcmp(buffer, "") == 0, "input and output xmls are not equal");

    // check for convert -0.0000 to 0.0000
#define POSITION_SIZE 14
    float coord[POSITION_SIZE][3] = {
            { 0.0,  0.0,  0.0},
            {-0.0, +0.0, +0.0},
            {+0.0, -0.0, +0.0},
            {+0.0, +0.0, -0.0},
            {-0.0, -0.0, +0.0},
            {-0.0, +0.0, -0.0},
            {+0.0, -0.0, -0.0},
            {-0.0, -0.0, -0.0},
            {-0.00004999999, -0.00004999999, -0.00004999999},
            {-0.00005000000, -0.00005000000, -0.00005000000},
            {-0.00009999999, -0.00009999999, -0.00009999999},
            { 0.00004999999,  0.00004999999,  0.00004999999},
            { 0.00005000000,  0.00005000000,  0.00005000000},
            { 0.00009999999,  0.00009999999,  0.00009999999}
    };
    harkio_Position pos[POSITION_SIZE];
    harkio_Position* ppos[POSITION_SIZE];
    int i, j;
    for (i = 0; i < POSITION_SIZE; i++) {
        pos[i].id = i;
        pos[i].sys = Cartesian;
        pos[i].coord = coord[i];
        pos[i].path = NULL;
        pos[i].data = NULL;
        pos[i].channelsUse = 0;
        pos[i].numChannels = 0;
        pos[i].channels = 0;
        ppos[i] = &pos[i];
    }
    harkio_Positions* poses = harkio_Positions_new("impulse", -1, POSITION_SIZE, ppos);
    xml = harkio_XML_newFromData(poses, NULL, NULL);
    fp = fopen("test_write_zero.xml", "w");
    ret = harkio_XML_write(xml, fp);
    ck_assert_int_eq(ret, EXIT_SUCCESS);

    ret = harkio_XML_write(xml, NULL);
    ck_assert_int_eq(ret, EXIT_FAILURE);
    harkio_XML_delete(&xml);
    fclose(fp);

    xml = harkio_XML_newFromXML("test_write_zero.xml", NULL);
    float minus_zero = -0.0f;
    for (i = 0; i < xml->poses->size; i++) {
        for (j = 0; j < 3; j++) {
            if (memcmp(&xml->poses->pos[i]->coord[j], &minus_zero, sizeof(float)) == 0) {
                ck_abort_msg("output position incorrect. pos[%d][%d] input=%f,%f,%f, output=%f,%f,%f", i, j, coord[i][0],
                        coord[i][1], coord[i][2], xml->poses->pos[i]->coord[0], xml->poses->pos[i]->coord[1], xml->poses->pos[i]->coord[2]);
            }
        }
    }
    harkio_XML_delete(&xml);

}
END_TEST

START_TEST(test_writePositions_HeadTail)
{
    int ret;
    char buffer[4096] = "";
    FILE *fp;
    // harkio_XML *xml = harkio_XML_newFromXML("../../tests/tsp.xml", NULL);

    float pos[3] = {0.0, 1.0, 2.0};
    char* buf = "tsp";
    char* pathbuf = "/path/to/tsp.wav";
    harkio_Position* position_array[1];
    harkio_Position* position = harkio_Position_new(0, Cartesian, pos, pathbuf);
    if (!position) {
        ck_abort_msg("cannot create hark position instance.");
    }

    position_array[0] = position;
    harkio_Positions* positions = harkio_Positions_new(buf, 0, 1, position_array);

    if (!positions) {
        ck_abort_msg("cannot create hark positions instance.");
    }

    fp = fopen("test_write.xml", "w");
    ret = harkio_XML_writeHead(fp);
    ck_assert_int_eq(ret, EXIT_SUCCESS);
    ret = harkio_XML_writePositions(positions, fp);
    ck_assert_int_eq(ret, EXIT_SUCCESS);
    ret = harkio_XML_writeTail(fp);
    ck_assert_int_eq(ret, EXIT_SUCCESS);

    harkio_Position_delete(&position);
    harkio_Positions_delete(&positions);
    fclose(fp);

    // diff
    if ((fp = popen("diff ../../tests/write_position.xml test_write.xml", "r")) == NULL) {
        ck_abort_msg("diff command failed");
    }
    int diffout = 0;
    do {
        fgets(buffer, 4096, fp);
        if (diffout == 0 && strlen(buffer) > 0)
            diffout = 1;
        fprintf(stderr, "%s", buffer);
    } while (!feof(fp));
    ck_assert(diffout == 0);

    // system("test write positions.");
    system("echo ----- cat test_write.xml ------");
    system("cat test_write.xml");
    system("echo -------------------------------");


//system( "cat ../../tests/tsp.xml");


    pclose(fp);
//    ck_assert_msg(strcmp(buffer, "") == 0, "input and output xmls are not equal");
}

END_TEST

START_TEST(test_setgetPositions)
{
    harkio_XML *xml = harkio_XML_newFromXML("../../tests/tsp.xml", NULL);
    harkio_Positions *ps;

    ps = harkio_XML_getPositions(xml);
    ck_assert_int_eq(ps->size, 5);
    harkio_XML_delete(&xml);
}
END_TEST

START_TEST(test_setgetNeighbors)
{
    harkio_XML *xml = harkio_XML_newFromXML("../../tests/tsp.xml", NULL);
    harkio_Neighbors *nbrs;

    nbrs = harkio_XML_getNeighbors(xml);
    ck_assert_int_eq(nbrs->size, 5);
    harkio_XML_delete(&xml);
}
END_TEST

START_TEST(test_setgetData)
{
    harkio_XML *xml = harkio_XML_new();
    ck_assert(xml->poses == NULL);
    ck_assert(xml->nbrs  == NULL);
    ck_assert(xml->cfg   == NULL);
    harkio_XML_delete(&xml);
    ck_assert(xml == NULL);
}
END_TEST

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

    TCase *tcase1 = tcase_create("new/delete");
    tcase_add_test(tcase1, test_new);
    tcase_add_test(tcase1, test_newFromData);
    tcase_add_test(tcase1, test_newFromBuffer);
    tcase_add_test(tcase1, test_delete);
    suite_add_tcase(suite, tcase1);

    TCase *tcase2 = tcase_create("output");
    tcase_add_test(tcase2, test_write);
    tcase_add_test(tcase2, test_writePositions_HeadTail);
    suite_add_tcase(suite, tcase2);

    TCase *tcase3 = tcase_create("setter / getter");
    tcase_add_test(tcase3, test_setgetPositions);
    tcase_add_test(tcase3, test_setgetNeighbors);
    tcase_add_test(tcase3, test_setgetData);
    suite_add_tcase(suite, tcase3);

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

    return suite;
}

int main(int argc, char *argv[])
{
    int number_failed;
    Suite *suite = str_suite();
    SRunner *runner = srunner_create(suite);
    srunner_set_xml(runner, "test.xml");
    srunner_run_all(runner, CK_NORMAL);
    number_failed = srunner_ntests_failed(runner);
    srunner_free(runner);
    return number_failed;
}
