#define  _GNU_SOURCE

#include "vxi11.h"
#include "vxi11_server.h"
#include <stdio.h>
#include <stdlib.h>
#include <rpc/pmap_clnt.h>
#include <string.h>
#include <memory.h>
#include <poll.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define	VXI11_DEFAULT_TIMEOUT	1000

#ifndef SIG_PF
#define SIG_PF void(*)(int)
#endif

static void device_async_1(struct svc_req *rqstp, register SVCXPRT *transp)
{
	union {
		Device_Link device_abort_1_arg;
	} argument;
	char *result;
	xdrproc_t _xdr_argument, _xdr_result;
	char *(*local)(char *, struct svc_req *);

	switch (rqstp->rq_proc) {
	case NULLPROC:
		(void) svc_sendreply(transp, (xdrproc_t) xdr_void, (char *) NULL);
		return;

	case device_abort:
		_xdr_argument = (xdrproc_t) xdr_Device_Link;
		_xdr_result = (xdrproc_t) xdr_Device_Error;
		local = (char *(*)(char *, struct svc_req *)) device_abort_1_svc;
		break;

	default:
		svcerr_noproc(transp);
		return;
	}
	memset((char *) &argument, 0, sizeof(argument));
	if (!svc_getargs(transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {
		svcerr_decode(transp);
		return;
	}
	result = (*local)((char *) &argument, rqstp);
	if (result != NULL && !svc_sendreply(transp, (xdrproc_t) _xdr_result, result)) {
		svcerr_systemerr(transp);
	}
	if (!svc_freeargs(transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {
		fprintf(stderr, "%s", "unable to free arguments");
		exit(1);
	}
	return;
}

static void device_core_1(struct svc_req *rqstp, register SVCXPRT *transp)
{
	union {
		Create_LinkParms create_link_1_arg;
		Device_WriteParms device_write_1_arg;
		Device_ReadParms device_read_1_arg;
		Device_GenericParms device_readstb_1_arg;
		Device_GenericParms device_trigger_1_arg;
		Device_GenericParms device_clear_1_arg;
		Device_GenericParms device_remote_1_arg;
		Device_GenericParms device_local_1_arg;
		Device_LockParms device_lock_1_arg;
		Device_Link device_unlock_1_arg;
		Device_EnableSrqParms device_enable_srq_1_arg;
		Device_DocmdParms device_docmd_1_arg;
		Device_Link destroy_link_1_arg;
		Device_RemoteFunc create_intr_chan_1_arg;
	} argument;
	char *result;
	xdrproc_t _xdr_argument, _xdr_result;
	char *(*local)(char *, struct svc_req *);

	switch (rqstp->rq_proc) {
	case NULLPROC:
		(void) svc_sendreply(transp, (xdrproc_t) xdr_void, (char *) NULL);
		return;

	case create_link:
		_xdr_argument = (xdrproc_t) xdr_Create_LinkParms;
		_xdr_result = (xdrproc_t) xdr_Create_LinkResp;
		local = (char *(*)(char *, struct svc_req *)) create_link_1_svc;
		break;

	case device_write:
		_xdr_argument = (xdrproc_t) xdr_Device_WriteParms;
		_xdr_result = (xdrproc_t) xdr_Device_WriteResp;
		local = (char *(*)(char *, struct svc_req *)) device_write_1_svc;
		break;

	case device_read:
		_xdr_argument = (xdrproc_t) xdr_Device_ReadParms;
		_xdr_result = (xdrproc_t) xdr_Device_ReadResp;
		local = (char *(*)(char *, struct svc_req *)) device_read_1_svc;
		break;

	case device_readstb:
		_xdr_argument = (xdrproc_t) xdr_Device_GenericParms;
		_xdr_result = (xdrproc_t) xdr_Device_ReadStbResp;
		local = (char *(*)(char *, struct svc_req *)) device_readstb_1_svc;
		break;

	case device_trigger:
		_xdr_argument = (xdrproc_t) xdr_Device_GenericParms;
		_xdr_result = (xdrproc_t) xdr_Device_Error;
		local = (char *(*)(char *, struct svc_req *)) device_trigger_1_svc;
		break;

	case device_clear:
		_xdr_argument = (xdrproc_t) xdr_Device_GenericParms;
		_xdr_result = (xdrproc_t) xdr_Device_Error;
		local = (char *(*)(char *, struct svc_req *)) device_clear_1_svc;
		break;

	case device_remote:
		_xdr_argument = (xdrproc_t) xdr_Device_GenericParms;
		_xdr_result = (xdrproc_t) xdr_Device_Error;
		local = (char *(*)(char *, struct svc_req *)) device_remote_1_svc;
		break;

	case device_local:
		_xdr_argument = (xdrproc_t) xdr_Device_GenericParms;
		_xdr_result = (xdrproc_t) xdr_Device_Error;
		local = (char *(*)(char *, struct svc_req *)) device_local_1_svc;
		break;

	case device_lock:
		_xdr_argument = (xdrproc_t) xdr_Device_LockParms;
		_xdr_result = (xdrproc_t) xdr_Device_Error;
		local = (char *(*)(char *, struct svc_req *)) device_lock_1_svc;
		break;

	case device_unlock:
		_xdr_argument = (xdrproc_t) xdr_Device_Link;
		_xdr_result = (xdrproc_t) xdr_Device_Error;
		local = (char *(*)(char *, struct svc_req *)) device_unlock_1_svc;
		break;

	case device_enable_srq:
		_xdr_argument = (xdrproc_t) xdr_Device_EnableSrqParms;
		_xdr_result = (xdrproc_t) xdr_Device_Error;
		local = (char *(*)(char *, struct svc_req *)) device_enable_srq_1_svc;
		break;

	case device_docmd:
		_xdr_argument = (xdrproc_t) xdr_Device_DocmdParms;
		_xdr_result = (xdrproc_t) xdr_Device_DocmdResp;
		local = (char *(*)(char *, struct svc_req *)) device_docmd_1_svc;
		break;

	case destroy_link:
		_xdr_argument = (xdrproc_t) xdr_Device_Link;
		_xdr_result = (xdrproc_t) xdr_Device_Error;
		local = (char *(*)(char *, struct svc_req *)) destroy_link_1_svc;
		break;

	case create_intr_chan:
		_xdr_argument = (xdrproc_t) xdr_Device_RemoteFunc;
		_xdr_result = (xdrproc_t) xdr_Device_Error;
		local = (char *(*)(char *, struct svc_req *)) create_intr_chan_1_svc;
		break;

	case destroy_intr_chan:
		_xdr_argument = (xdrproc_t) xdr_void;
		_xdr_result = (xdrproc_t) xdr_Device_Error;
		local = (char *(*)(char *, struct svc_req *)) destroy_intr_chan_1_svc;
		break;

	default:
		svcerr_noproc(transp);
		return;
	}
	memset((char *) &argument, 0, sizeof(argument));
	if (!svc_getargs(transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {
		svcerr_decode(transp);
		return;
	}
	result = (*local)((char *) &argument, rqstp);
	if (result != NULL && !svc_sendreply(transp, (xdrproc_t) _xdr_result, result)) {
		svcerr_systemerr(transp);
	}
	if (!svc_freeargs(transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {
		fprintf(stderr, "%s", "unable to free arguments");
		exit(1);
	}
	return;
}

void vxi_main()
{

	printf("VXI service started\n");

	register SVCXPRT *transp;

	pmap_unset(DEVICE_ASYNC, DEVICE_ASYNC_VERSION);
	pmap_unset(DEVICE_CORE, DEVICE_CORE_VERSION);

	transp = svcudp_create(RPC_ANYSOCK);
	if (transp == NULL) {
		fprintf(stderr, "%s", "cannot create udp service.");
		exit(1);
	}
	if (!svc_register(transp, DEVICE_ASYNC, DEVICE_ASYNC_VERSION, device_async_1, IPPROTO_UDP)) {
		fprintf(stderr, "%s", "unable to register (DEVICE_ASYNC, DEVICE_ASYNC_VERSION, udp).");
		exit(1);
	}
	if (!svc_register(transp, DEVICE_CORE, DEVICE_CORE_VERSION, device_core_1, IPPROTO_UDP)) {
		fprintf(stderr, "%s", "unable to register (DEVICE_CORE, DEVICE_CORE_VERSION, udp).");
		exit(1);
	}

	transp = svctcp_create(RPC_ANYSOCK, 0, 0);
	if (transp == NULL) {
		fprintf(stderr, "%s", "cannot create tcp service.");
		exit(1);
	}
	if (!svc_register(transp, DEVICE_ASYNC, DEVICE_ASYNC_VERSION, device_async_1, IPPROTO_TCP)) {
		fprintf(stderr, "%s", "unable to register (DEVICE_ASYNC, DEVICE_ASYNC_VERSION, tcp).");
		exit(1);
	}
	if (!svc_register(transp, DEVICE_CORE, DEVICE_CORE_VERSION, device_core_1, IPPROTO_TCP)) {
		fprintf(stderr, "%s", "unable to register (DEVICE_CORE, DEVICE_CORE_VERSION, tcp).");
		exit(1);
	}

	int i, j;
	struct pollfd *my_pollfd = NULL;
	int last_max_pollfd = 0;

	while (1) {
		int max_pollfd = svc_max_pollfd;
		if (max_pollfd == 0 && svc_pollfd == NULL) {
			break;
		}

		if (last_max_pollfd != max_pollfd) {
			struct pollfd *new_pollfd = realloc(my_pollfd, sizeof(struct pollfd) * max_pollfd);

			if (new_pollfd == NULL) {
				break;
			}

			my_pollfd = new_pollfd;
			last_max_pollfd = max_pollfd;
		}

		for (i = 0; i < max_pollfd; ++i) {
			my_pollfd[i].fd = svc_pollfd[i].fd;
			my_pollfd[i].events = svc_pollfd[i].events | POLLRDHUP; // add the "socket is dead" event
			my_pollfd[i].revents = 0;
		}

		j = poll(my_pollfd, max_pollfd, VXI11_DEFAULT_TIMEOUT);

		for (i = 0; i < max_pollfd; i++) {
			if (my_pollfd[i].revents & POLLRDHUP) {
				vxi11_deadsocketcallback(my_pollfd[i].fd);
			}
		}

		switch (j) {
		case -1:
			break;
		case 0:
			continue;
		default:
			svc_getreq_poll(my_pollfd, i);
			continue;
		}
		break;
	}

	//svc_run();
	//fprintf(stderr, "%s", "svc_run returned");
}