#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
#include <zconf.h>
#include <wait.h>
#include <time.h>

struct sembuf op[2];

void mineur(int i);

int sem_id;

int randomNumber(int max);

void P();

void V();

int main(int argc, char **argv) {
    if (argc < 3) {
        printf("mineOr nbDeMineurs nbDePelles nbDePioches\n");
        exit(0);
    }

    int nbDeMineurs = atoi(argv[1]);
    int nbDePelles = atoi(argv[2]);
    int nbDePioches = atoi(argv[3]);

    key_t key = ftok(argv[0], 'k');
    sem_id = semget(key, 2, IPC_CREAT | IPC_EXCL | 0666); // création de deux semaphore
    if (sem_id < 0) {
        perror("semget()");
        exit(EXIT_FAILURE);
    }
    semctl(sem_id, 0, SETVAL, nbDePelles); // initialise le nombre de pelle dans la semaphore
    semctl(sem_id, 1, SETVAL, nbDePioches); // initialise le nombre de pioche dans la semaphore

    pid_t pid;
    for (int i = 1; i <= nbDeMineurs; ++i) {
        switch (pid = fork()) {
            case -1 :
                perror("fork()");
            case 0 : // child
                mineur(i);
                exit(0);
            default: // father
                printf("Création du mineur %i (%i)\n", i, pid);
        }
    }

    // wait for any child process
    while ((pid = wait(NULL)) != -1)
        printf("Le mineur de pid %d a fini.\n", pid);

    // Destruction de la sémaphore
    if (semctl(sem_id, 0, IPC_RMID, 0) == -1) {
        perror("semctl() destruction sémaphore");
    }

    return 0;
}

void mineur(int numMineur) {
    srand((unsigned) time(NULL) * getpid());
    int attenteAvantTravail = randomNumber(5);
    int dureeDeTravail = randomNumber(5);

    /* attente avant de se mettre au travail */
    printf("Le mineur %d attend %d heures avant de travailler.\n", numMineur, attenteAvantTravail);
    sleep((unsigned int) attenteAvantTravail);

    P(); /* Session critique */

    printf("Le mineur %d prend une pelle et une pioche\n", numMineur);
    printf("Le mineur %d commence à travailler pour %d heures.\n", numMineur, dureeDeTravail);
    sleep((unsigned int) dureeDeTravail);
    printf("Le mineur %d a extrait %d g d'or\n", numMineur, randomNumber(1000));

    V(); /* Fin session critique */

    printf("Le mineur %d rend une pelle et une pioche\n", numMineur);
}

int randomNumber(int max) {
    return (unsigned int) (rand() % max + 1);
}

void P() {
    /* Prise de la pelle */
    op[0].sem_num = 0; // Numéro de notre sémaphore 0 pour la pelle
    op[0].sem_op = -1; // Pour un P() on décrémente
    op[0].sem_flg = SEM_UNDO; // On ne s'en occupe pas

    /* Prise de la pioche */
    op[1].sem_num = 1; // Numéro de notre sémaphore 1 pour la pioche
    op[1].sem_op = -1; // Pour un P() on décrémente
    op[1].sem_flg = SEM_UNDO;

    /* Application des operations */
    if (semop(sem_id, op, 2) == -1) {
        perror("semop() P failed :");
    }
}

void V() {
    /* Rend la pelle */
    op[0].sem_num = 0; // Numéro de notre sémaphore 0 pour la pelle
    op[0].sem_op = 1;  // Pour un V() on incrémente
    op[0].sem_flg = SEM_UNDO;

    /* Rend la pioche */
    op[1].sem_num = 1; // Numéro de notre sémaphore 1 pour la pioche
    op[1].sem_op = 1;  // Pour un V() on incrémente
    op[1].sem_flg = SEM_UNDO;

    /* Application des operations */
    if (semop(sem_id, op, 2) == -1) {
        perror("semop() V failed :");
    }
}