/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */
/* Copyright (c) 2024 Brett Sheffield <bacs@librecast.net> */

#include "test.h"
#include <librecast_pvt.h>
#include <librecast/router.h>

#define ROUTERS 7

/* next_pow2 - Public Domain, credit to Sean Anderson from
 * https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 */
static uint32_t next_pow2(uint32_t v)
{
	v--;
	v |= v >> 1;
	v |= v >> 2;
	v |= v >> 4;
	v |= v >> 8;
	v |= v >> 16;
	return ++v;
}

int main(void)
{
	lc_ctx_t *lctx;
	lc_router_t *r[ROUTERS];
	int rc;

	test_name("lc_router_net() - LC_TOPO_STAR");

	lctx = lc_ctx_new();
	if (!test_assert(lctx != NULL, "lc_ctx_new()")) return test_status;

	/* create a star topology */
	rc = lc_router_net(lctx, r, ROUTERS, LC_TOPO_STAR, 0, 0);
	if (!test_assert(rc == 0, "lc_router_net()")) goto err_ctx_free;
	for (int i = 0; i < ROUTERS; i++) {
		test_log("====== ROUTER %i\n", i);

		/* check router parameters */
		if (!test_assert(r[i] != NULL, "r[%i] allocated", i))
			goto err_ctx_free;
		if (!test_assert(r[i]->ctx == lctx, "context set"))
			goto err_ctx_free;
		if (!test_assert(r[i]->flags == 0, "flags set"))
			goto err_ctx_free;
		/* r[0] has a port for each other router (round up to next power of 2),
		 * others have 1 */
		unsigned int ports = (!i) ? next_pow2(ROUTERS - 1) : 1;
		if (!test_assert(r[i]->ports == ports, "ports == %i", r[i]->ports))
			goto err_ctx_free;
	}
	/* now check ports are connected correctly */
	for (int i = 1; i < ROUTERS; i++) {
		int port = i - 1;
		if (!test_assert(r[0]->port[port] != NULL, "R[0]P[%i] connected", port))
			goto err_ctx_free;
		test_assert(r[0]->port[port]->pair == r[i]->port[0],
				"R[0]P[%i] <--> R[%i][0]", port, i);
	}
	for (int i = 0; i < ROUTERS; i++) lc_router_port_up(r[i], -1);
	test_assert(!lc_router_net_hasloop(r, ROUTERS), "routing loop not expected");
err_ctx_free:
	lc_ctx_free(lctx);
	return test_status;
}
