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

/* test send functions return nett bytes sent, without overhead */

#include "test.h"
#include "testnet.h"
#include <librecast/net.h>

#define CHANNEL_NAME "0000-0083"

#ifdef HAVE_LIBLCRQ
static char data[1024];
static unsigned int ifidx;

static int test_prepare_to_send(lc_ctx_t **lctx, lc_socket_t **sock, lc_channel_t **chan, int encodings)
{
	int rc;

	*lctx = lc_ctx_new();
	if (!test_assert(lctx != NULL, "lc_ctx_new()")) return test_status;
	*sock = lc_socket_new(*lctx);
	if (!test_assert(sock != NULL, "lc_socket_new()")) return test_status;
	rc = lc_socket_bind(*sock, ifidx);
	if (rc == -1) perror("lc_socket_bind");
	if (!test_assert(rc == 0, "lc_socket_bind() ifx = %u", ifidx)) return test_status;
	*chan = lc_channel_new(*lctx, CHANNEL_NAME);
	if (!test_assert(*chan != NULL, "lc_channel_new()")) return test_status;
	rc = lc_channel_bind(*sock, *chan);
	if (!test_assert(rc == 0, "lc_channel_bind()")) return test_status;
	rc = lc_channel_join(*chan);
	if (!test_assert(rc == 0, "lc_channel_join()")) return test_status;
	rc = lc_socket_loop(*sock, 1);
	if (!test_assert(rc == 0, "lc_socket_loop()")) return test_status;

	if (encodings) {
		lc_channel_coding_set(*chan, encodings);
		if (encodings & LC_CODE_FEC_RQ) lc_channel_rq_overhead(*chan, RQ_OVERHEAD * 2);
	}

	return test_status;
}

static int test_channel_send(int encodings)
{
	lc_ctx_t *lctx;
	lc_socket_t *sock;
	lc_channel_t *chan = NULL;
	char buf[BUFSIZ];
	ssize_t byt;

	/* generate random data to send */
	ssize_t len = arc4random_uniform(sizeof data);
	arc4random_buf(data, len);

	if (test_prepare_to_send(&lctx, &sock, &chan, encodings)) goto free_ctx;
	byt = lc_channel_send(chan, data, len, 0);
	if (byt == -1) perror("lc_channel_send");
	if (!test_assert(byt == len, "%s returned %zi (expected %zi) with enc=%i",
				__func__, byt, len, encodings)) goto free_ctx;
	byt = lc_channel_recv(chan, buf, sizeof buf, 0);
	test_assert(byt == len, "%s recv'ed %zi (expected %zi) with enc=%i", __func__, byt, len, encodings);

free_ctx:
	lc_ctx_free(lctx);
	return test_status;
}

static int test_channel_sendmsg(int encodings)
{
	lc_ctx_t *lctx;
	lc_socket_t *sock;
	lc_channel_t *chan = NULL;
	char buf[BUFSIZ];
	ssize_t byt;

	/* generate random data to send */
	ssize_t len = arc4random_uniform(sizeof data);
	arc4random_buf(data, len);
	struct iovec iov = { .iov_base = data, .iov_len = len };
	struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1 };

	if (test_prepare_to_send(&lctx, &sock, &chan, encodings)) goto free_ctx;
	byt = lc_channel_sendmsg(chan, &msg, 0);
	if (byt == -1) perror("lc_channel_sendmsg");
	if (!test_assert(byt == len, "%s returned %zi (expected %zi) with enc=%i",
				__func__, byt, len, encodings)) goto free_ctx;
	iov.iov_base = buf;
	byt = lc_channel_recvmsg(chan, &msg, 0);
	test_assert(byt == len, "%s recv'ed %zi (expected %zi) with enc=%i", __func__, byt, len, encodings);
free_ctx:
	lc_ctx_free(lctx);
	return test_status;
}
#endif

int main(void)
{
	char name[] = "check send/recv return values";
#ifdef HAVE_LIBLCRQ
	test_name(name);
	test_require_net(TEST_NET_BASIC);

	ifidx = get_multicast_if();
	if (!test_assert(ifidx > 0, "get_multicast_if()")) return test_status;

	if (test_channel_send(0)) return test_status;
	if (test_channel_send(LC_CODE_FEC_RQ)) return test_status;
	if (test_channel_sendmsg(0)) return test_status;
	if (test_channel_sendmsg(LC_CODE_FEC_RQ)) return test_status;
	return test_status;
#else
	return test_skip(name);
#endif
}
