/**************************************/
/* DESCRIPTION */

// Just a tester to see if the tunnel works properly.


/**************************************/
/* INCLUDES */

#include "tool.h"
#include "assert.h"

#define IDLE_TIMEOUT		1

#define	FATAL_ERROR()		{ printf("An error occured at line %d.\n", __LINE__); exit(1); }

#define MY_STRING_1		"Hello, World! This is a major test, I don't know if you'll like this, but I sure hope."
#define MY_STRING_2		"Another string, this rocks!"
#define MY_STRING_3		"Third string.0123456789!@#$%^&*()+=~.,|"
#define DATA(X)			#X, (void *) X, sizeof(X)

#define xstr(s) str(s)
#define str(s) #s
#define foo 4
#define FOO1 DATA(MY_DATA_1)
#define FOO2 (FOO1)
    
     
int MY_DATA_1[]	= { 0, 1, 0, 60, 42, 47, 0 };
int MY_DATA_2[]	= { 255 };

void do_local();
void do_remote();
void do_seq(int fd);
void write_data(int fd, char *name, void *data, int size);
void read_data(int fd, char *name, void *data, int size);
void write_string(int fd, char *string);
void read_string(int fd, char *string);
int gen_send(int sock, char *buf, int length);
int gen_recv(int sock, char *buf, int length);
int gen_recv(int sock, char *buf, int length);
void dump_buffer_in_hex(char *buffer, int size);


/**************************************/
/* FUNCTIONS */


int main(int argc, char **argv)
	{
	if(argc != 2)
		{
		printf("Usage: tester {l | r}\n");
		return 1;
		}
	
	if(strcmp(argv[1], "l") == 0)
		{
		do_local();
		}
	
	else if(strcmp(argv[1], "r") == 0)
		{
		do_remote();
		}
	
	else
		{
		printf("Usage: tester {l | r}\n");
		return 1;
		}
	}


/**************************************/
void do_local()
	{
	int error;
	sockaddr_in my_addr;
	int my_sock;
	
	error = get_ip_address("127.0.0.1", &my_addr);
	if(error)
		FATAL_ERROR();
		
	
	error = establish_ip_connection(&my_addr, 1020, &my_sock, TCP_CONNECTION);
	if(error)
		FATAL_ERROR();
	
	printf("Connected.\n");

	do_seq(my_sock);
	
	printf("OK.\n");
	exit(0);
	}


/**************************************/
void do_remote()
	{
	int error;
	int my_l_sock;
	int my_sock;

	error = open_ip_sock(TCP_CONNECTION, &my_l_sock);
	if(error)
		FATAL_ERROR();

	error = bind_ip_sock(&my_l_sock, 1060);
	if(error)
		FATAL_ERROR();
	
	error = listen(my_l_sock, 0);
	if(error)
		FATAL_ERROR();
	
	error = accept(my_l_sock, NULL, 0);
	if(error == -1)
		{
		perror("Cannot accept a connection");
		exit(1);
		}
	
	my_sock = error;
	
	printf("Got the connection.\n");
	
	do_seq(my_sock);
	
	printf("OK.\n");
	exit(0);
	}
	

/**************************************/
void do_seq(int fd)
	{
	
	write_data(fd, DATA(MY_DATA_2));
	read_data(fd, DATA(MY_DATA_2));
	
	write_data(fd, DATA(MY_DATA_1));
	read_data(fd, DATA(MY_DATA_1));

	write_data(fd, DATA(MY_DATA_1));
	read_data(fd, DATA(MY_DATA_1));
	write_data(fd, DATA(MY_DATA_1));
	read_data(fd, DATA(MY_DATA_1));
	write_data(fd, DATA(MY_DATA_1));
	read_data(fd, DATA(MY_DATA_1));
	write_data(fd, DATA(MY_DATA_1));
	read_data(fd, DATA(MY_DATA_1));
	
	write_data(fd, DATA(MY_DATA_2));
	read_data(fd, DATA(MY_DATA_2));
	write_data(fd, DATA(MY_DATA_2));
	read_data(fd, DATA(MY_DATA_2));
	write_data(fd, DATA(MY_DATA_2));
	read_data(fd, DATA(MY_DATA_2));
	write_data(fd, DATA(MY_DATA_2));
	read_data(fd, DATA(MY_DATA_2));
	write_data(fd, DATA(MY_DATA_2));
	read_data(fd, DATA(MY_DATA_2));
	
	write_data(fd, DATA(MY_DATA_2));
	write_data(fd, DATA(MY_DATA_2));
	write_data(fd, DATA(MY_DATA_2));
	write_data(fd, DATA(MY_DATA_2));
	write_data(fd, DATA(MY_DATA_2));
	write_data(fd, DATA(MY_DATA_2));
	write_data(fd, DATA(MY_DATA_2));
	write_data(fd, DATA(MY_DATA_2));
	write_data(fd, DATA(MY_DATA_2));
	write_data(fd, DATA(MY_DATA_2));
	write_data(fd, DATA(MY_DATA_2));
	write_data(fd, DATA(MY_DATA_2));
	write_data(fd, DATA(MY_DATA_2));
	write_data(fd, DATA(MY_DATA_2));
	write_data(fd, DATA(MY_DATA_2));
	write_data(fd, DATA(MY_DATA_2));
	read_data(fd, DATA(MY_DATA_2));
	read_data(fd, DATA(MY_DATA_2));
	read_data(fd, DATA(MY_DATA_2));
	read_data(fd, DATA(MY_DATA_2));
	read_data(fd, DATA(MY_DATA_2));
	read_data(fd, DATA(MY_DATA_2));
	read_data(fd, DATA(MY_DATA_2));
	read_data(fd, DATA(MY_DATA_2));
	read_data(fd, DATA(MY_DATA_2));
	read_data(fd, DATA(MY_DATA_2));
	read_data(fd, DATA(MY_DATA_2));
	read_data(fd, DATA(MY_DATA_2));
	read_data(fd, DATA(MY_DATA_2));
	read_data(fd, DATA(MY_DATA_2));
	read_data(fd, DATA(MY_DATA_2));
	read_data(fd, DATA(MY_DATA_2));

	
	write_string(fd, MY_STRING_1);
	read_string(fd, MY_STRING_1);
	write_string(fd, MY_STRING_2);
	read_string(fd, MY_STRING_2);
	write_string(fd, MY_STRING_3);
	read_string(fd, MY_STRING_3);
	
	write_string(fd, MY_STRING_3);
	read_string(fd, MY_STRING_3);
	write_string(fd, MY_STRING_3);
	read_string(fd, MY_STRING_3);
	write_string(fd, MY_STRING_3);
	read_string(fd, MY_STRING_3);
	write_string(fd, MY_STRING_3);
	read_string(fd, MY_STRING_3);
	write_string(fd, MY_STRING_3);
	read_string(fd, MY_STRING_3);
	write_string(fd, MY_STRING_3);
	read_string(fd, MY_STRING_3);
	write_string(fd, MY_STRING_3);
	read_string(fd, MY_STRING_3);
	write_string(fd, MY_STRING_3);
	read_string(fd, MY_STRING_3);
	write_string(fd, MY_STRING_3);
	read_string(fd, MY_STRING_3);
	write_string(fd, MY_STRING_3);
	read_string(fd, MY_STRING_3);
	write_string(fd, MY_STRING_3);
	read_string(fd, MY_STRING_3);
	write_string(fd, MY_STRING_3);
	read_string(fd, MY_STRING_3);
	write_string(fd, MY_STRING_3);
	read_string(fd, MY_STRING_3);
	
	write_string(fd, MY_STRING_3);
	write_string(fd, MY_STRING_3);
	write_string(fd, MY_STRING_3);
	write_string(fd, MY_STRING_3);
	read_string(fd, MY_STRING_3);
	read_string(fd, MY_STRING_3);
	read_string(fd, MY_STRING_3);
	read_string(fd, MY_STRING_3);
	}


/**************************************/
void write_data(int fd, char *name, void *data, int size)
	{
	int error;

	error = gen_send(fd, (char *) data, size);
	
	if(error)
		{
		printf("Failed to send %s.\n", name);
		exit(1);
		}
	}


/**************************************/
void read_data(int fd, char *name, void *data, int size)
	{
	char buf[88024];
	int error;
	
	error = gen_recv(fd, (char *) buf, size);
	
	if(error)
		{
		printf("Failed to read %s.\n", name);
		exit(1);
		}
	

	error = memcmp(buf, data, size);
	
	if(error != 0)
		{
		printf("Received %s and it's corrupted (%d).\n", name, error);
		printf("Should be: ");
		dump_buffer_in_hex((char *) data, size);
		printf("\n\n");
		printf("Is: ");
		dump_buffer_in_hex(buf, size);
		printf("\n");
		
		exit(1);
		}
	}


/**************************************/
void write_string(int fd, char *string)
	{
	int error;
	
	error = gen_send(fd, string, strlen(string) + 1);
	
	if(error)
		{
		printf("I was attempting to write '%s', %d bytes.\n", string, strlen(string) + 1);
		exit(1);
		}
	}


/**************************************/
void read_string(int fd, char *string)
	{
	char buf[88024];
	int error;
	
	error = gen_recv(fd, buf, strlen(string) + 1);
	
	if(error)
		{
		printf("I was attempting to read '%s', %d bytes.\n", string, strlen(string) + 1);
		exit(1);
		}
	

	error = strcmp(buf, string);
	if(error != 0)
		{
		printf("I was attempting to read '%s', %d bytes.\n", string, strlen(string) + 1);
		printf("The strings do not match. We read |%s|.\n", buf);
		exit(1);
		}
	}


/**************************************/
/* Generic send. */
int gen_send(int sock, char *buf, int length)
	{
	int byte_count = 0;
	int byte_sent;
	fd_set write_set;
	fd_set error_set;
	int error;
	struct timeval timeout;
				
	/* Write until every byte is sent */
	while(byte_count != length)
		{
		/* Reset fd_set and timer every time a byte is sent */
		FD_ZERO(&error_set);
		FD_ZERO(&write_set);
		FD_SET((unsigned int) sock, &write_set);
		timeout.tv_sec = IDLE_TIMEOUT;
		timeout.tv_usec = 0;
		
		/* Select until ready to write */
		error = select(sock + 1, NULL, &write_set, &error_set, &timeout);
		if(error == -1)
			{
			perror("Select failed");
			return 1;
			}

		if(!FD_ISSET(sock, &write_set))
			{
			/* If there is an error, perform the write():
			 * That will set errno so we get the error code.
			 * Else it's a timeout.
			 */
			if(!FD_ISSET(sock, &error_set))
				{
				printf("Connection timed out.\n");
				return 1;
				}
			}

		byte_sent = send(sock, buf + byte_count, length - byte_count, 0);

		/* -1 means error on write */
		if(byte_sent == -1 || byte_sent == 0)
			{
			if(errno == EPIPE)
				{
				printf("Connection lost\n");
				return 1;
				}
			
			else
				{
				perror("Write error");
				return 1;
				}
			}
				
		byte_count += byte_sent;
		}

	return 0;
	}


/**************************************/
/* Generic receive. */
int gen_recv(int sock, char *buf, int length)
	{
	int byte_count = 0;
	int byte_read;
	fd_set read_set;
	fd_set error_set;
	int error;
	struct timeval timeout;
	
	/* Write until every byte is received */
	while(byte_count != length)
		{
		/* Reset fd_set and timer every time a byte is received */
		FD_ZERO(&error_set);
		FD_ZERO(&read_set);
		FD_SET((unsigned int) sock, &read_set);
		timeout.tv_sec = IDLE_TIMEOUT;
		timeout.tv_usec = 0;
		
			/* Select until ready to read */
		error = select(sock + 1, &read_set, NULL, &error_set, &timeout);
		if(error == -1)
			{
			perror("Select failed");
			return 1;
			}

		if(!FD_ISSET(sock, &read_set))
			{
			/* If there is an error, perform the read():
			 * That will set errno so we get the error code.
			 * Else it's a timeout.
			 */ 
			if(! FD_ISSET(sock, &error_set))
				{
				printf("Connection timed out.\n");
				return 1;
				}
			}

		byte_read = recv(sock, buf + byte_count, length - byte_count, 0);

		if(byte_read == 0)
			{
			printf("Connection closed by remote side.\n");
			return 1;
			}
		
		/* -1 means error on read */
		if(byte_read == -1)
			{
			if(errno == EPIPE)
				{
				printf("Connection lost\n");
				return 1;
				}
			
			else
				{
				perror("Read error");
				return 1;
				}
			}
				
		byte_count += byte_read;
		}
 	
	return 0;
	}


/**************************************/
void dump_buffer_in_hex(char *buf, int size)
	{
	unsigned char *buffer = (unsigned char *) buf;
	
	printf("Dump: ");
	
	for(int i = 0; i < size; i++)
		{
		printf("%X ", buffer[i]);
		}
	
	printf("\n");
	}
