#include<errno.h>

#include<math.h>

#include<signal.h>

#include<stdio.h>

#include<stdlib.h>


#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/sem.h>

#include<sys/time.h>


#define	FALSE	0

#define	TRUE	1

#define	NPHIL	4 //عدد الفلاسفة يعدل الى خمسة

#define	NCHOPSTICKS	NPHIL //عدد الشوك هو نفس عدد الفلاسفة

#define	AVG_THINK	2.5 // زمن التفكير بدون أكل حالة الانتظار

#define	AVG_EAT	1.5 //زمن الاكل حالة العمل

#define	DURATION	30 //الزمن الذي تحدث بعده المقاطعة



typedef  union  semun{

	int   val; //حقل لتاسيس السيمافور على الصفر او الواحد

	struct semid_ds *buf;

	ushort	*array;

}SEM_UNION;






static  int  alarm_flag=FALSE;



/*------print error mesage and terminate a process------*/


void  error(const  char  *msg)
{
	fflush(stdout);
	
printf( "%s %d  %s\n", msg ,errno,strerror(errno));

	exit(0);
}

/*******signal the semaphore*******/



void  my_signal(int  s)//التابع الذي يعمل عملية السيغنل
                      //نمرر له رقم السيمافور الذي اريد ان اعمل عليه سغنل
{

	int  status;


	struct sembuf  sb;
	sb.sem_num=0;//السيمافور الاول بالمصفوفة
	sb.sem_op=1;//رفع )سيغنال
	sb.sem_flg=SEM_UNDO;//عندما تجد مشكلةمثل بطئ الكومبيوتر) حرر السيمافور

	status=semop(s,&sb,1);//s=semid
	if(status<0)
	{ error("In my wait,semaphore failed");}
}
	

/********wait on semaphore*******/
void  my_wait(int  s)
{
	int  status;
	struct sembuf  sb;
	sb.sem_num=0;
	sb.sem_op=-1;//wait1
	sb.sem_flg=SEM_UNDO;

	status=semop(s,&sb,1);
	if(status<0)
	{ error("In my wait,semaphore failed");}
}


/********think *******///خارج المعالج

void  think(int phil)//نمرر له رقم الفيلسوف
{
	 int duration=drand48()*1000000.0*AVG_THINK;//للتحويل الى ميكرو ثانية
	printf("phil %d (proc %d)  thinking %d  	microseconds\n",phil,getpid(), duration);
	usleep(duration);
}

void  eat(int phil)//داخل المعالج
{
	 int duration=drand48()*1000000.0*AVG_EAT;
	printf("phil %d (proc %d)  eating %d  	microseconds\n",phil,getpid(), duration);
	usleep(duration);
}

 
/*****timer*****/

void  alarm_handler(sig,code,scp)

	int sig;

	int code;
	
struct sigcontext  *scp;

{

alarm_flag=TRUE;

}



void  set_sig_handler()

{
	struct sigaction act;
	act.sa_handler=alarm_handler;

#if defined_FreeBSD_
	act.sa_mask=0;


#endif
	act.sa_flags=SA_RESTART|SA_RESETHAND;

	sigaction(SIGUSR1, &act,(struct  sigaction *)	NULL);

}




/******creat  process*********/


int   create_philososphers(const int n,int  child[])//تابع انشاء الاجرائيات حيث نولد اجراء اب وهو سيولد اربع اجرائيات وجميع الاجرائيات تمثل الفلاسفة
{                                                  //n=5

	int  i,status;
	for (i=0;i<n;++i)
	{

		child[i]=0;
	
}
	
printf("creating  %d  philosophers\n",n);


	for(i=0;i<n;++i)

	{

		status=fork();
		if(status<0)

		{ 
error("failed to creat 			     philosopher	\n");}
	
	else if(status==0)//نحن بالابن رجع ابناء
		{return  i-1;
}
		else{ child[i-1]=status;}//نحنبالاب خزن بيايدي للابن pid
	}
	return -1;
}


	
/**********Main program*********/

int   main( int argc,char   *argv[])
{
int	child[NPHIL],
	chopstick[NCHOPSTICKS],i,status,   philosopher_id=0 , left,right;//الشوكة اليمين واليسار
	SEM_UNION	arg;//لتاسيس السيمافور//from union
	key_t	k;//لربط السيمافورات
	for(i=0;i<=NCHOPSTICKS;++i)	
	{
		k=ftok(argv[0],i);//لتوليد الكي لخمس سيمافورات الشوك 
		printf("\n",argv[0],k);
		chopstick[i]=semget(k,1,IPC_CREAT);//1 لكل شوكة سيمافور وحيد//IPC_CREAT becouse creat new key !!!
	printf("chopstick[%d] is %d\n",i,chopstick[i]);


	
	if(chopstick[i]<0)
	
{error("bad sem creat");
}

	

arg.val=1;//نأسس السيمافورات على قيمة 1 
	
status=semctl(chopstick[i],0,SETVAL,arg);
	if(status<0)
	{error("couldnt itinalize sem counter to 1");}
	}

/*********creat semaphores******/

philosopher_id=create_philososphers(NPHIL,child);

if(philosopher_id==-1)
{
	sleep(DURATION);
	for(i=0;i<NPHIL;++i)
	{
		kill(child[i],SIGUSR1);
	}
	for(i=0;i<NPHIL;++i)
	{
		status=wait((int *)0);
		if(status<0)
		{
			error("wait erorr detected");
		}
	}
	for(i=0;i<NCHOPSTICKS;i++)
	{
		status=semctl(chopstick[i],0,IPC_RMID,0);
		if(status<0)
		{error("bad remove sem");}
	}
}
else
{
	left=philosopher_id;
	right=(philosopher_id +1)%NPHIL;

	set_sig_handler();
	while(!alarm_flag)
	{
		think(philosopher_id);
		if(philosopher_id & 0x1)
		{
			my_wait(chopstick[left]);
			printf("phil %d picked up left chopstick %d\n",philosopher_id,getpid(),left);
			my_wait(chopstick[right]);
			printf("phil %d picked up right chopstick %d\n",philosopher_id,getpid(),right);
		}	
		else
		{
			my_wait(chopstick[right]);
			printf("phil %d picked up right chopstick %d\n",philosopher_id,getpid(),right);
			my_wait(chopstick[left]);
			printf("phil %d picked up left chopstick %d\n",philosopher_id,getpid(),left);
		}

		eat(philosopher_id);
		if(philosopher_id & 0x1)
		{
			my_signal(chopstick[left]);
			printf("phil %d put down left chopstick %d\n",philosopher_id,getpid(),left);
			my_signal(chopstick[right]);
			printf("phil %d put down right chopstick %d\n",philosopher_id,getpid(),right);
		}	
		else
		{
			my_signal(chopstick[right]);
			printf("phil %d put down right chopstick %d\n",philosopher_id,getpid(),right);
			my_signal(chopstick[left]);
			printf("phil %d put down left chopstick %d\n",philosopher_id,getpid(),left);
		}
	}
}
}




