//
// -*- filler.c
// -*- (sans filler melanie)
// -*- mycroft@minithins.net
/*
           ___   ___   ____    _       ____ _   _    _    ____    _    
          / _ \ / _ \ / ___|  / \     / ___| | | |  / \  / ___|  / \   
         | | | | | | | |  _  / _ \   | |   | |_| | / _ \| |     / _ \  
         | |_| | |_| | |_| |/ ___ \  | |___|  _  |/ ___ \ |___ / ___ \ 
          \___/ \___/ \____/_/   \_\  \____|_| |_/_/   \_\____/_/   \_\

*/

#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

#define MAX_CNX 64

typedef struct ma_liste_fd {
	struct ma_liste_fd *suivant;
	int mon_fd;
	} *p_liste;

p_liste tete_liste;

void add_to_list (int fd)
{
	p_liste temp, new;

	if (tete_liste)
		for (temp = tete_liste; temp->suivant != NULL; temp = temp->suivant)
			if (temp->mon_fd == fd)
				return ; 

	new = malloc(sizeof(struct ma_liste_fd));
	new->suivant = tete_liste;
	new->mon_fd = fd;

	tete_liste = new;
}

void rem_of_list (int fd)
{
	p_liste temp, old = NULL;

	if (!tete_liste) return;

	for (temp = tete_liste; temp->suivant != NULL; temp = temp->suivant)
	{
		if (temp->mon_fd == fd)
		{
			// ok on le retire.	
			if (old)
				old->suivant = temp->suivant;
			else
				tete_liste = temp->suivant;

			free (temp);
			return ;
		}
		old = temp;
	}

}

fd_set gen_my_list (int *fd)
{
	p_liste temp;
	fd_set lala;

	FD_ZERO (&lala);

	if (!tete_liste) return lala;

	*fd = tete_liste->mon_fd;

	for (temp = tete_liste; temp; temp = temp->suivant)
	{
		FD_SET(temp->mon_fd, &lala);

		if (temp->mon_fd > *fd) 
			*fd = temp->mon_fd;
	}

	return lala;
}

void list_all_list ()
{
	p_liste temp;
	
	if (tete_liste)
	for (temp = tete_liste; temp; temp = temp->suivant)
	{
		printf("--[ %i\n", 	temp->mon_fd);
	}
}

void my_debug (const char *format, ...)
{
	va_list args;
	time_t tmp;
	struct tm *tmp2;

	tmp = time(NULL);
	tmp2 = localtime (&tmp);
	printf("[%d:%.2d:%.2d] ", tmp2->tm_hour, tmp2->tm_min, tmp2->tm_sec);

	va_start (args, format);
	vprintf (format, args);
	va_end (args);
}

int listen_tcp_socket (struct sockaddr_in my_addr)
{
	int fd;

	if ( (fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
		perror("socket"), exit(1);

	if (bind (fd, (struct sockaddr*)&my_addr, sizeof(my_addr)) == -1)
		perror("bind"), exit(1);

	if (listen (fd, 128) == -1)
		perror("listen"), exit(1);
	
	return (fd);
}

int main (int argn, char **argv, char **envp)
{
	int fin = 0, r, i;
	int ls, len, ls_c, last_desc;
	int connections[MAX_CNX];
	char buffer[1024];

	fd_set m_fd;
	struct sockaddr_in my_addr;

	my_debug ("SYSTEM STARTING\n");

	if (argn != 2)
	{
		my_debug("USING IT IS : %s <PORT>\n", argv[0]);
		exit (1);	
	}

	my_addr.sin_family = AF_INET;
	my_addr.sin_addr.s_addr = INADDR_ANY;
	my_addr.sin_port = htons(atoi(argv[1]));
	len = sizeof(my_addr);

	ls = listen_tcp_socket (my_addr);
	if (ls <= 0)
	{
		my_debug("unable to bind anything ...\n");
		exit (1);
	}

	FD_ZERO (&m_fd);
	FD_SET (ls, &m_fd);
	last_desc = ls;
	memset (connections, 0, 64 * sizeof(int));

	add_to_list (ls);

	// problems to handle a disconnection
	// problems if timeout

	do
	{
		// ok, right here i've to wait for connections or datas.
		my_debug ("waiting for data/connections (%i) ...\n", last_desc);
		list_all_list();

		m_fd = gen_my_list(&last_desc);

		printf("%i %i\n", last_desc, FD_ISSET(ls, &m_fd));

		if ((r = select (last_desc + 1, &m_fd, NULL, NULL, NULL)) > 0)
		{
			perror("select");
			if (FD_ISSET(ls, &m_fd))
			{
				if ((ls_c=accept (ls, (struct sockaddr*)&my_addr, &len)) == -1)
					perror("accept"), exit(1);

				for (i = 0; i < 64 && connections[i]; i ++);

				if (i != 64)
				{
					FD_SET (ls_c, &m_fd);

					my_debug("assign %i to %i\n", ls_c, i);
					add_to_list (ls_c);

					connections[i] = ls_c;
					if (ls_c > last_desc) last_desc = ls_c;
				}
				else
					close (ls_c);
				
			} else {
				my_debug ("data sockets !\n");
				for (i = 0; i < 64; i ++)
				{
					if (connections[i] != 0)
					{
						if (FD_ISSET(connections[i], &m_fd))
						{
							my_debug("this socket: %i\n", connections[i]);
							if (read (connections[i], buffer, 1024) <= 0)
							{
								my_debug("fermeture de la connection %i (%i)\n",
									connections[i], i);

								rem_of_list (connections[i]);

							list_all_list();

								m_fd = gen_my_list(&last_desc);

								close(connections[i]);
								connections[i] = 0;
							}
							else
							{
								my_debug("data: [%s]\n", buffer);
								memset (buffer, 0, 1024);
							}

						break;
						}
					}
				}
			}
		}
		else
		{
			perror("select");
			my_debug("no select answer (timeout/error)\n");
		}
	}
	while (!fin);

	my_debug ("SYSTEM ENDING\n");
	exit (0);
}
