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

#include "testnet.h"
#include <librecast/net.h>
#include <pthread.h>
#include <semaphore.h>

#define WAITS 1

static char channel_name[] = "0000-0070";
static sem_t sem;
static ssize_t bytes = -1;

static void *listen_thread(void *arg)
{
	lc_ctx_t * lctx;
	lc_socket_t * sock;
	lc_channel_t * chan;
	struct iovec iov;
	struct msghdr msg = {0};

	lctx = lc_ctx_new();
	sock = lc_socket_new(lctx);
	chan = lc_channel_new(lctx, channel_name);

	iov.iov_base = arg;
	iov.iov_len = BUFSIZ;
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;

	lc_channel_bind(sock, chan);
	lc_channel_join(chan);

	sem_post(&sem); /* tell send thread we're ready */
	bytes = lc_channel_recvmsg(chan, &msg, 0);
	if (bytes == -1) perror("lc_channel_recvmsg");
	sem_post(&sem); /* tell send thread we're done */

	lc_ctx_free(lctx);
	return arg;
}

int main(void)
{
	lc_ctx_t * lctx;
	lc_socket_t * sock;
	lc_channel_t * chan;
	pthread_attr_t attr = {0};
	pthread_t thread;
	struct timespec ts;
	char buf[] = "liberté";
	char recvbuf[BUFSIZ] = "";
	struct iovec iov;
	struct msghdr msg = {0};

	test_name("lc_channel_sendmsg() / lc_channel_recvmsg()");
	test_require_net(TEST_NET_BASIC);

	sem_init(&sem, 0, 0);

	pthread_attr_init(&attr);
	pthread_create(&thread, &attr, &listen_thread, &recvbuf);
	pthread_attr_destroy(&attr);

	sem_wait(&sem); /* recv thread is ready */

	lctx = lc_ctx_new();
	sock = lc_socket_new(lctx);
	chan = lc_channel_new(lctx, channel_name);

	iov.iov_base = buf;
	iov.iov_len = strlen(buf) + 1;
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;

	lc_socket_loop(sock, 1);
	lc_channel_bind(sock, chan);
	lc_channel_sendmsg(chan, &msg, 0);
	lc_ctx_free(lctx);

	test_assert(!clock_gettime(CLOCK_REALTIME, &ts), "clock_gettime()");
	ts.tv_sec += WAITS;
	test_assert(!sem_timedwait(&sem, &ts), "timeout");
	sem_timedwait(&sem, &ts);
	sem_destroy(&sem);

	pthread_cancel(thread);
	pthread_join(thread, NULL);

	test_assert(bytes == (ssize_t)iov.iov_len,
			"received %zi bytes, expected %zu", bytes, iov.iov_len);
	test_expect(buf, recvbuf);

	return test_status;
}
