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

int n = 0;    /* nb d'éléments dans le stockage */
int stock[5]; /* stockage */
pthread_mutex_t ms; /* verrou pour protéger le stockage */

sem_t s_rest; /* nombre de places restantes */
sem_t s_occup; /* nombre de places occupées */

int valeur(){
    int v;
    pthread_mutex_lock(&ms);
    v = n;
    pthread_mutex_unlock(&ms);
    return v;
}

void ajoute(int x){
    pthread_mutex_lock(&ms);
    assert(n < 5);
    stock[n] = x;
    n += 1;
    pthread_mutex_unlock(&ms);
}

int retire(){
    pthread_mutex_lock(&ms);
    assert(n > 0);
    n -= 1;
    int ret = stock[n];
    pthread_mutex_unlock(&ms);
    return ret;
}

void* producteur(void* args){
    char* nom = (char*) args;
    for (int i = 0; i < 10; i += 1) {
        sleep((rand() % 100) * 0.01);
        if (valeur() == 5) printf("producteur %s : stock plein ... je vais probablement attendre\n", nom);
        sem_wait(&s_rest);
        printf("producteur %s ajoute %d\n", nom, i);
        ajoute(i);
        sem_post(&s_occup);
    }
    return NULL;
}

void* consommateur(void* args){
    char* nom = (char*) args;
    for (int i = 0; i < 15; i += 1) {
        sleep((rand() % 100) * 0.01);
        if (valeur() == 0) printf("consommateur %s : stock vide ... je vais probablement attendre\n", nom);
        sem_wait(&s_occup);
        int x = retire();
        printf("consommateur %s retire %d\n", nom, x);
        sem_post(&s_rest);
    }
    return NULL;
}

int main() {
    srand(time(NULL));
    pthread_mutex_init(&ms, NULL);
    sem_init(&s_rest, 0, 5);
    sem_init(&s_occup, 0, 0);
    pthread_t p1, p2, p3;
    pthread_t c1, c2;
    pthread_create(&p1, NULL, producteur, "A");
    pthread_create(&p2, NULL, producteur, "B");
    pthread_create(&p3, NULL, producteur, "C");
    pthread_create(&c1, NULL, consommateur, "D");
    pthread_create(&c2, NULL, consommateur, "E");
    pthread_join(p1, NULL);
    pthread_join(p2, NULL);
    pthread_join(p3, NULL);
    pthread_join(c1, NULL);
    pthread_join(c2, NULL);
    return 0;
}