/* 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 can take NULL, 0 data with RaptorQ
 *
 * - lc_socket_send
 * - lc_socket_sendmsg
 * - lc_channel_send
 * - lc_channel_sendmsg
 *
 */

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

#ifdef HAVE_LIBLCRQ
enum {
	NO_FEC,
	FEC,
};

static int test_prepare_to_send(lc_ctx_t **lctx, lc_socket_t **sock, lc_channel_t **chan, int use_fec)
{
	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;
	*chan = lc_channel_new(*lctx, "nada");
	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;

	if (use_fec) lc_channel_coding_set(*chan, LC_CODE_FEC_RQ);

	return test_status;
}

static int test_socket_sendmsg(int use_fec)
{
	lc_ctx_t *lctx = NULL;
	lc_socket_t *sock = NULL;
	lc_channel_t *chan = NULL;
	struct msghdr msg = { .msg_iov = NULL, .msg_iovlen = 0 };
	ssize_t byt;

	if (test_prepare_to_send(&lctx, &sock, &chan, use_fec)) goto free_ctx;
	byt = lc_socket_sendmsg(sock, &msg, 0);
	test_assert(byt >= 0, "%s returned %zi with NULL data (FEC=%i)", __func__, byt, use_fec);

free_ctx:
	if (lctx) lc_ctx_free(lctx);
	return test_status;
}

static int test_channel_sendmsg(int use_fec)
{
	lc_ctx_t *lctx;
	lc_socket_t *sock;
	lc_channel_t *chan;
	struct msghdr msg = { .msg_iov = NULL, .msg_iovlen = 0 };
	ssize_t byt;

	if (test_prepare_to_send(&lctx, &sock, &chan, use_fec)) goto free_ctx;
	byt = lc_channel_sendmsg(chan, &msg, 0);
	test_assert(byt >= 0, "%s returned %zi with NULL data (FEC=%i)", __func__, byt, use_fec);

free_ctx:
	lc_ctx_free(lctx);
	return test_status;
}

static int test_socket_send(int use_fec)
{
	lc_ctx_t *lctx;
	lc_socket_t *sock;
	lc_channel_t *chan;
	ssize_t byt;

	if (test_prepare_to_send(&lctx, &sock, &chan, use_fec)) goto free_ctx;
	byt = lc_socket_send(sock, NULL, 0, 0);
	test_assert(byt >= 0, "%s returned %zi with NULL data (FEC=%i)", __func__, byt, use_fec);

free_ctx:
	lc_ctx_free(lctx);
	return test_status;
}

static int test_channel_send(int use_fec)
{
	lc_ctx_t *lctx;
	lc_socket_t *sock;
	lc_channel_t *chan;
	ssize_t byt;

	if (test_prepare_to_send(&lctx, &sock, &chan, use_fec)) goto free_ctx;
	byt = lc_channel_send(chan, NULL, 0, 0);
	test_assert(byt >= 0, "%s returned %zi with NULL data (FEC=%i)", __func__, byt, use_fec);

free_ctx:
	lc_ctx_free(lctx);
	return test_status;
}
#endif

int main(void)
{
	char name[] = "send zero length data with RaptorQ";
#ifdef HAVE_LIBLCRQ
	test_name(name);
	if (test_channel_send(NO_FEC)) return test_status;
	if (test_channel_send(FEC)) return test_status;
	if (test_channel_sendmsg(NO_FEC)) return test_status;
	if (test_channel_sendmsg(FEC)) return test_status;
	if (test_socket_send(NO_FEC)) return test_status;
	if (test_socket_send(FEC)) return test_status;
	if (test_socket_sendmsg(NO_FEC)) return test_status;
	if (test_socket_sendmsg(FEC)) return test_status;
	return test_status;
#else
	return test_skip(name);
#endif
}
