#include "vxi11.h"
#include "globals.h"
#include <stdio.h>
#include <rpc/rpc.h>
#include <stdbool.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define DEBUG

#ifdef DEBUG
#include <stdlib.h>
#endif

#define ERR_SYNTAXERROR 1
#define ERR_DEVICENOTACCESSIBLE 3
#define ERR_INVALIDLINKINDENTIFIER 4
#define ERR_PARAMETERERROR 5
#define ERR_CHANNELNOTESTABLISHED 6
#define ERR_OPERATIONNOTSUPPORTED 8
#define ERR_OUTOFRESOURCES 9
#define ERR_DEVICELOCKEDBYANOTHERLINK 11
#define ERR_NOLOCKHELDBYTHISLINK 12
#define ERR_IOTIMEOUT 15
#define ERR_IOERROR 17
#define ERR_INVALIDADDRESS 21
#define ERR_ABORT 23
#define ERR_CHANNELALREADYESTABLISHED 29

static bool isLocked() {
	return globals.VxiLocks.locked_network_server != NO_SERVER_LOCKED;
}

static bool haveLock(int lid) {
	return globals.VxiLocks.locked_network_server == lid;
}

static bool waitForLock(long timeout) {
	return false;
}

static void lock(int lid) {
	globals.VxiLocks.locked_network_server = lid;
}

static void unlock() {
	globals.VxiLocks.locked_network_server = NO_SERVER_LOCKED;
}

Device_Error *
device_abort_1_svc(Device_Link *argp, struct svc_req *rqstp) {
	printf("device_abort_1_svc()\n");
	static Device_Error result;

	/*
	 * insert server code here
	 */
	result.error = 0;
	return &result;
}

Create_LinkResp *
create_link_1_svc(Create_LinkParms *argp, struct svc_req *rqstp) {
	printf("create_link_1_svc()\n");
	static Create_LinkResp result;
	globals.Remote.vxi_connections++;
	result.error = 0;

	struct sockaddr_in sin;
	socklen_t len = sizeof(sin);
	getsockname(rqstp->rq_xprt->xp_sock, (struct sockaddr *) &sin, &len);
	result.abortPort = sin.sin_port;
	return &result;
}

Device_WriteResp *
device_write_1_svc(Device_WriteParms *argp, struct svc_req *rqstp) {
	static Device_WriteResp result;
	printf("device_write_1_svc()\n");

	if (isLocked(argp->lid) && !haveLock(argp->lid))
		result.error = ERR_DEVICELOCKEDBYANOTHERLINK;
	else {
		printf("%s\n", argp->data.data_val);
		//result.size = argp->data.data_len;
		result.size = 1;
		result.error = 0;
	}
	return &result;
}

Device_ReadResp *
device_read_1_svc(Device_ReadParms *argp, struct svc_req *rqstp) {
	static Device_ReadResp result;
	printf("device_read_1_svc()\n");

	if (isLocked(argp->lid) && !haveLock(argp->lid))
		result.error = ERR_DEVICELOCKEDBYANOTHERLINK;
	else {
		result.data.data_val = "HELLO!";
		result.data.data_len = 7;
		result.error = 0;
		result.reason = 0x4;
	}
	return &result;
}

Device_ReadStbResp *
device_readstb_1_svc(Device_GenericParms *argp, struct svc_req *rqstp) {
	printf("device_readstb_1_svc()\n");
	static Device_ReadStbResp result;
	result.error = 0;
	result.stb = 0;
	return &result;
}

Device_Error *
device_trigger_1_svc(Device_GenericParms *argp, struct svc_req *rqstp) {
	printf("device_trigger_1_svc()\n");
	static Device_Error result;

	result.error = 0;
	return &result;
}

Device_Error *
device_clear_1_svc(Device_GenericParms *argp, struct svc_req *rqstp) {
	printf("device_clear_1_svc()\n");
	static Device_Error result;

	result.error = 0;
	return &result;
}

Device_Error *
device_remote_1_svc(Device_GenericParms *argp, struct svc_req *rqstp) {
	static Device_Error result;
	printf("device_remote_1_svc()\n");

	if (isLocked() && !haveLock(argp->lid))
		result.error = ERR_DEVICELOCKEDBYANOTHERLINK;
	else
		result.error = 0;
	return &result;
}

Device_Error *
device_local_1_svc(Device_GenericParms *argp, struct svc_req *rqstp) {
	static Device_Error result;
	printf("device_local_1_svc()\n");

	if (isLocked() && !haveLock(argp->lid))
		result.error = ERR_DEVICELOCKEDBYANOTHERLINK;
	else
		result.error = 0;
	return &result;
}

Device_Error *
device_lock_1_svc(Device_LockParms *argp, struct svc_req *rqstp) {
	static Device_Error result;
	printf("device_lock_1_svc()\n");

	if (isLocked(argp->lid)) {
		result.error = ERR_DEVICELOCKEDBYANOTHERLINK;
		if (haveLock(argp->lid)) {
			// maybe do something here to warn about a device trying to lock multiple times?
		}
	}
	else {
		lock(argp->lid);
		result.error = 0;
	}
	return &result;
}

Device_Error *
device_unlock_1_svc(Device_Link *argp, struct svc_req *rqstp) {
	static Device_Error result;
	printf("device_unlock_1_svc()\n");

	if (!isLocked(*argp) || !haveLock(*argp))
		result.error = ERR_NOLOCKHELDBYTHISLINK;
	else {
		result.error = 0;
		unlock();
	}
	return &result;
}

Device_Error *
device_enable_srq_1_svc(Device_EnableSrqParms *argp, struct svc_req *rqstp) {
	static Device_Error result;
	printf("device_enable_srq_1_svc()\n");
	return &result;
}

Device_DocmdResp *
device_docmd_1_svc(Device_DocmdParms *argp, struct svc_req *rqstp) {
	printf("device_docmd_1_svc()\n");
	static Device_DocmdResp result;
	result.data_out.data_out_len = 0;
	result.data_out.data_out_val = NULL;
	result.error = ERR_OPERATIONNOTSUPPORTED;
	return &result;
}

Device_Error *
destroy_link_1_svc(Device_Link *argp, struct svc_req *rqstp) {
	printf("destroy_link_1_svc()\n");
	static Device_Error result;
	globals.Remote.vxi_connections--;
	return &result;
}

Device_Error *
create_intr_chan_1_svc(Device_RemoteFunc *argp, struct svc_req *rqstp) {
	static Device_Error result;
	printf("create_intr_chan_1_svc()\n");

#ifdef DEBUG
	char clientaddressstring[INET_ADDRSTRLEN];
	inet_ntop(AF_INET, &argp->hostAddr, clientaddressstring, sizeof(clientaddressstring));
	printf("Client %s is asking for an interrupt connection on port %u\n", clientaddressstring, ntohs(argp->hostPort));
#endif

	bool alreadyhavechannel = false;
	if (alreadyhavechannel) {
		result.error = ERR_CHANNELALREADYESTABLISHED;
	}
	else {
		struct sockaddr_in clientaddr;
		clientaddr.sin_family = AF_INET;
		clientaddr.sin_port = argp->hostPort;
		clientaddr.sin_addr.s_addr = argp->hostAddr;
		int sock = RPC_ANYSOCK;
		CLIENT* clnt = clnttcp_create(&clientaddr, DEVICE_INTR, DEVICE_INTR_VERSION, &sock, 0, 0);
		if (clnt == NULL) {
#ifdef DEBUG
			printf("Couldn't create interrupt channel client\n");
#endif
			result.error = ERR_CHANNELNOTESTABLISHED;
		}
		else {
			result.error = 0;
		}
	}
	return &result;
}

Device_Error *
destroy_intr_chan_1_svc(void *argp, struct svc_req *rqstp) {
	static Device_Error result;
	printf("destroy_intr_chan_1_svc()\n");
	bool havechannel = false;
	if (!havechannel) {
		result.error = ERR_CHANNELNOTESTABLISHED;
	}
	else {
		result.error = 0;
	}
	return &result;
}

/**
 void *
 device_intr_srq_1_svc(Device_SrqParms *argp, struct svc_req *rqstp) {
 printf("device_intr_srq_1_svc()\n");
 static char * result;
 return (void *) &result;
 }
 **/