/***************************/
/*  sem_gest.c by IoDream  */
/* sem_server sem handler  */
/*   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 <signal.h>
#include "sem_server.h"

/* Linked list structure to store PIDs */
struct linked_list_pid
{
	pid_t pid;
	struct linked_list_pid *next_pid;
};
typedef struct linked_list_pid ll_pid;

ll_pid *sem[SEM_MAXID];
int created[SEM_MAXID];
int blocked[SEM_MAXID];

/* Message texts to be sent */
const char mess_ok[] = "OK\n";
const char mess_wait[] = "WAIT\n";
const char mess_error1[] = "ERROR: semaphore already created\n";
const char mess_error2[] = "ERROR: semaphore not created yet\n";
const char mess_error3[] = "ERROR: semaphore id out of range (max is %d)\n";

/* Function prototypes */
void send_message2PS(const char *mess);
void sem_gest_reset(void);
void sigusr2_handler(int s);
void sighup_handler(int s);

/* Main semaphore handler function. */
void sem_gest(void)
{
	char message[SEM_MAXLEN_MESSAGE];
	int num, id;
	pid_t pid;
	ll_pid *p_temp, *p_aux;
	FILE *fd;

	for (id = 0; id < SEM_MAXID; ++id)
	{
		sem[id] = NULL;
		created[id] = FALSE;
		blocked[id] = SEM_SEMINIT;
	}

	signal(SIGUSR1, SIG_IGN);
	signal(SIGUSR2, sigusr2_handler);
	signal(SIGHUP, sighup_handler);
	printf("sem_gest-:  sem_handler pid is %d\n", getpid());
	fd = fopen(SEM_SERVER_PID_FILENAME, "w");
	if (fd != NULL)
	{
		fprintf(fd, "%d", getpid());
		fclose(fd);
	}

	for (;;)
	{
		if (read_max(pipePS2GE[0], (void *) &message, SEM_MAXLEN_MESSAGE) < 0)
		{
			perror("sem_gest-:  read_max()");
			exit(1);
		}
		num = parse_request(message, &id, &pid);
		switch (num)
		{
			case SEM_REQ_CREATE:
				DEBUG_(printf("sem_gest.c-:  request is SEM_REQ_CREATE\n");)
				if ((id < 0) || (SEM_MAXID <= id))
				{
				    sprintf(message, mess_error3, SEM_MAXID - 1);
					send_message2PS(message);
				}
				else if (created[id])
					send_message2PS(mess_error1);
				else
				{
					send_message2PS(mess_ok);
					created[id] = TRUE;
				}
				break;
			case SEM_REQ_DESTROY:
				DEBUG_(printf("sem_gest.c-:  request is SEM_REQ_DESTROY\n");)
				if ((id < 0) || (SEM_MAXID <= id))
				{
				    sprintf(message, mess_error3, SEM_MAXID - 1);
					send_message2PS(message);
				}
				else if (!created[id])
					send_message2PS(mess_error2);
				else
				{
					while (sem[id] != NULL)
					{
						p_aux = sem[id]->next_pid;
						free(sem[id]);
						sem[id] = p_aux;
					}
					created[id] = FALSE;
					blocked[id] = 0;
					send_message2PS(mess_ok);
				}
				break;
			case SEM_REQ_WAIT:
				DEBUG_(printf("sem_gest.c-:  request is SEM_REQ_WAIT\n");)
				if ((id < 0) || (SEM_MAXID <= id))
				{
				    sprintf(message, mess_error3, SEM_MAXID - 1);
					send_message2PS(message);
					break;
				}
				if (!created[id])
				{
					send_message2PS(mess_error2);
					break;
				}
				if (blocked[id] > 0)
				{
					--blocked[id];
					send_message2PS(mess_ok);
					break;
				}
				p_temp = malloc(sizeof(ll_pid));
				p_temp->pid = pid;
				p_temp->next_pid = NULL;
				if (sem[id] == NULL)
					sem[id] = p_temp;
				else
				{
					p_aux = sem[id];
					while (p_aux->next_pid != NULL)
						p_aux = p_aux->next_pid;
					p_aux->next_pid = p_temp;
				}
				send_message2PS(mess_wait);
				break;
			case SEM_REQ_RELEASE:
				DEBUG_(printf("sem_gest.c-:  request is SEM_REQ_RELEASE\n");)
				if ((id < 0) || (SEM_MAXID <= id))
				{
				    sprintf(message, mess_error3, SEM_MAXID - 1);
					send_message2PS(message);
					break;
				}
				if (!created[id])
				{
					send_message2PS(mess_error2);
					break;
				}
				send_message2PS(mess_ok);
				++blocked[id];
				while ((blocked[id] > 0) && (sem[id] != NULL))
				{
					pid = sem[id]->pid;
					p_aux = sem[id]->next_pid;
					free(sem[id]);
					sem[id] = p_aux;
					--blocked[id];
					lock_mutex(mutex);
					send_message2PS(mess_ok);
					kill(pid, SIGUSR1);
				}
				break;
			case SEM_REQ_RESET:
				DEBUG_(printf("sem_gest.c-:  request is SEM_REQ_RESET\n");)
				sem_gest_reset();
				break;
			default:
				DEBUG_(fprintf(stderr, "sem_gest-:  Bad request from server processus\n");)
				break;
		}
	}

	unlink(SEM_SERVER_PID_FILENAME);
	return;
}

/* Shortcut to send the message mess to the socket server processus. */
void send_message2PS(const char *mess)
{
	if (mess == NULL)
	{
		DEBUG_(fprintf(stderr, "sem_gest-:  send_message argument is NULL\n");)
		return;
	}
	DEBUG_(printf("sem_gest-:  envoi au processus serveur du message: %s", mess);)
	if (write_all(pipeGE2PS[1], (void *) mess, strlen(mess)) < 0)
	{
		perror("sem_gest-:  write_all in send_message");
		exit(1);
	}

	return;
}

/* Make a reset of all the semaphores */
void sem_gest_reset(void)
{
	int id;
	ll_pid *p_aux;

	DEBUG_(printf("sem_gest-:  Resetting\n");)
	for (id = 0; id < SEM_MAXID; ++id)
	{
		while (sem[id] != NULL)
		{
			kill(sem[id]->pid, SIGUSR1);
			p_aux = sem[id]->next_pid;
			free(sem[id]);
			sem[id] = p_aux;
		}
		created[id] = FALSE;
		blocked[id] = SEM_SEMINIT;
	}
	unlock_mutex(mutex);
	return;
}

/* Handler of the SIGUSR2 signal. Used to print semaphore status. */
void sigusr2_handler(int s)
{
	int id, nb;
	ll_pid *p_temp;

	printf(" ---=== Semaphore handler status ===---\n");
	for (id = 0; id < SEM_MAXID; ++id)
		if (!created[id])
			printf("%4d: not created\n", id);
		else
		{
			printf("%4d: created, value is %d, ", id, blocked[id]);
			nb = 0;
			p_temp = sem[id];
			while (p_temp != NULL)
			{
				p_temp = p_temp->next_pid;
				++nb;
			}
			if (nb == 0)
				printf("no pending element\n");
			else
			{
				printf("%d pending element%c:", nb, (nb == 1)?' ':'s');
				p_temp = sem[id];
				while (p_temp != NULL)
				{
					printf(" %d", p_temp->pid);
					p_temp = p_temp->next_pid;
				}
				printf("\n");
			}
		}

	signal(SIGUSR2, sigusr2_handler);

	return;
}

/* Handler of the SIGHUP signal. Used to invoke reset. */
void sighup_handler(int s)
{
	sem_gest_reset();
	signal(SIGHUP, sighup_handler);
	return;
}

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