#include <gtest/gtest.h>

#include "interval_tree.h"

struct interval_tree_node *node_new(uint64_t start, uint64_t last, void *user_data)
{
    uint64_t size = last - start + 1;
    struct interval_tree_node *node = (struct interval_tree_node *)calloc(1, sizeof(struct interval_tree_node) + size);
    if (node == nullptr)
    {
        return nullptr;
    }
    node->start = start;
    node->last = last;
    node->user_data = (char *)node + sizeof(struct interval_tree_node);
    memcpy(node->user_data, user_data, size);
    printf("new node: %p, start: %lu, last: %lu, user_data: %s\n", node, node->start, node->last, (char *)node->user_data);

    return node;
}

void node_free(struct interval_tree_node *node)
{
    if (node)
    {
        printf("free node: %p, start: %lu, last: %lu, user_data: %s\n", node, node->start, node->last, (char *)node->user_data);
        free(node);
        node = NULL;
    }
}

struct range
{
    uint64_t start;
    uint64_t last;
};

#if 1
TEST(INTERVAL_TREE, FIND)
{
    struct rb_root_cached root = RB_ROOT_CACHED;
    struct interval_tree_node *node;

    // insert
    node = node_new(5, 10, (void *)"Hello");
    interval_tree_insert(node, &root);

    // not found
    struct range ranges_not_found[] = {
        {0, 4},
        {11, 15},
    };
    for (size_t i = 0; i < sizeof(ranges_not_found) / sizeof(ranges_not_found[0]); i++)
    {
        node = interval_tree_iter_first(&root, ranges_not_found[i].start, ranges_not_found[i].last);
        EXPECT_TRUE(node == nullptr);
    }

    // found
    struct range ranges_found[] = {
        // overlap interval
        {0, 5},
        {1, 6},
        {2, 7},
        {3, 8},
        {4, 9},
        {5, 10},
        {6, 11},
        {7, 12},
        {8, 13},
        {9, 14},
        {10, 15},
        // overlap point
        {5, 5},
        {6, 6},
        {7, 7},
        {8, 8},
        {9, 9},
        {10, 10},
        // overlap interval
        {5, 10},
        {4, 11},
    };
    for (size_t i = 0; i < sizeof(ranges_found) / sizeof(ranges_found[0]); i++)
    {
        node = interval_tree_iter_first(&root, ranges_found[i].start, ranges_found[i].last);
        EXPECT_TRUE(node != nullptr);
        EXPECT_TRUE(node->start == 5);
        EXPECT_TRUE(node->last == 10);
        EXPECT_STREQ((const char *)node->user_data, "Hello");
    }

    // remove
    interval_tree_remove(node, &root);
    node_free(node);

    // find
    node = interval_tree_iter_first(&root, 5, 10);
    EXPECT_TRUE(node == nullptr);
}
#endif

#if 1
TEST(INTERVAL_TREE, REPEAT)
{
    struct rb_root_cached root = RB_ROOT_CACHED;
    struct interval_tree_node *node;

    // insert
    node = node_new(5, 10, (void *)"Hello");
    interval_tree_insert(node, &root);

    // insert repeat
    node = node_new(5, 10, (void *)"World");
    interval_tree_insert(node, &root);

    node = interval_tree_iter_first(&root, 5, 10);
    EXPECT_TRUE(node != nullptr);
    EXPECT_TRUE(node->start == 5);
    EXPECT_TRUE(node->last == 10);
    EXPECT_STREQ((const char *)node->user_data, "Hello");

    node = interval_tree_iter_next(node, 5, 10);
    EXPECT_TRUE(node != nullptr);
    EXPECT_TRUE(node->start == 5);
    EXPECT_TRUE(node->last == 10);
    EXPECT_STREQ((const char *)node->user_data, "World");

    node = interval_tree_iter_next(node, 5, 10);
    EXPECT_TRUE(node == nullptr);

    // remove all
    while ((node = interval_tree_iter_first(&root, 5, 10)) != nullptr)
    {
        interval_tree_remove(node, &root);
        node_free(node);
    }
}
#endif

#if 1
TEST(INTERVAL_TREE, OVERLAP)
{
    struct rb_root_cached root = RB_ROOT_CACHED;
    struct interval_tree_node *node;

    // insert
    node = node_new(5, 10, (void *)"Hello");
    interval_tree_insert(node, &root);

    // insert repeat
    node = node_new(7, 12, (void *)"World");
    interval_tree_insert(node, &root);

    node = interval_tree_iter_first(&root, 5, 10);
    EXPECT_TRUE(node != nullptr);
    EXPECT_TRUE(node->start == 5);
    EXPECT_TRUE(node->last == 10);
    EXPECT_STREQ((const char *)node->user_data, "Hello");

    node = interval_tree_iter_next(node, 5, 10);
    EXPECT_TRUE(node != nullptr);
    EXPECT_TRUE(node->start == 7);
    EXPECT_TRUE(node->last == 12);
    EXPECT_STREQ((const char *)node->user_data, "World");

    node = interval_tree_iter_next(node, 5, 10);
    EXPECT_TRUE(node == nullptr);

    // remove all
    while ((node = interval_tree_iter_first(&root, 5, 10)) != nullptr)
    {
        interval_tree_remove(node, &root);
        node_free(node);
    }
}
#endif

int main(int argc, char **argv)
{
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
