/********************************/
/*	sem_sock_serv.c	by IoDream	*/
/* sem_server socket server		*/
/*	 Not released yet			*/
/*       PUBLIC DOMAIN          */
/********************************/

/* IoDream (IoDream@ifrance.com) */

/* C++ compilers friendly */
#ifdef __cplusplus
extern "C" {
#endif /*__cplusplus*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h> /* for strcmp */
#include <signal.h>
#include "sem_server.h"
#include "j_net.h"

const char *mess_ok_answer = "OK\n";
const char *mess_wait_answer = "WAIT\n";
const char *mess_wait_format = "WAIT %d %d\n";
const char *mess_error_bad_request = "ERROR: Bad request\n";

int sigusr1_raised;

void sem_server_spawned(void *arg);
void sigusr1_handler(int s);

/* Main semaphore socket server function. */
void sem_sock_server(void)
{
	short local_port_num = SEM_LISTENING_PORT;
	int	local_sock;
	FILE *fd;

	signal(SIGUSR1, SIG_IGN);
	signal(SIGUSR2, SIG_IGN);
	signal(SIGHUP, SIG_IGN);
	local_sock = j_net_create_new_socket(&local_port_num);
	if (local_port_num == -1)
		printf("sem_sock_server-:  Port number is unknown !\n");
	else
	{
		printf("sem_sock_server-:  Port number is %d\n", local_port_num);
		fd = fopen(SEM_SERVER_PORT_FILENAME, "w");
		if (fd != NULL)
		{
			fprintf(fd, "%d", local_port_num);
			fclose(fd);
		}
	}

	j_net_run_server(local_sock, SEM_MAXNB_CLIENTS,	&sem_server_spawned);
	unlink(SEM_SERVER_PORT_FILENAME);

	return;
}

/* Spawned processus function that is called when a connection is accepted. */
void sem_server_spawned(void *arg)
{
	char message[SEM_MAXLEN_MESSAGE];
	int client_sock = *((int *) arg);
	int	len_message;
	int num, id;

	DEBUG_(printf("sem_sock_server-:  New Client connected\n");)

	/* Reception du message du client */
	memset(message,	0, sizeof(message));
	len_message	= j_net_read_max(client_sock, (void *) message, sizeof(message));
	if ((len_message <=	0) || ((len_message	== 1) && (message[0] == 4)))
	{
		DEBUG_(printf("sem_sock_server-:  Client disconnected\n");)
		if (j_net_close_socket(client_sock)	< 0)
		{
			perror("sem_sock_server-:  j_net_close_socket()");
		}
		return;
	}
	DEBUG_(printf("sem_sock_server-:  Message recu du client: %s", message);)
	num = parse_request(message, &id, NULL);
	if (num < 0)
	{
		DEBUG_(fprintf(stderr, "sem_sock_server-:  Bad request error from client\n");)
		(void) j_net_write_all(client_sock,	mess_error_bad_request, strlen(mess_error_bad_request));
		(void) sleep(1); /* TODO: to improve */
		if (j_net_close_socket(client_sock)	< 0)
		{
			perror("sem_sock_server-:  j_net_close_socket()");
		}
		return;
	}

	/* Envoi du message vers le gestionnaire */
	if (num == SEM_REQ_WAIT)
	{
		memset(message,	0, sizeof(message));
		sprintf(&message[0], mess_wait_format, id, getpid());
	}
	lock_mutex(mutex);
	DEBUG_(printf("sem_sock_server-:  Message envoye au gestionnaire: %s", message);)
	(void) j_net_write_all(pipePS2GE[1], (void *) message, strlen(message));

	/* Reception du message du gestionnaire */
	if (num != SEM_REQ_RESET)
		{
		memset(message,	0, sizeof(message));
		len_message	= j_net_read_max(pipeGE2PS[0], (void *) message, sizeof(message));
		unlock_mutex(mutex);
		if ((len_message <=	0) || ((len_message	== 1) && (message[0] == 4)))
		{
			DEBUG_(printf("sem_sock_server-:  Sem_handler disconnected !!\n");)
			if (j_net_close_socket(client_sock)	< 0)
			{
				perror("sem_sock_server-:  j_net_close_socket()");
			}
			return;
		}
		DEBUG_(printf("sem_sock_server-:  Message recu du gestionnaire: %s", message);)
		if (strcmp(mess_wait_answer, message) == 0)
		{
			DEBUG_(printf("sem_sock_server-:  c'est un WAIT => on attends on OK du gestionnaire\n");)
			sigusr1_raised = FALSE;
			(void) signal(SIGUSR1, sigusr1_handler);
			while (!sigusr1_raised)
				(void) pause();
			memset(message,	0, sizeof(message));
			len_message	= j_net_read_max(pipeGE2PS[0], (void *) message, sizeof(message));
			unlock_mutex(mutex);
			if ((len_message <=	0) || ((len_message	== 1) && (message[0] == 4)))
			{
				DEBUG_(printf("sem_sock_server-:  Sem_handler disconnected !!\n");)
				if (j_net_close_socket(client_sock)	< 0)
				{
					perror("sem_sock_server-:  j_net_close_socket()");
				}
				return;
			}
			DEBUG_(printf("sem_sock_server-:  Message recu du gestionnaire: %s", message);)
		}

		/* Envoi du message vers le client */
		DEBUG_(printf("sem_sock_server-:  Message envoye au client: %s", message);)
		(void) j_net_write_all(client_sock,	(void *) message, len_message);
	}

	/* Fermeture de la connexion */
	(void) sleep(1); /* TODO: to improve */
	DEBUG_(printf("sem_sock_server-:  Disconnecting from client\n");)
	if (j_net_close_socket(client_sock)	< 0)
	{
		perror("sem_sock_server-:  j_net_close_socket()");
	}
	return;
}

/* Handler of the SIGUSR1 signal. Only used internally in sem_gest() and sem_server_spawned() synchronisation. */
void sigusr1_handler(int s)
{
	sigusr1_raised = TRUE;
	return;
}

/* C++ compilers friendly */
#ifdef __cplusplus
}
#endif /*__cplusplus*/
