#include <uhub.h>
#include <util/rbtree.h>

#define MAX_NODES 10000

static struct rb_tree* tree = NULL;

int test_tree_compare(const void* a, const void* b)
{
	return strcmp((const char*) a, (const char*) b);
}


EXO_TEST(rbtree_create_destroy, {
	int ok = 0;
	struct rb_tree* atree;
	atree = rb_tree_create(test_tree_compare, &hub_malloc, &hub_free);
	if (atree) ok = 1;
	rb_tree_destroy(atree);
	return ok;
});

EXO_TEST(rbtree_create_1, {
	tree = rb_tree_create(test_tree_compare, &hub_malloc, &hub_free);
	return tree != NULL;
});

EXO_TEST(rbtree_size_0, { return rb_tree_size(tree) == 0; });

EXO_TEST(rbtree_insert_1, {
	return rb_tree_insert(tree, "one", "1");
});

EXO_TEST(rbtree_insert_2, {
	return rb_tree_insert(tree, "two", "2");
});

EXO_TEST(rbtree_insert_3, {
	return rb_tree_insert(tree, "three", "3");
});

EXO_TEST(rbtree_insert_3_again, {
	return !rb_tree_insert(tree, "three", "3-again");
});

EXO_TEST(rbtree_size_1, { return rb_tree_size(tree) == 3; });

static int test_check_search(const char* key, const char* expect)
{
	const char* value = (const char*) rb_tree_get(tree, key);
	if (!value) return !expect;
	if (!expect) return 0;
	return strcmp(value, expect) == 0;
}

EXO_TEST(rbtree_search_1, { return test_check_search("one", "1"); });
EXO_TEST(rbtree_search_2, { return test_check_search("two", "2"); });
EXO_TEST(rbtree_search_3, { return test_check_search("three", "3"); });
EXO_TEST(rbtree_search_4, { return test_check_search("four", NULL); });


EXO_TEST(rbtree_remove_1, {
	return rb_tree_remove(tree, "one");
});

EXO_TEST(rbtree_size_2, { return rb_tree_size(tree) == 2; });

EXO_TEST(rbtree_remove_2, {
	return rb_tree_remove(tree, "two");
});

EXO_TEST(rbtree_remove_3, {
	return rb_tree_remove(tree, "three");
});

EXO_TEST(rbtree_remove_3_again, {
	return !rb_tree_remove(tree, "three");
});

EXO_TEST(rbtree_search_5, { return test_check_search("one", NULL); });
EXO_TEST(rbtree_search_6, { return test_check_search("two", NULL); });
EXO_TEST(rbtree_search_7, { return test_check_search("three", NULL); });
EXO_TEST(rbtree_search_8, { return test_check_search("four", NULL); });

EXO_TEST(rbtree_size_3, { return rb_tree_size(tree) == 0; });

EXO_TEST(rbtree_insert_10000, {
	int i;
	for (i = 0; i < MAX_NODES ; i++)
	{
		const char* key = strdup(uhub_itoa(i));
		const char* val = strdup(uhub_itoa(i + 16384));
		if (!rb_tree_insert(tree, key, val))
			return 0;
	}
	return 1;
});

EXO_TEST(rbtree_size_4, { return rb_tree_size(tree) == MAX_NODES ; });

EXO_TEST(rbtree_check_10000, {
	int i;
	for (i = 0; i < MAX_NODES ; i++)
	{
		char* key = strdup(uhub_itoa(i));
		const char* expect = uhub_itoa(i + 16384);
		if (!test_check_search(key, expect))
			return 0;
		hub_free(key);
	}
	return 1;
});

EXO_TEST(rbtree_iterate_10000, {
	int i = 0;
	struct rb_node* n = (struct rb_node*) rb_tree_first(tree);
	while (n)
	{
		struct rb_node* p = n;
		n = (struct rb_node*) rb_tree_next(tree);
		i++;
	}
	return i == MAX_NODES ;
});


static void free_node(struct rb_node* n)
{
	hub_free((void*) n->key);
	hub_free((void*) n->value);
}

EXO_TEST(rbtree_remove_5000, {
	int i = 0;
	struct rb_node* n = (struct rb_node*) rb_tree_first(tree);
	for (i = 0; i < MAX_NODES ; i += 2)
	{
		const char* key = uhub_itoa(i);
		rb_tree_remove_node(tree, key, &free_node);
	}
	return 1;
});


