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

#include "libharkio3.h"

#define EPS 0.00001

START_TEST(test_new)
{
    harkio_Position *p;
    float coord[3] = {0.1, 0.2, 0.3};

    // normal case
    p = harkio_Position_new(1, Cartesian, coord, "/home");
    ck_assert_int_eq(p->id, 1);
    ck_assert_int_eq(p->sys, Cartesian);
    ck_assert(fabs(0.1 - p->coord[0]) < EPS);
    ck_assert(fabs(0.2 - p->coord[1]) < EPS);
    ck_assert(fabs(0.3 - p->coord[2]) < EPS);
    ck_assert_str_eq(p->path, "/home");
    harkio_Position_delete(&p);

    p = harkio_Position_new(1, Polar, coord, "/home");
    ck_assert_int_eq(p->sys, Polar);
    harkio_Position_delete(&p);

    // wrong type
    // wrong size

}
END_TEST

START_TEST(test_delete)
{
    float coord[3] = {0.1, 0.2, 0.3};
    harkio_Position *p = harkio_Position_new(1, Cartesian, coord, "/home");
    harkio_Position_delete(&p);
    ck_assert(p == NULL);
    harkio_Position_delete(&p);
    ck_assert(p == NULL);
//    harkio_Position_delete(NULL);
}
END_TEST

START_TEST(test_setgetPosition)
{
    float coord[3] = {0.1, 0.2, 0.3};
    float coord2[3] = {0.3, 0.4, 0.5};
    float *result;
    int id = 1;
    int numChannels = 5;
    int channels[5] = {1, 2, 3, 4, 5};
    int channelsUse = 0;
    harkio_Position *p = harkio_Position_new(id, Cartesian, coord, "/home");

    result = harkio_Position_getPosition(p);
    ck_assert_msg(fabs(result[0] - coord[0]) < EPS, "getPosition failed coord[0]");
    ck_assert_msg(fabs(result[1] - coord[1]) < EPS, "getPosition failed coord[1]");
    ck_assert_msg(fabs(result[2] - coord[2]) < EPS, "getPosition failed coord[2]");

    harkio_Position_setPosition(p, Cartesian, coord2);
    result = harkio_Position_getPosition(p);
    ck_assert_msg(fabs(result[0] - coord2[0]) < EPS, "setPosition failed coord2[0]");
    ck_assert_msg(fabs(result[1] - coord2[1]) < EPS, "setPosition failed coord2[1]");
    ck_assert_msg(fabs(result[2] - coord2[2]) < EPS, "setPosition failed coord2[2]");

    ck_assert(1 == harkio_Position_getID(p));
    ck_assert(Cartesian == harkio_Position_getSys(p));

    ck_assert(EXIT_SUCCESS == harkio_Position_setChannels(p, channels, numChannels));
    ck_assert(EXIT_SUCCESS == harkio_Position_setChannels(p, NULL, numChannels));
    ck_assert(EXIT_SUCCESS == harkio_Position_setNumChannels(p, numChannels));
    ck_assert(EXIT_SUCCESS == harkio_Position_setChannelsUse(p, channelsUse));

    harkio_Position_delete(&p);

    // fail tests
    ck_assert(EXIT_FAILURE == harkio_Position_setPosition(NULL, Cartesian, coord2));
    ck_assert(EXIT_FAILURE == harkio_Position_setChannels(NULL, channels, numChannels));
    ck_assert(EXIT_FAILURE == harkio_Position_setNumChannels(NULL, numChannels));
    ck_assert(EXIT_FAILURE == harkio_Position_setChannelsUse(NULL, channelsUse));

    ck_assert(NULL == harkio_Position_getPosition(NULL));
    ck_assert(-1 == harkio_Position_getID(NULL));
    ck_assert(-1 == harkio_Position_getSys(NULL));

}
END_TEST

START_TEST(test_compare)
{
    float coord1[3] = {0.1, 0.2, 0.3};
    float coord2[3] = {0.5, 0.6, 0.7};
    harkio_Position *a = harkio_Position_new(1, Cartesian, coord1, "/home");
    harkio_Position *b = harkio_Position_new(1, Cartesian, coord1, "/home");
    harkio_Position *c = harkio_Position_new(2, Cartesian, coord1, "/home");
    harkio_Position *d = harkio_Position_new(1, Polar,     coord1, "/home");
    harkio_Position *e = harkio_Position_new(1, Cartesian, coord2, "/home");
    harkio_Position *f = harkio_Position_new(1, Cartesian, coord2, "/home/elsewhere");
    harkio_Position *g = harkio_Position_new(1, Cartesian, coord2, NULL);

    harkio_Position *dd = harkio_Position_new(1, Polar,     coord1, "/home");


    ck_assert_int_eq(harkio_Position_isEqual(NULL, NULL), 0);
    ck_assert_int_eq(harkio_Position_isEqual(a, b), 1);
    ck_assert_int_eq(harkio_Position_isEqual(a, c), 0);
    ck_assert_int_eq(harkio_Position_isEqual(a, d), 0);
    ck_assert_int_eq(harkio_Position_isEqual(a, e), 0);
    ck_assert_int_eq(harkio_Position_isEqual(a, f), 0);
    ck_assert_int_eq(harkio_Position_isEqual(a, g), 0);
    ck_assert_int_eq(harkio_Position_isEqual(a, NULL), 0);

    ck_assert_int_eq(harkio_Position_isEqual(b, a), 1);
    ck_assert_int_eq(harkio_Position_isEqual(c, a), 0);
    ck_assert_int_eq(harkio_Position_isEqual(d, a), 0);
    ck_assert_int_eq(harkio_Position_isEqual(e, a), 0);
    ck_assert_int_eq(harkio_Position_isEqual(f, a), 0);
    ck_assert_int_eq(harkio_Position_isEqual(g, a), 0);
    ck_assert_int_eq(harkio_Position_isEqual(NULL, a), 0);

    ck_assert_int_eq(harkio_Position_isEqual(d, dd), 1);

    harkio_Position_delete(&a);
    harkio_Position_delete(&b);
    harkio_Position_delete(&c);
    harkio_Position_delete(&d);
    harkio_Position_delete(&e);
    harkio_Position_delete(&f);
    harkio_Position_delete(&g);
    harkio_Position_delete(&dd);

}
END_TEST

START_TEST(test_misc)
{
    fprintf(stderr, "in test_misc\n");
    float coord[3] = {0, 1, 2};
    harkio_Position *p = harkio_Position_new(1, Cartesian, coord, NULL);
    harkio_Position_print(p);
    harkio_Position_print(NULL);
    harkio_Position_setChannelsUse(p, 1);
    harkio_Position_setNumChannels(p, 3);
    int channels[] = {1,2,3};
    harkio_Position_setChannels(p, channels, 3);
    harkio_Position *p2 = harkio_Position_copy(p);
    harkio_Position_print(p2);
    ck_assert_int_eq(p->channelsUse, p2->channelsUse);
    ck_assert_int_eq(p->numChannels, p2->numChannels);
    ck_assert_int_eq(p->channels[0], p2->channels[0]);
    ck_assert_int_eq(p->channels[1], p2->channels[1]);
    ck_assert_int_eq(p->channels[2], p2->channels[2]);
    harkio_Position_delete(&p);
    harkio_Position_delete(&p2);
    fprintf(stderr, "out test_misc\n");
}
END_TEST

Suite* newdel_suite(void)
{
    Suite *suite = suite_create("libharkio3_position");
    TCase *tcase1 = tcase_create("new/delete");
    tcase_add_test(tcase1, test_new);
    tcase_add_test(tcase1, test_delete);
    suite_add_tcase(suite, tcase1);
    return suite;
}

Suite* setget_suite(void)
{
    Suite *suite = suite_create("libharkio3_position");
    TCase *tcase3 = tcase_create("setter / getter");
    tcase_add_test(tcase3, test_setgetPosition);
    suite_add_tcase(suite, tcase3);
    return suite;
}

Suite* util_suite(void)
{
    Suite *suite = suite_create("libharkio3_position");
    TCase *tcase4 = tcase_create("utilities");
    tcase_add_test(tcase4, test_compare);
    suite_add_tcase(suite, tcase4);

    return suite;
}
Suite* misc_suite(void)
{
    Suite *suite = suite_create("libharkio3_position");
    TCase *tcase5 = tcase_create("misc APIs");
    tcase_add_test(tcase5, test_misc);
    suite_add_tcase(suite, tcase5);

    return suite;
}
int main(int argc, char *argv[])
{
    int number_failed = 0;
    Suite *suite;
    SRunner *runner;

    suite = newdel_suite();
    runner = srunner_create(suite);
    srunner_run_all(runner, CK_NORMAL);
    number_failed += srunner_ntests_failed(runner);
    srunner_free(runner);

    suite = setget_suite();
    runner = srunner_create(suite);
    srunner_run_all(runner, CK_NORMAL);
    number_failed += srunner_ntests_failed(runner);
    srunner_free(runner);

    suite = util_suite();
    runner = srunner_create(suite);
    srunner_run_all(runner, CK_NORMAL);
    number_failed += srunner_ntests_failed(runner);
    srunner_free(runner);

    suite = misc_suite();
    runner = srunner_create(suite);
    srunner_run_all(runner, CK_NORMAL);
    number_failed += srunner_ntests_failed(runner);
    srunner_free(runner);

    return number_failed;
}
