#include <termios.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define BAUDRATE B2400
#define SERIAL_DEVICE "/dev/ttyS0"
#define _POSIX_SOURCE 1
#define TCP_SERVER 1
#define TCP_PORT 2445
#define TCP_HOST "192.168.1.5"

#define MAX(a, b) (a > b ? a : b) 

int open_serial_device (const char *);
int open_net_connection (void);
void print_data (char *, int, char);

int main (void)
{
	int serial_device, net_connection, size, max_fd, i;
	char buffer[256];
	fd_set read_fs;

	serial_device = open_serial_device(SERIAL_DEVICE);
	net_connection = open_net_connection();

	printf("net connection established!\n");
	
	max_fd = MAX(serial_device, net_connection) + 1;
	
	while (1)
	{
		FD_SET(serial_device, &read_fs);
		FD_SET(net_connection, &read_fs);

		select(max_fd, &read_fs, NULL, NULL, NULL);
		
		if (FD_ISSET(serial_device, &read_fs))
		{
			size = read(serial_device, buffer, 255);
			write(net_connection, buffer, size);
			printf("got data (%d) of from serial device = ", size);
			for (i = 0; i < size; i++)
				printf("%.2hhx ", buffer[i]);
			printf("\n");
		}
		if (FD_ISSET(net_connection, &read_fs))
		{
			size = read(net_connection, buffer, 255);
			write(serial_device, buffer, size);
			printf("got data (%d) from net connection = ", size);
			for (i = 0; i < size; i++)
				printf("%.2hhx ", buffer[i]);
			printf("\n");
		}
	}

	close(serial_device);
	close(net_connection);
	
	return 0;
}

int
open_serial_device 
(const char * serial_device)
{
	struct termios term_settings;
	int fd;

	fd = open(serial_device, O_RDWR | O_NOCTTY);
	if (fd < 0)
	{
		perror(serial_device);
		exit(1);
	}
	
	memset(&term_settings, 0, sizeof(term_settings));
	
	term_settings.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
	term_settings.c_iflag = IGNPAR;
	term_settings.c_oflag = 0;
	term_settings.c_lflag = 0;
	term_settings.c_cc[VMIN] = 1;
	term_settings.c_cc[VTIME] = 0;

	tcflush(fd, TCIFLUSH);
	tcsetattr(fd, TCSANOW, &term_settings);

	return fd;
}

int open_net_connection (void)
{
#if TCP_SERVER
	int listen_sock, comm_sock, size, yes = 1;
	struct sockaddr_in server_addr, client_addr;

	if ((listen_sock = socket(PF_INET, SOCK_STREAM, 6)) == -1)
	{
		perror("socket");
		exit(1);
	}

	if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
	{
		perror("setsockopt");
		exit(1);
	}
	
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(TCP_PORT);
	server_addr.sin_addr.s_addr = INADDR_ANY; 
	memset(&(server_addr.sin_zero), '\0', 8);
	
	if (bind(listen_sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in)) == -1)
	{
		perror("bind");
		exit(1);
	}
	
	if (listen(listen_sock, 5) == -1)
	{
		perror("listen");
		exit(1);
	}
	
	printf("listening on TCP port %d\n", TCP_PORT);
	
	if ((comm_sock = accept(listen_sock, (struct sockaddr *)&client_addr, &size)) == -1)
	{
		perror("accept");
		exit(1);
	}
	
	return comm_sock;
#else	
	int sockfd;
	struct sockaddr_in dest_addr;

	sockfd = socket(AF_INET, SOCK_STREAM, 0);

	dest_addr.sin_family = AF_INET;
	dest_addr.sin_port = htons(TCP_PORT);
	dest_addr.sin_addr.s_addr = inet_addr(TCP_HOST);
	memset(&(dest_addr.sin_zero), '\0', 8);

	printf("connecting to %s on TCP port %d\n", TCP_HOST, TCP_PORT);

	if (connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr)))
	{
		perror("connect");
		exit(1);
	}

	return sockfd;
#endif
}
