#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <dirent.h>
#include "glob.h"
#include <string.h>
#include <stdlib.h>

#define MAXN 1024

pid_t bakers[MAXN];
pid_t custs[MAXN];
pid_t queue;
pid_t bg=0,cg=0;

int qsize, bn, cn, btiming[MAXN], ctiming[MAXN];

DIR* d;
struct dirent *dd;

char *args[4];
char b[3][20];
char s[50];

void makebaker(int i)
{
	if(!(bakers[i]=fork()))
	{
		args[0]=b[0];
		args[1]=b[1];
		args[2]=b[2];
		args[3]=0;

		strcpy(b[0],"./baker");
		int h = queue;
		sprintf(b[1], "%d", h);
		sprintf(b[2], "%d", btiming[i]);
		execv("./baker",args);
	}
	else
	{
		if(bg==0)
			bg=bakers[i];
		setpgid(bakers[i],bg);
	}
}

void makecust(int i)
{
	if(!(custs[i]=fork()))
	{
		args[0]=b[0];
		args[1]=b[1];
		args[2]=b[2];
		args[3]=0;

		strcpy(b[0],"./cust");
		int h=queue;
		sprintf(b[1], "%d", h);
		sprintf(b[2], "%d", ctiming[i]);
		execv("./cust",args);
	}
	else
	{
		if(cg==0)
			cg=custs[i];
		setpgid(custs[i],cg);
	}
}
	
void killall(void)
{
	int i;
	kill(queue, SIGKILL);
	for(i=0;i<bn;i++)
		kill(bakers[i],SIGKILL);
	for(i=0;i<cn;i++)
		kill(custs[i],SIGKILL);
	raise(SIGTERM);
}

void termh(int s)
{
	killall();
	exit(0);
}


int main(void)
{
	int i;
	
	// block signals
	sigset_t ss;
	sigemptyset(&ss);
	sigaddset(&ss, SIG1);
	sigaddset(&ss, SIG2);
	sigaddset(&ss, SIG3);
	sigaddset(&ss, SIG4);
	sigaddset(&ss, SIG5);
	sigaddset(&ss, SIG6);
	sigaddset(&ss, SIG7);
	sigaddset(&ss, SIGALRM);
	sigprocmask(SIG_BLOCK, &ss, 0);
	signal(SIGTERM, termh);
	signal(SIGINT, termh);
	
	// init
	printf("= Init\n");
	FILE* f=fopen("./config","r");
	fscanf(f,"%d%d%d", &qsize, &bn, &cn);
	for(i=0;i<bn;i++)
		fscanf(f,"%d",&btiming[i]);
	for(i=0;i<cn;i++)
		fscanf(f,"%d",&ctiming[i]);
	fclose(f);
	
	atexit(killall);

	mkdir("./in",0777);
	mkdir("./out",0777);
	mkdir("./tmp",0777);

	system("rm ./tmp/* ./in/* ./out/*");
	
	// fork
	printf("= Fork Queue\n");fflush(stdout);
	if(!(queue=fork()))
	{	
		args[0]=b[0];
		args[1]=b[1];
		args[2]=0;

		strcpy(b[0],"./queue");
		sprintf(b[1], "%d", qsize);
		execv("./queue",args);
	}
	printf("= Fork Bakers\n");fflush(stdout);
	for(i=0;i<bn;i++)
		makebaker(i);
	printf("= Fork Customers\n");fflush(stdout);
	for(i=0;i<cn;i++)
		makecust(i);
	
	
	// cycle
	while(1)
	{
		int status;
		pid_t dead=wait(&status);
		
		/*
		if(WIFSIGNALED(status))
			printf("termination signal %d\n",(int)WTERMSIG(status));
		*/
		if(dead==queue)
		{
			printf("= Queue is Dead! Closing...\n");fflush(stdout);
			killall();
		}
		for(i=0;i<cn;i++){
			if(dead==custs[i]){
				printf("= Customer%d if Dead! ReForking...\n",i);
				fflush(stdout);
				//makecust(i);
				d=opendir("./tmp");
				while((dd=readdir(d))!=0) if(dd->d_name[0]=='e')
				{
					int h;
					sscanf(dd->d_name, "e%d", &h);
					if(h==dead)
					{
						unlink("./out/1");
						kill(queue, SIG7);
					}
					sprintf(s,"./tmp/%s",dd->d_name);
					unlink(s);
				}
				makecust(i);
				
			}
		}
		for(i=0;i<cn;i++){ 
			if(dead==bakers[i]){
				printf("= Baker%d if Dead! ReForking...\n",i);
				fflush(stdout);
				makebaker(i);
			}
		}
	}
	return 0;
}
