#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>

//DEKLARACJE FUNKCJI, STRUKTUR, ZMIENNYCH
struct wiadomosc{
	long typkomunikatu;
	char wiad[500];
	};
struct wiadomosc2{
	long typkomunikatu;
	int liczba_znakow;
};
key_t idkolejki[2];
int wczytaj_znaki(char *znaki);
int desk1,desk2,desk3,pauza_p1=0,pauza_p2=0,pauza_p3=0,pob=0,oczekujacysygnal=0;

//OBSLUGA SYGNALOW
// SYGNALY DLA LACZ NAZWANYCH KOLEJEK FIFO
void sm_obsluga(){ };
void sm_term(){
printf("** PM: Odebralem SIGTERM od P3.\n");
if(write(desk1,"t",2)==-1) printf("** PM: Blad zapisu term do fifo1.\n");
if(write(desk2,"t",2)==-1) printf("** PM: Blad zapisu term do fifo2.\n"); 
if(write(desk3,"t",2)==-1) printf("** PM: Blad zapisu term do fifo3.\n"); 
if(!kill(getpid()+1,SIGUSR2)) printf("** PM: Wysylam SIGUSR2 do P1.\n");
sleep(1);
close(desk1);
close(desk2);
close(desk3);
}
void sm_cont(){
printf("** PM: Odebralem SIGCONT od P3.\n");
printf("** PM: Wysylam SIGUSR2 do P1.\n");
if(write(desk1,"c",2)==-1) printf("** PM: Blad zapisu cont do fifo1.\n");
if(write(desk2,"c",2)==-1) printf("** PM: Blad zapisu cont do fifo2.\n"); 
if(write(desk3,"c",2)==-1) printf("** PM: Blad zapisu cont do fifo3.\n"); 
if(!kill(getpid()+1,SIGUSR2)) printf("** PM: Wysylam SIGUSR2 do P1.\n");
}
void sm_tstp(){
printf("** PM: Odebralem sygnal SIGTSTP od P3\n");
if(write(desk1,"s",2)==-1) printf("** PM: Blad zapisu tstp do fifo1.\n");
if(write(desk2,"s",2)==-1) printf("** PM: Blad zapisu tstp do fifo2.\n"); 
if(write(desk3,"s",2)==-1) printf("** PM: Blad zapisu tstp do fifo3.\n"); 
if(!kill(getpid()+1,SIGUSR2)) printf("** PM: Wysylam sygnal SIGUSR2 do P1\n");
printf("** PM: Zasypiam....\n");
pause();
}
void s0_signal(){
sleep(1);
char buf[2];
if(read(desk1,buf,2)==-1) printf("** P1: Blad odczytu z fifo1.\n");
	else printf("** P1: Odczytałem z fifo1 desk1=%d wartosc=%c\n",desk1,buf[0]);
if(pob && buf[0]==116){
oczekujacysygnal=1;
} else{
printf("** P1: sygnal obslugi odczytu z FIFO1\n");
if(!kill(getpid()+1,SIGUSR2)) printf("** P1: Wysylam SIGUSR2 do P2.\n");
if(buf[0]==115){
	printf("** P1: Zasypiam....\n");
	pauza_p1=1;
	}
if(buf[0]==99){
pauza_p1=0;
return; }
if(buf[0]==116){ exit(0); }
}
}
void s1_signal(){
sleep(1);
printf("** P2: sygnal obslugi odczytu z FIFO2\n");
char buf[2];
if(read(desk2,buf,2)==-1) printf("** P2: Blad odczytu z fifo2.\n");
	else printf("** P1: Odczytałem z fifo2 desk2=%d wartosc=%c\n",desk2,buf[0]);
if(!kill(getpid()+1,SIGUSR2)) printf("** P2: Wysylam SIGUSR2 do P3.\n");
if(buf[0]==115){
	printf("** P2: Zasypiam....\n");
	pauza_p2=1;		
	}
if(buf[0]==99){
pauza_p2=0;
return; }
if(buf[0]==116){ exit(0); }
}
void s2_signal(){
sleep(1);
printf("** P3: sygnal obslugi odczytu z FIFO3\n");
char buf[2];
if(read(desk3,buf,2)==-1) printf("** P3: Blad odczytu z fifo3.\n");
	else printf("** P1: Odczytałem z fifo3 desk3=%d wartosc=%c\n",desk3,buf[0]);
if(buf[0]==115){
	printf("** P3: Zasypiam....\n");
	pauza_p3=1;
	}
if(buf[0]==99){ 
pauza_p3=0;
return; }
if(buf[0]==116){ 
	if(!unlink("fifo1")) printf("kolejke fifo1 usunieto pomyslnie!\n");
	if(!unlink("fifo2")) printf("kolejke fifo2 usunieto pomyslnie!\n");
	if(!unlink("fifo3")) printf("kolejke fifo3 usunieto pomyslnie!\n");	
exit(0); }
}
void p3_sigterm(){
printf("** P3: odebralem sygnal SIGTERM z zewnatrz!\n");
if(!kill(getppid(),SIGTERM)) printf("** P3: pomyslnie wyslano sygnal do PM\n"); else printf("** P3: problem z wyslaniem sygnalu do PM\n");
}
void p3_sigcont(){
printf("** P3: odebralem sygnal SIGCONT z zewnatrz!\n");
if(!kill(getppid(),SIGCONT)) printf("** P3: pomyslnie wyslano sygnal do PM\n"); else printf("** P3: problem z wyslaniem sygnalu do PM\n");
}
void p3_sigtstp(){
printf("** P3: odebralem sygnal SIGTSTP z zewnatrz!\n");
if(!kill(getppid(),SIGTSTP)) printf("** P3: pomyslnie wyslano sygnal do PM\n"); else printf("** P3: problem z wyslaniem sygnalu do PM\n");
}
//SYGNALY DLA KOLEJEK KOMUNIKATOW
void s1_obsluga(){
int i=0,msgerror;
struct wiadomosc kom1;
struct wiadomosc2 kom2;
printf("** obsluga sygnalu S1!!\n");
if(msgrcv(idkolejki[0],&kom1,500,0,0)) printf("** P1: otrzymalem komunikat z kolejki id=%d\n",idkolejki[0]);
printf("** P2: otrzymalem komunikat o tresci: \n");
printf(">> %s\n",kom1.wiad);
i=strlen(kom1.wiad);
printf("** wiersz ma %d znakow\n",i);
kom2.typkomunikatu=1;
kom2.liczba_znakow=i;
if(!(msgerror=msgsnd(idkolejki[1],&kom2,4,0))) printf("** P2: komunikat wyslano poprawnie\n");
if(msgerror==-1) printf("** P2: BLAD WYSYLANIA KOMUNIKATU! kod err: %s\n",strerror(errno));
if(!kill(getpid()+1,SIGUSR1)) printf("** P2: sygnal do %d wyslany poprawnie\n",getpid()+1);

}
void s2_obsluga(){
struct wiadomosc2 kom2;
if(msgrcv(idkolejki[1],&kom2,4,0,0)) printf("** S3! otrzymalem komunikat z kolejki id=%d\n",idkolejki[1]);
printf("** P3: otrzymalem komunikat o tresci -liczba znakow w wierszu=: \n");
printf(">> %d\n",kom2.liczba_znakow);
}
// --------PROCES 1---------//
void proc1(void){
	    signal(SIGTERM,SIG_IGN);
	    signal(SIGTSTP,SIG_IGN);
	    signal(SIGCONT,SIG_IGN);
	signal(SIGUSR2,s0_signal);
if(mkfifo("fifo1",0700)==-1) printf("** P1: blad przy tworzeniu fifo1\n"); else printf("** P1: pomyslnie utworzylem fifo1\n");
desk1=open("fifo1",O_RDONLY);
if(desk1==-1) printf("** P1: nie moge otworzyc fifo1\n");

printf("** P1: moj PID=%d\n",getpid()); 
	//deklaracja zmiennych
	char znaki[500],key[2];
	int j,k,msgerror,msgerror1;
	struct wiadomosc komunikat;
	char buf[2];
idkolejki[0]=msgget(111, IPC_CREAT|0666);
idkolejki[1]=msgget(222, IPC_CREAT|0666);
printf("** P1: STWORZYLEM KOLEJKE, idkolejki[0] = %d\n",idkolejki[0]);
while(1){
printf("\n--------\n--Menu--\n--------\n");
printf("1.Z klawiatury\n");
printf("2.Z pliku\n");
printf("3.Zakoncz program\n\nTwoj wybor: >\n");
scanf("%s",key);
printf("** Wybrano opcje: %s\n",key);
//-- Z KLAWIATURY
if(key[0]=='1'){

	while(1){printf("** wpisuj wiersze\n");
		scanf("%s",znaki);

		if(znaki[0]=='.') break;
printf("** Znaki odczytane.\n");
		//WYSYLANIE
		komunikat.typkomunikatu=1;
		for(j=0;j<500;j++) komunikat.wiad[j]=znaki[j];
		if(!(msgerror=msgsnd(idkolejki[0],&komunikat,500,0))) printf("\n** P1: komunikat wyslano poprawnie\n");
if(msgerror==-1) printf("**P1: BLAD WYSYLANIA KOMUNIKATU! kod err: %s\n",strerror(errno));
		if(!kill(getpid()+1,SIGUSR1)) printf("** P1: sygnal do %d wyslany poprawnie\n",getpid()+1);
	};

}
if(key[0]=='2'){
		char filename[20];
		FILE *file1;
		printf("** P1: Podaj nazwe pliku: >\n");
		scanf("%s",filename);
		if((file1=fopen(filename,"r"))==NULL) 
		{
		printf("**P1: (!) Nie ma takiego pliku\n");
		}
		else{
			pob=1; 
			while(pob){
				fgets(znaki, 500, file1);
if(pauza_p1){ pause(); printf("** P1: >>OBUDZILEM SIE<<\n"); }
				if(znaki[0]=='.') pob=0;
				else{
		//WYSYLANIE
		komunikat.typkomunikatu=1;
		for(j=0;j<500;j++) komunikat.wiad[j]=znaki[j];
		if(!(msgerror=msgsnd(idkolejki[0],&komunikat,500,0))) printf("** P1: komunikat wyslano poprawnie\n");
if(msgerror==-1) printf("** P1: BLAD WYSYLANIA KOMUNIKATU! kod err: %s\n",strerror(errno));
	if(!kill(getpid()+1,SIGUSR1)) printf("P1: sygnal do %d wyslany poprawnie\n",getpid()+1);
sleep(2);
			}} 
pob=0;
if(oczekujacysygnal){ 
printf("** P1: sygnal obslugi odczytu z FIFO1\n");
if(!kill(getpid()+1,SIGUSR2)) printf("** P1: Wysylam SIGUSR2 do P2.\n");
exit(0);
}
oczekujacysygnal=0;
}	
}
if(key[0]=='3'){
printf("\n\nZAMYKANIE PROGRAMU\n\n");
//usuwanie kolejek komunikatow!!!!1
if(!(msgerror1=msgctl(idkolejki[0],IPC_RMID,NULL))) printf("** P1: pomyslnie usunieto kolejke o id=%d\n",idkolejki[0]);
if(msgerror1==-1) printf("** P1: blad usuwania kolejki! kod err: %s\n",strerror(errno));
if(!(msgerror1=msgctl(idkolejki[1],IPC_RMID,NULL))) printf("** P1: pomyslnie usunieto kolejke o id=%d\n",idkolejki[1]);
if(msgerror1==-1) printf("** P1: blad usuwania kolejki! kod err: %s\n",strerror(errno));
//TERMINATE!!__!_!_!_!__!_!
kill(getpid()+2,SIGTERM);
break; 
}
};
while(1){ if(pauza_p1){ pause(); printf("** P1: >>OBUDZILEM SIE<<\n"); } }
}
//---------PROCES 2-----------
void proc2(void){ 
	    signal(SIGTERM,SIG_IGN);
	    signal(SIGTSTP,SIG_IGN);
	    signal(SIGCONT,SIG_IGN);
    signal(SIGUSR1,s1_obsluga);
	signal(SIGUSR2,s1_signal);
if(mkfifo("fifo2",0700)==-1) printf("** P2: blad przy tworzeniu fifo2\n"); else printf("** P2: pomyslnie utworzylem fifo2\n");
desk2=open("fifo2",O_RDONLY);
if(desk2==-1) printf("** P2: nie moge otworzyc fifo1\n");

printf("** P2: moj PID=%d\n",getpid());
int x;
 idkolejki[0]=msgget(111, IPC_CREAT|0666);
 idkolejki[1]=msgget(222, IPC_CREAT|0666);
printf("** P2: STWORZYLEM KOLEJKE idkolejki[1] = %d\n",idkolejki[1]);



while(1){ if(pauza_p2){ pause(); printf("** P2: >>OBUDZILEM SIE<<\n"); } }
}
//-----------------_PROCES 3----------------
void proc3(void){ 
	signal(SIGUSR1,s2_obsluga);
	signal(SIGUSR2,s2_signal);
	signal(SIGTERM,p3_sigterm);
	signal(SIGTSTP,p3_sigtstp);
	signal(SIGCONT,p3_sigcont);
if(mkfifo("fifo3",0700)==-1) printf("** P3: blad przy tworzeniu fifo3\n"); else printf("** P3: pomyslnie utworzylem fifo3\n");
desk3=open("fifo3",O_RDONLY);
if(desk3==-1) printf("** P3: nie moge otworzyc fifo3\n");

idkolejki[1]=msgget(222, IPC_CREAT|0666);
 printf("** P3: moj PID=%d\n",getpid());


while(1){ if(pauza_p3){ pause(); printf("** P3: >>OBUDZILEM SIE<<\n"); } }
}
//-------------P:ROCES MACIERZYSTY ---------------
void proc_m(void){
printf("** PM: moj PID=%d\n",getpid());
desk1=open("fifo1",O_WRONLY);
if(desk1==-1) printf("** PM: nie moge otworzyc fifo1\n");
	else printf("** PM: otworzylem kolejke fifo1, desk1=%d\n",desk1);
desk2=open("fifo2",O_WRONLY);
if(desk2==-1) printf("** PM: nie moge otworzyc fifo2\n");
	else printf("** PM: otworzylem kolejke fifo2, desk2=%d\n",desk2);
desk3=open("fifo3",O_WRONLY);
if(desk3==-1) printf("** PM: nie moge otworzyc fifo3\n");
	else printf("** PM: otworzylem kolejke fifo3, desk3=%d\n",desk3);
signal(SIGUSR1,sm_obsluga);
signal(SIGTERM,sm_term);
signal(SIGCONT,sm_cont);
signal(SIGTSTP,sm_tstp);
}
int main(int argc, char *argv[]){
pid_t p[3];

switch(p[0]=fork())
{
case -1 :{
	printf("\t\tNie moge utworzyc procesu!!\n");
	break;
	}
	
case 0  :{
	proc1();
	break;
	}

default:{
	switch(p[1]=fork())
			{
		case -1 :{
			printf("\t\tNie moge utworzyc procesu\n");
			break;
			}
		case 0  :{			
			proc2();
			break;
			}
		default:{
        		switch(p[2]=fork())
                			{
                		case -1 :{
                        		printf("\t\tNie moge utworzyc procesu\n");
                        		break;
                        		}
                		case 0  :{
                        		proc3();
                        		break;
					}
                		default:{
					proc_m();
					wait();
					wait();
					wait();
                        		break;
					}
					}
			break;
			}
			}	
break;
}
}
return 0; }

