#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>  // Include semaphore header
#include <unistd.h>

#define NUM_PHILOSOPHERS 5

sem_t forks[NUM_PHILOSOPHERS];   // Declare semaphore for forks
pthread_mutex_t eatCountMutex = PTHREAD_MUTEX_INITIALIZER;
int eatCount = 0;

void *philosopher(void *arg) {
    int id = *(int *)arg;

    while (1) {
        printf("Philosopher %d is thinking.\n", id);
        sleep(rand() % 3);

        printf("Philosopher %d is hungry.\n", id);

        // Pick up the forks (left and right)
        sem_wait(&forks[id]);                   // Left fork
        sem_wait(&forks[(id + 1) % NUM_PHILOSOPHERS]); // Right fork

        // Eating
        printf("Philosopher %d is eating.\n", id);
        sleep(rand() % 3);

        // Put down the forks
        sem_post(&forks[id]);                   // Left fork
        sem_post(&forks[(id + 1) % NUM_PHILOSOPHERS]); // Right fork

        // Track if each philosopher has eaten at least once
        pthread_mutex_lock(&eatCountMutex);
        eatCount++;
        if (eatCount >= NUM_PHILOSOPHERS) {
            printf("All philosophers have eaten at least once.\n");
            pthread_mutex_unlock(&eatCountMutex);
            exit(0);
        }
        pthread_mutex_unlock(&eatCountMutex);
    }
    return NULL;
}

int main() {
    pthread_t philosophers[NUM_PHILOSOPHERS];
    int ids[NUM_PHILOSOPHERS];

    // Initialize semaphores
    for (int i = 0; i < NUM_PHILOSOPHERS; i++) {
        ids[i] = i;
        sem_init(&forks[i], 0, 1);  // Initialize each fork semaphore with value 1
    }

    // Create philosopher threads
    for (int i = 0; i < NUM_PHILOSOPHERS; i++) {
        pthread_create(&philosophers[i], NULL, philosopher, (void *)&ids[i]);
    }

    // Join philosopher threads
    for (int i = 0; i < NUM_PHILOSOPHERS; i++) {
        pthread_join(philosophers[i], NULL);
    }

    // Destroy semaphores
    for (int i = 0; i < NUM_PHILOSOPHERS; i++) {
        sem_destroy(&forks[i]);  // Clean up semaphores
    }

    return 0;
}
