#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>
#include <fcntl.h>
#include <errno.h>

                                   
#define     SLEEP_TIME       2     /* maximum amount of time */

void hungry();
void thinking();
void *philosopher(void*);
void eating(int num);
void orderWine(int num);
void *serviceTask(void *arg);

sem_t* gabel[5];	/* semaphores for all forks */

pthread_t thread[5];		/* threads for all philosophers */
int id[5];			/* id parameter */


int main()
{
	int i;
	
	/************************************************/
	/*initialize 5 semaphores						*/
	/************************************************/

	/*
		one must be really careful here - if the program is terminated before
		sem_unlink is called, then the semaphore still exist with their last value ...
		in this case the sempaphores have to be opened and unlinked ...
	 */

	// on ubuntu sem_open needs sudo right
	// alternatively use sem_init(&gabel[0],...) and change sem_t* to sem_t, sem_wait(&gabel[i],...) etc

	gabel[0] = sem_open("sem0", O_CREAT , 1, 1);
	gabel[1] = sem_open("sem1", O_CREAT , 1, 1);
	gabel[2] = sem_open("sem2", O_CREAT , 1, 1);
	gabel[3] = sem_open("sem3", O_CREAT , 1, 1);
	gabel[4] = sem_open("sem4", O_CREAT , 1, 1);

	for( i = 0; i < 5; i++ )
		printf("%p\n", gabel[ i ] );

	/************************************************/
	/*create philosopher threads					*/
	/************************************************/
	for (i=0; i<5; i++) {
		id[i] = i;
		if (0!=pthread_create (&thread[i], NULL, philosopher, &id[i])) /* creation of philosopher thread i */ {
			printf("ERROR: main thread: could not create thread%d\n", i);
			return -1;
		}
		else {
			printf("INFO: main thread: thread%d created\n", i);
		}

	}

	pthread_join(thread[0], NULL);
	pthread_join(thread[1], NULL);
	pthread_join(thread[2], NULL);
	pthread_join(thread[3], NULL);
	pthread_join(thread[4], NULL);

	sem_close(gabel[0]);
	sem_close(gabel[1]);
	sem_close(gabel[2]);
	sem_close(gabel[3]);
	sem_close(gabel[4]);
	
	printf("END\n");

	return 0;
}

/**********************************************************************/
/**FUNCTION:  philTask()                                         	 **/
/**                                                                  **/
/**DESCRIPTION:  every philosopher thinks, is hungry, eats and     	 **/
/** 			 orders wine			                             **/
/**********************************************************************/
void *philosopher(void *arg)
{
	int i;
	struct timespec current_time;

	clock_gettime(CLOCK_REALTIME, &current_time); /* current time */
	srand((int)current_time.tv_nsec);

	/**************************************************************/
	/*philosopher (child) thinks, becomes hungry, ...*/
	/**************************************************************/

	for (i=0;i<10;i++)
	{
		thinking(*(int*)arg);
		hungry(*(int*)arg);
		eating(*(int*)arg);		
	}
	return 0;
}

/**********************************************************************/
/**FUNCTION:  hungry()                                               **/
/**                                                                  **/
/**DESCRIPTION:   This function is representative of a philosopher   **/
/** being hungry.                                                    **/
/**********************************************************************/
void hungry(int p_num)
{
	printf("Philosopher %d: I'm hungry!\n",p_num);
}


/**********************************************************************/
/**FUNCTION:   thinking()                                            **/
/**                                                                  **/
/**DESCRIPTION:   This function is representative of a philosopher   **/
/** thinking for a random amount of time.                            **/
/**********************************************************************/
void thinking(int p_num)
{
	printf("Philosopher %d is thinking!\n",p_num);
	sleep(rand() % SLEEP_TIME);
}


/**********************************************************************/
/**FUNCTION:  eating()                                               **/
/**                                                                  **/
/**DESCRIPTION:   This function is representative of a philosopher   **/
/** eating for a random amount of time.                              **/
/**********************************************************************/
int left( int p )
{
	p--;
	while( p < 0 )
		p = ( 5 + p );
	printf("%d\n",  p % 5 );
	return p % 5;
}

int right( int p )
{
	while( p < 0 )
		p = ( 5 + p );

	printf("%d\n",  p % 5 );
	return p % 5;
}

void eating(int p_num)
{
	if (!( p_num % 2 ) )
	{
		/*********************/
		/****    TAKE FORKS **/
		/*********************/
		sem_wait(gabel[ left( p_num ) ]);
		sem_wait(gabel[ right( p_num ) ]);

		printf("Philosopher %d is eating!\n",p_num);
		sleep(rand() % SLEEP_TIME);

		/*********************/
		/****    GIVE FORKS **/
		/*********************/
		sem_post(gabel[ left( p_num ) ]);
		sem_post(gabel[ right( p_num ) ]);
	}
	else
	{
		/*********************/
		/****    TAKE FORKS **/
		/*********************/
		sem_wait(gabel[ right( p_num ) ]);
		sem_wait(gabel[ left( p_num ) ]);

		printf("Philosopher %d is eating!\n",p_num);
		sleep(rand() % SLEEP_TIME);

		/*********************/
		/****    GIVE FORKS **/
		/*********************/
		sem_post(gabel[ right( p_num ) ]);
		sem_post(gabel[ left( p_num ) ]);
	}

}

