#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <sys/param.h>
#include <errno.h>
#include <unistd.h>
#include <netinet/in.h>
#include <linux/tipc.h>
#include <fcntl.h>
#include <math.h>
#include <time.h>

#include "common.h"


/*
 * Receive data and check sequence numbers until server says otherwise
 */
static void rdm_rcv(uint32_t rcvbuf, int permit_ooo, uint32_t senders)
{
	struct sockaddr_tipc sa;
	uint32_t sd = socket(AF_TIPC, SOCK_RDM, 0);
	uint8_t buf[TIPC_MAX_USER_MSG_SIZE];
	struct pkthdr *header;
	uint32_t n;
	uint32_t expected = 0;
	uint32_t max_seqno = 0;
	uint32_t rcvd_done = 0;
	socklen_t olen = sizeof(uint32_t);

	sa.family = AF_TIPC;
	sa.addrtype = TIPC_ADDR_NAMESEQ;
	sa.addr.nameseq.type = TEST_TYPE;
	sa.addr.nameseq.lower = TEST_INSTANCE;
	sa.addr.nameseq.upper = TEST_INSTANCE;
	sa.scope = TIPC_ZONE_SCOPE;

	if (0 != bind(sd, (struct sockaddr*)&sa, sizeof(sa)))
		diep("bind()");

	if (rcvbuf) {
		if (setsockopt(sd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(uint32_t)) < 0)
			diep("SO_RCVBUF");
		/*net.core.rmem_max may limit what was actually requested*/
		if (getsockopt(sd, SOL_SOCKET, SO_RCVBUF, &n, &olen) < 0)
			diep("SO_RCVBUF");
		printf("SO_RCVBUF set to %d bytes\n", n);
	}
	while (1) {
		if((n = recv(sd, buf, TIPC_MAX_USER_MSG_SIZE, 0)) <=0)
			diep("recv()");

		header = (struct pkthdr *)buf;
		if (max_seqno < header->seq +1)
			max_seqno = header->seq + 1;

		if (!permit_ooo && header->seq != expected) {
			printf("RDM: message %u out of sequence, expected=%u [%u missing] bytes\n",
			       header->seq, expected, (header->seq - expected));
			expected = header->seq;
		}
		expected++;
		if (!permit_ooo && header->extra == CMD_DONE) {
			printf("RDM: Server says we're done after %d messages, exiting\n", expected);
			exit(0);
		}
		if (header->extra == CMD_DONE)
			rcvd_done++;
		if ((rcvd_done == senders) && (expected == max_seqno * senders)) {
			printf("RDM: Server says we're done after %d messages, exiting\n", expected);
			exit(0);
		}
	}
}

int32_t main(int32_t argc, char *argv[], char *envp[])
{
	uint32_t msgsize = TIPC_MAX_USER_MSG_SIZE;
	uint32_t rcvbuf = 0;
	int expect_ooo = 0;
	uint32_t multiplier = 0;
	char c;

	printf("TIPC RDM receiver started\n");
	srand(time(NULL));
	while ((c = getopt(argc, argv, "t:m:r:")) != -1)
	{
		switch(c) {
		case 'm':
			msgsize = atoi(optarg);
		break;
		case 'r':
			rcvbuf = atoi(optarg);
		break;
		case 't':
			expect_ooo = 1;
			multiplier = atoi(optarg);
		break;
		default:
			exit(0);
		break;
		}
	}
	printf("Message size= %d\n", msgsize);
	rdm_rcv(rcvbuf, expect_ooo, multiplier);
	exit(0);
}
