/* filozofi.c
 *
 * Operacijski sustavi -- vjezba 3, zadatak 3b
 *
 * Rijesio:
 * Filip Niksic (fniksic@gmail.com)
 * 2008-05-27
 **/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

#define nFilozofa 5
#define eatTime 1
#define thinkTime 1

pthread_mutex_t monitor;
pthread_cond_t uvjet[nFilozofa];

int vilica[nFilozofa];
char filozof[nFilozofa];

void misliti() {
    sleep(thinkTime);
}

void ispisiStatus(int n) {
    int i;
    for (i = 0; i < nFilozofa; ++i)
	printf("%c ", filozof[i]);
    printf("(%d)\n", n + 1);
}

void jesti(int n) {
    pthread_mutex_lock(&monitor);
    
    filozof[n] = '.';
    /* Cekamo u redu uvjeta n dok vilice ne postanu slobodne */
    while (vilica[n] == 0 || vilica[(n + 1) % nFilozofa] == 0)
	pthread_cond_wait(&uvjet[n], &monitor);
    /* Zauzimamo vilice */
    vilica[n] = vilica[(n + 1) % nFilozofa] = 0;
    filozof[n] = 'X';
    ispisiStatus(n);

    pthread_mutex_unlock(&monitor);

    sleep(eatTime); /* Jedem */

    pthread_mutex_lock(&monitor);

    filozof[n] = 'O';
    /* Oslobadjam vilice */
    vilica[n] = vilica[(n + 1) % nFilozofa] = 1;
    /* Signaliziram lijevom i desnom susjedu da im je vilica slobodna */
    pthread_cond_signal(&uvjet[(n + 1) % nFilozofa]);
    pthread_cond_signal(&uvjet[(n + nFilozofa - 1) % nFilozofa]);
    ispisiStatus(n);

    pthread_mutex_unlock(&monitor);
}

void *filozof_dretva(void *arg) {
    while (1) {
	misliti();
	jesti(*(int *)arg);
    }
    return NULL;
}

int main(void) {
    int i;
    int arg[nFilozofa];
    pthread_t fil[nFilozofa];

    /* Inicijalizacija mutexa i ostalih stvari */
    pthread_mutex_init(&monitor, NULL);
    for (i = 0; i < nFilozofa; ++i) {
	pthread_cond_init(&uvjet[i], NULL);
	vilica[i] = 1;
	filozof[i] = 'O';
	arg[i] = i;
    }

    /* Pokrecem dretve */
    for (i = 0; i < nFilozofa; ++i) {
	pthread_create(&fil[i], NULL, filozof_dretva, &arg[i]);
	/*sleep(1);*/
    }
    /* Cekam dok dretve ne zavrse */
    for (i = 0; i < nFilozofa; ++i)
	pthread_join(fil[i], NULL);

    /* Destrukcija uvjetnih varijabli i mutexa, u principu nepotrebno */
    for (i = 0; i < nFilozofa; ++i)
	pthread_cond_destroy(&uvjet[i]);
    pthread_mutex_destroy(&monitor);

    return EXIT_SUCCESS;
}
