#include "acutest.h"
#include "vieter_tree_internal.h"

#define TEST_SIZE(tree, size) \
    TEST_CHECK(vieter_tree_size(tree) == size); \
    TEST_MSG("Size: %zu", vieter_tree_size(tree))

void test_init() {
    vieter_tree *tree = vieter_tree_init();
    TEST_CHECK(tree != NULL);
    TEST_SIZE(tree, 0);
    vieter_tree_free(tree);
}

void test_insert() {
    vieter_tree *tree = vieter_tree_init();

    for (uint64_t i = 0; i < 250; i++) {
        TEST_CHECK(vieter_tree_insert(tree, i, (void *)i) == vieter_tree_ok);
        TEST_SIZE(tree, i + 1);
        TEST_CHECK(vieter_tree_validate(tree));
    }

    vieter_tree_iterator *iter = vieter_tree_iterator_from(tree);

    void *out = NULL;

    for (uint64_t i = 0; i < 250; i++) {
        TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_ok);
        TEST_CHECK(out == (void *)i);
        TEST_CHECK(vieter_tree_insert(tree, i, NULL) == vieter_tree_already_present);
        TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_ok);

        out = NULL;

        TEST_CHECK(vieter_tree_iterator_next(&out, iter) == vieter_tree_ok);
        TEST_CHECK(out == (void *)i);
    }

    TEST_CHECK(vieter_tree_iterator_next(&out, iter) == vieter_tree_iterator_done);

    vieter_tree_iterator_free(&iter);
    vieter_tree_free(tree);
}

void test_remove() {
    vieter_tree *tree = vieter_tree_init();

    for (uint64_t i = 0; i < 250; i++) {
        TEST_CHECK(vieter_tree_insert(tree, i, NULL) == vieter_tree_ok);
        TEST_CHECK(vieter_tree_validate(tree));
    }

    void *out;

    for (uint64_t i = 0; i < 250; i++) {
        TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_ok);
        TEST_CHECK(vieter_tree_remove(&out, tree, i) == vieter_tree_ok);
        TEST_CHECK(vieter_tree_validate(tree));
        TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_not_present);
        TEST_SIZE(tree, 250 - i - 1);
    }

    vieter_tree_free(tree);
}

TEST_LIST = {
    {"tree init", test_init},
    {"tree insert", test_insert},
    {"tree remove", test_remove},
    {NULL, NULL}
};