/*
 * Simple chat server.
 *
 * wildcat - 2004
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/select.h>
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>

#define MAX_CLIENT 5
#define MAX_TRIES  3

void syntax(char **);
void enter_loop(int *, int *);

void init_set(int *, int);
void delfromset(int *, int, int);
void dump_set(int *, int);
void addtoset(int *, int, int);

int sendtoall(int *, int, char *, int);

int current;

int main (int argc, char **argv)
{
	int ssock;		/*  serveur socket   */
	int csock[MAX_CLIENT];  /*  clients sockets  */
	int i;
	int port;
	
	struct sockaddr_in saddr;
	struct hostent     *n_saddr;

	if (argc != 3)
		syntax(argv);

	if ((port = atoi(argv[2])) == 0) {

		printf("Error, port incorrect...");
		return -1;
	}

	if ( (ssock = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
	
		printf("Error, cannot create socket..\n");
		return -1;
	}
	
	printf("Resolv %s..\n", argv[1]);
	if ( (n_saddr = gethostbyname(argv[1])) == NULL ) {
		perror("Unable to get ip");
		return -1;
	}

	saddr.sin_family = n_saddr->h_addrtype;
	memcpy(&saddr.sin_addr.s_addr, n_saddr->h_addr, n_saddr->h_length);
	saddr.sin_port = htons(port);

	/* binding */
	printf("Trying to bind %s:%d\n", argv[1], port);
	i=MAX_TRIES;
	do {
		printf("Try %d of %d...\n", MAX_TRIES - i + 1, MAX_TRIES);
		
		if (bind(ssock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) {
		
			perror("Cannot bind port");
			continue;
		}
		else /* we are binding port... */
		{
			listen(ssock, 5);
			printf("Good, we listen %s:%d !\n", argv[1], port);
			enter_loop(&ssock, &csock[0]);
			printf("Server shutdown.\n");
			return 1;
		}
		sleep(5); /* wait 5second before retrying */
		
	} while (--i) ;

	return 1;
}


void syntax (char **a)
{
  printf("Syntax: %s <ip> <port>\n", a[0]);
  exit(1);
}

void init_set(int *set, int nb)
{
	int i;
	for (i=0;i<nb;i++)
		*(set+i) = 0;
	return;
}

void dump_set(int *set, int max)
{
	int i;
	printf("Dump set:\n");
	for (i=0;i<max;i++)
		printf("\t%d socket -> %d\n", i, *(set + i));
}

void addtoset(int *set, int max, int v)
{
	int i;
	for (i=0;i<max;i++)
		if (*(set+i) == 0)
		{
			*(set+i) = v;
			break;
		}
	return;
}

void delfromset(int *set, int max, int v)
{
	int i;
	printf("Closing socket %d\n", v);
	close (v);
	for (i=0; i<max;i++)
		if (*(set+i) == v)
		{
			*(set+i) = 0;
			break;
		}
	return;
}

int sendtoall(int *set, int max, char *buf, int size)
{
	int i;
	printf("Sending to all: %s (%d)\n", buf, size);
	for (i=0;i<max;i++)
		if (*(set+i) != 0)
		{
			if (write(*(set+i), buf, size) == -1) {
				
				if (errno == EPIPE)
				{
					delfromset(set, max, *(set + i));
					current--;
				}
			}
		}
	return;
}

void enter_loop(int *ss, int *cs)
{
	int biggest = 0;
	fd_set c_set;
	fd_set s_set;
	struct timeval t_out, *to;	
	struct sockaddr c_addr;
	int s, len;
	int i, j, k, size;
	char buffer[100];


	init_set(cs, MAX_CLIENT);
	while(1)
	{
		FD_ZERO(&s_set);
		FD_SET(*ss, &s_set);
		if (!current)
			to = NULL;
		else
		{
			t_out.tv_sec  = 0;
			t_out.tv_usec = 500;
			to = &t_out;
		}
		if (select((*ss) + 1, &s_set, NULL, NULL, to) > 0) {
			
			/* client a accepter */	
			len = sizeof(c_addr);
			if ( (s = accept((*ss), (struct sockaddr *) &c_addr, &len)) == -1) {
	
				perror("Error accept");
			}
			else
			{
				printf("New client accepted\n");
				if (current == MAX_CLIENT)
				{
					printf("Server full closing connection\n");
					write (s, "Server Full", 13);
					close (s);
				}
				else
				{
					addtoset(cs,MAX_CLIENT ,s);
					current++;
				}
				dump_set(cs, MAX_CLIENT);
			}
		}
		
		/* see if we have sght to read from clients */
		
		if (current)
		{
			FD_ZERO(&c_set);
			for (i=0, biggest=0; i<MAX_CLIENT; i++)
			{
				if (*(cs + i) == 0) continue;

				FD_SET(*(cs + i), &c_set);
				if (*(cs + i) > biggest)
					biggest = *(cs + i);
			}
			/* set ok */
			t_out.tv_sec  = 0;
			t_out.tv_usec = 500;
			if ( (i = select (biggest + 1, &c_set, NULL, NULL, &t_out)) > 0) {

				/* sght to read from client */
				for (k=0,j=0 ; k < MAX_CLIENT && j < i ; k++)
				{
					if (FD_ISSET(*(cs + k), &c_set))
					{
						j++;
						/* read from this socket */
						bzero(buffer, sizeof(buffer));
						size = read(*(cs + k), &buffer, 100);
						if (!size) {
							delfromset(cs, MAX_CLIENT, *(cs + k));
							continue;
						}
						/* send the buffer read to all */
						sendtoall(cs, MAX_CLIENT, &buffer[0], size);
					}
				}
			}
		}
	}
}
