#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

/* XXX Make that some parameter */
#define TCP_PORT	13245
#define MAX_PACKET	32

#define vhpi0	2	/* forcing 0 */
#define vhpi1	3	/* forcing 1 */

static void to_std_logic_vector(unsigned long val, unsigned char *p,
				unsigned long len)
{
	if (len > 64) {
		fprintf(stderr, "%s: invalid length %lu\n", __func__, len);
		exit(1);
	}

	for (unsigned long i = 0; i < len; i++) {
		if ((val >> (len-1-i) & 1))
			*p = vhpi1;
		else
			*p = vhpi0;

		p++;
	}
}

static uint64_t from_std_logic_vector(unsigned char *p, unsigned long len)
{
	unsigned long ret = 0;

	if (len > 64) {
		fprintf(stderr, "%s: invalid length %lu\n", __func__, len);
		exit(1);
	}

	for (unsigned long i = 0; i < len; i++) {
		unsigned char bit;

		if (*p == vhpi0) {
			bit = 0;
		} else if (*p == vhpi1) {
			bit = 1;
		} else {
			fprintf(stderr, "%s: bad bit %d\n", __func__, *p);
			bit = 0;
		}

		ret = (ret << 1) | bit;
		p++;
	}

	return ret;
}

static int fd = -1;
static int cfd = -1;

static void open_socket(void)
{
	struct sockaddr_in addr;
	int opt, rc, flags;

	if (fd >= 0 || fd < -1)
		return;

	signal(SIGPIPE, SIG_IGN);
	fd = socket(AF_INET, SOCK_STREAM, 0);
	if (fd < 0) {
		fprintf(stderr, "Failed to open debug socket !\r\n");
		goto fail;
	}

	rc = 0;
	flags = fcntl(fd, F_GETFL);
	if (flags >= 0)
		rc = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
	if (flags < 0 || rc < 0) {
		fprintf(stderr, "Failed to configure debug socket !\r\n");
	}

	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(TCP_PORT);
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	opt = 1;
	setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
	rc = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
	if (rc < 0) {
		fprintf(stderr, "Failed to bind debug socket !\r\n");
		goto fail;
	}
	rc = listen(fd,1);
	if (rc < 0) {
		fprintf(stderr, "Failed to listen to debug socket !\r\n");
		goto fail;
	}
	fprintf(stdout, "Debug socket ready\r\n");
	return;
fail:
	if (fd >= 0)
		close(fd);
	fd = -2;
}

static void check_connection(void)
{
	struct sockaddr_in addr;
	socklen_t addr_len = sizeof(addr);

	cfd = accept(fd, (struct sockaddr *)&addr, &addr_len);
	if (cfd < 0)
		return;
	fprintf(stdout, "Debug client connected !\r\n");
}

void sim_jtag_read_msg(unsigned char *out_msg, unsigned char *out_size)
{
	unsigned char data[MAX_PACKET];
	unsigned char size = 0;
	struct pollfd fdset[1];
	int rc, i;

	if (fd == -1)
		open_socket();
	if (fd < 0)
		goto finish;
	if (cfd < 0)
		check_connection();
	if (cfd < 0)
		goto finish;

	memset(fdset, 0, sizeof(fdset));
	fdset[0].fd = cfd;
	fdset[0].events = POLLIN;
	rc = poll(fdset, 1, 0);
	if (rc <= 0)
		goto finish;
	rc = read(cfd, data, MAX_PACKET);
	if (rc < 0)
		fprintf(stderr, "Debug read error, assuming client disconnected !\r\n");
	if (rc == 0)
		fprintf(stdout, "Debug client disconnected !\r\n");
	if (rc <= 0) {
		close(cfd);
		cfd = -1;
		goto finish;
	}

#if 0
	fprintf(stderr, "Got message:\n\r");
	{
		for (i=0; i<rc; i++)
			fprintf(stderr, "%02x ", data[i]);
		fprintf(stderr, "\n\r");
	}
#endif
	size = data[0]; /* Size in bits */

	/* Special sizes */
	if (size == 255) {
		/* JTAG reset, message to translate */
		goto finish;
	}

	if (((rc - 1) * 8) < size) {
		fprintf(stderr, "Debug short read: %d bytes for %d bits, truncating\r\n",
			rc - 1, size);
		size = (rc - 1) * 8;
	}

	for (i = 0; i < size; i++) {
		int byte = i >> 3;
		int bit = 1 << (i & 7);
		out_msg[i] = (data[byte+1] & bit) ? vhpi1 : vhpi0;
	}
finish:
	to_std_logic_vector(size, out_size, 8);
}

void sim_jtag_write_msg(unsigned char *in_msg, unsigned char *in_size)
{
	unsigned char data[MAX_PACKET];
	unsigned char size;
	int rc, i;

	size = from_std_logic_vector(in_size, 8);
	data[0] = size;
	for (i = 0; i < size; i++) {
		int byte = i >> 3;
		int bit = 1 << (i & 7);
		if (in_msg[i] == vhpi1)
			data[byte+1] |= bit;
		else
			data[byte+1] &= ~bit;
	}
	rc = (size + 7) / 8;

#if 0
	fprintf(stderr, "Sending response:\n\r");
	{
		for (i=0; i<rc; i++)
			fprintf(stderr, "%02x ", data[i]);
		fprintf(stderr, "\n\r");
	}
#endif

	rc = write(cfd, data, rc);
	if (rc < 0)
		fprintf(stderr, "Debug write error, ignoring\r\n");
}

