#include <sys/types.h>		// includo le librerie di interesse
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <signal.h>
#include <sys/stat.h>
#include "mio.h"

//prototipi di funzioni
void gestoreSig1(int);		// funzione per gestire la terminazione di un figlio
void gestoreSig2 (int);		// funzione per chiudere i figli
void gestoreUscita (int);	// gestisco l'uscita Control +C
void salvataggio ();		// salvo in un unico file i numeri trovati

int figli_necessari=0;  // numero che dipende dall'intervallo e dal quanto numero
int num_figli =0;       // è uguale al numero di client che si è connesso

int main (int argc, char *argv[])
{
	int ds_sock, ds_sock_acc;
	struct sockaddr_in server;		// struttura del server
	struct sockaddr client;			// struttura del client
	int lunghezza;				// misura in byte della struttura client effettivamente usati
	char ip_server [20]="128.0.0.0";	//IP del server
	char buffer[BUF_DIM];  			//per trasmettere messaggi
	long numero;				// per trasmettere i numeri dell'intervallo
	int porta_TCP;				// porta di comunicazione
	long primo_numero, ultimo_numero,inf,sup; // primo e ultimo numero dell'intervallo da analizzare
	long quanto;				// dimensione dei pacchetti
	int pid;				// pid del figlio
	int controlla = 0;			// misura dell'intervallo controllato
	FILE *fp;
	char nome_file [12];	// nome dei file creati dai figli


	if (argc<5)		// controllo il numero di argomenti inseriti
		{
		printf ("Inserire %s <porta_TCP> <primo_intero> <secondo_intero> <dim_pacchetti>\n",argv[0]);
		exit(-1);
		}

	porta_TCP = atoi (argv[1]);		// inizializzo le variabili con gli argomenti inseriti
	primo_numero= atol(argv[2]);
	ultimo_numero = atol (argv[3]);
	quanto = atol (argv[4]);

	if (primo_numero<2)			// controllo alcune caratteristiche dei numeri introdotti
	{
	printf ("<primo_numero> deve essere maggiore di 1\n");
	exit(-1);
	}

	if (primo_numero >= ultimo_numero)	// controllo l'ordine dell'intervallo
	{
	printf ("<ultimo_numero> deve essere maggiore di <primo_numero>\n");
	exit(-1);
	}


	figli_necessari = (ultimo_numero-primo_numero)/quanto;	// rapporto tra interi è intero

	if (((ultimo_numero-primo_numero)%quanto) !=0) figli_necessari++; // ho un figlio che lavorerà con meno di un quanto

	printf("\n");
	printf("Premere Control + C per uscire\n");

	if  ((ds_sock = socket(AF_UNIX,SOCK_STREAM,0))==-1)	// creo il soket
		{
		printf ("Errore di socket\n");
		printf ("Impossibile avviare il Server\n");
		exit(-1);
		}

	server.sin_family = AF_UNIX;		// imposto la struttura dati del socket
	server.sin_port = porta_TCP;
	server.sin_addr.s_addr = inet_addr(ip_server); //converto l'indirizzo da stringa in unsigned long

	printf ("ds server %d\n",ds_sock);				// visualizzo impostazioni socket
	printf ("Server IP : %s \n",inet_ntoa (server.sin_addr.s_addr));
	printf ("Porta di connessione : %d\n",porta_TCP);
	printf ("Numero figli necessari : %d\n",figli_necessari);


	if((bind (ds_sock, &server, sizeof(server)))==-1)		// effettuo bind del socket creato
		{	
		printf ("Bind fallito \n");
		printf ("Impossibile avviare il Server\n");
		exit(-1);
		}

	if ((listen (ds_sock,CODA_DIM)) ==-1)			//imposto la coda dei client
		printf("Impossibile accodare client\n");

	while(2)	//ciclo infinito
	{	printf("*******************************************\n");
		printf("Sono in attesa di connessioni \n");
		while ((ds_sock_acc = accept (ds_sock, &client, &lunghezza))==-1); // attendo connessione client
		printf("Un client si è connesso\n");
		printf ("ds client %d\n",ds_sock_acc);
  		
		if (controlla >= (ultimo_numero-primo_numero) )		// se l'intervallo è controllato blocco i client
			write(ds_sock_acc,"fine",BUF_DIM);		// avviso dello stato con il messaggio fine

		else	// asssegno ad un altro client il pacchetto e risorse necessarie
		{
			num_figli++; 			// aumento il numero dei figli attivati = client accettati
			controlla = quanto*num_figli;	// imposto l'intervallo controllato
			inf = primo_numero + quanto*(num_figli-1);
			sup = inf+quanto-1;
			if (sup> ultimo_numero) sup= ultimo_numero;	// non controllo numeri oltre l'intervallo;

			signal (SIGUSR1,gestoreSig1); 		// armo il segnale
			
			pid = fork();  // se l'intervallo è completo non creo altri figli
			
			if (pid == -1)
				{ 
				printf ("Impossibile creare un nuovo figlio \n");
				exit (-1);	// non attendo il liberarsi delle risorse di sistema
				}

			if (pid!=0)
				{
				signal (SIGUSR2,SIG_IGN);	// ignoro il segnale 2 di utente (chiude i figli)
				signal (SIGINT,gestoreUscita);  // armo il segnale di uscita per i salvataggi necessari
				}
								
			else					//sono il figlio
			{	close (ds_sock);
				signal (SIGUSR2,gestoreSig2);		//armo il segnale
				read(ds_sock_acc,buffer,BUF_DIM);
				printf("messaggio ricevuto %s \n",buffer);
				write(ds_sock_acc,"OK",BUF_DIM);	// invia il messaggio OK al client
				write(ds_sock_acc,&inf,sizeof(inf));	// invia l'intervallo al client
				write(ds_sock_acc,&sup,sizeof(sup));

				sprintf (nome_file,"%d",num_figli);	// creo nome file con indice del figlio (univoco)

				if ((fp=fopen(nome_file, "w")) == NULL)
					{
					printf ("Impossibile aprire il file \n");
					exit(-1);
					}

				do	// scrivo nel file i numeri inviati dal client
				{
				read(ds_sock_acc,&numero,sizeof(numero));
				if (numero!= 0) fprintf (fp,"%d ",numero);  // con 0 il client segnala la fine dell'analisi pacchetto
				}
				while (numero!=0);
				
				// chiudo il file aperto dal figlio

				if ((fclose (fp))== -1 ) printf ("Impossibile chiudere file %s \n", nome_file);
				
				if ((close(ds_sock_acc)) == -1) printf ("Impossibile chiudere soket\n");

				while (kill(getppid(),SIGUSR1)==-1);	// invio al padre il segnale usr1
									// segnalando fine del figlio
				exit (0); // termine del figlio
			}
		}
	} // ritorno all'inizio del ciclo infinito
}

// gestisco le azioni da compiere prima di uscire
void gestoreUscita (int sig)
{
	kill (0,SIGUSR2);		// invio il segnale a tutti i figli, il padre lo ignora
	salvataggio();
	exit(1);
}


//gestisco i figli che terminano
void gestoreSig1 (int sig)
{	static figli_terminati = 0;	// inizializzo la variabile solo la prima volta

	figli_terminati++;
	printf ("Figlio è terminato %d°°°°°°°°°\n",figli_terminati);
	if (figli_terminati == figli_necessari)
		salvataggio();
}

// gestisco la chiusura del figlio sotto il volere del padre
void gestoreSig2 (int sig)
{	printf("Figlio PID : %d chiuso\n",getpid());
	exit(-1);	// chiudo il figlio

}

// effettuo il salvataggio dei file nell'unico file FINAL.DAT
void salvataggio ()
{	int i,j=0;	// i contatore file letti     j contatore numeri scritti per andare poi a capo
	int numero;		// per la lettura dei numeri nei file
	char nome_file [12];
	FILE *fp,*fp_finale;

		printf("___________________________________________\n");
		printf("I CLIENT HANNO TERMINATO L'ELABORAZIONE\n");
		printf("\n");
		printf("exit file : FINAL.DAT\n");
		printf("___________________________________________\n");

		if ((fp_finale=fopen("FINAL.DAT", "w")) == NULL)
			{
			printf ("Impossibile salvare i risultati\n");
			exit(-1);
			}

		for (i=1; i <=num_figli; i++)
		{

		sprintf (nome_file,"%d",i);			// creo il nome del file dall'indice


		if ((fp=fopen(nome_file, "r")) == NULL)
			{
			printf ("Impossibile aprire file per salvataggio finale %s\n",nome_file);
			 continue;
			}

		while (fscanf(fp,"%d",&numero)!=EOF)
			{
			if ((++j%10)==0) fprintf(fp_finale,"\n");  // salto una riga ogni 10 numeri scritti
			fprintf(fp_finale,"%d ",numero);  // trasferisco il contenuto dei file in FINAL.DAT

			}
		
		close(fp);

		//cancella il file letto
		if ((remove(nome_file)) == -1 ) printf ("Impossibile rimuovere il file %s \n",nome_file);
		
		}
		close(fp_finale);
	exit(0);
}