#include <linux/netlink.h>
#include <stdio.h>
#include <time.h>
#include <check.h>

#include "netlink-private/types.h"
#include "netlink/route/cls/ematch.h"
#include "cksuite-all.h"
#include "netlink-private/nl-auto.h"

#define MAX_DEPTH 6
#define MAX_CHILDREN 5

static int current_depth = 0;
static int id = 1;
static long long array_size = 0;

static long long my_pow(long long x, long long y)
{
	int ret = x;

	if (y == 0)
		return 1;

	if (y < 0 || x == 0)
		return 0;

	while (--y) {
		ret *= x;
	}

	return ret;
}

static long int generate_random(long int max)
{
	srandom(time(NULL) + id);
	return (random() % max);
}

static int build_children(struct nl_list_head *parent)
{
	int i, num = 0;
	struct rtnl_ematch *child = NULL;

	if (!parent)
		return 0;

	if (++current_depth > MAX_DEPTH) {
		--current_depth;
		return 0;
	}

	num = generate_random(MAX_CHILDREN + 1);
	for (i = 0; i < num; ++i) {
		child = rtnl_ematch_alloc();
		if (!child) {
			printf("Mem alloc error\n");
			exit(1);
		}
		build_children(&child->e_childs);
		child->e_id = id++;
		nl_list_add_tail(&child->e_list, parent);
	}

	--current_depth;
	return 0;
}

static void build_src_cgroup(struct rtnl_ematch_tree *tree)
{
	build_children(&tree->et_list);
}

static void dump_ematch_list(struct nl_list_head *head, int *result, int *index)
{
	struct rtnl_ematch *pos = NULL;

	nl_list_for_each_entry (pos, head, e_list) {
		if (!nl_list_empty(&pos->e_childs))
			dump_ematch_list(&pos->e_childs, result, index);
		result[*index] = pos->e_id;
		(*index)++;
	}
}

static void dump_ematch_tree(struct rtnl_ematch_tree *tree, int *result,
			     int *index)
{
	if (!tree)
		return;

	dump_ematch_list(&tree->et_list, result, index);
}

static int compare(int *r1, int *r2, int len)
{
	int i = 0;
	for (i = 0; i < len; ++i) {
		if (r1[i] != r2[i])
			return -1;
	}
	return 0;
}

START_TEST(ematch_tree_clone)
{
	_nl_auto_rtnl_ematch_tree struct rtnl_ematch_tree *src = NULL;
	_nl_auto_rtnl_ematch_tree struct rtnl_ematch_tree *dst = NULL;
	_nl_auto_free int *src_result = NULL;
	_nl_auto_free int *dst_result = NULL;
	int i = 0;
	int j = 0;

	array_size = (MAX_DEPTH * my_pow(MAX_CHILDREN, MAX_DEPTH)) / 2;
	src_result = calloc(4, array_size);
	dst_result = calloc(4, array_size);

	src = rtnl_ematch_tree_alloc(2);

	build_src_cgroup(src);
	dump_ematch_tree(src, src_result, &i);

	dst = rtnl_ematch_tree_clone(src);
	dump_ematch_tree(dst, dst_result, &j);

	ck_assert(dst);
	ck_assert(i == j);
	ck_assert(!compare(src_result, dst_result, i));
}
END_TEST

Suite *make_nl_ematch_tree_clone_suite(void)
{
	Suite *suite = suite_create("Clone ematch tree");
	TCase *tc = tcase_create("Core");

	tcase_add_test(tc, ematch_tree_clone);
	suite_add_tcase(suite, tc);

	return suite;
}
