#include <stdio.h>
#include <tkPort.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/wait.h>

#define TMP_DECOLLAGE 5
#define TMP_ATTERRISSAGE 3

int sem_id;
struct sembuf op;

void randomSleep(); // sleep random entre 1 et 10
void P(); // down, prendre la ressource
void V(); // up, libere la ressource
void creerAvions(int nbAvion, void (*fonc)(int)); // creer nb avion avec une fonction (decollage ou atterrisage)
void decollage(int id); // fait decoller l'avion
void atterrisage(int id); // fait atterir l'avion

int main(int argc, char **argv) {
    // on vérifie le nombre d'arguments fourni par l'utilisateur
    if (argc < 3) {
        printf("Usage : %s <nbAvionDecollage> <nbAvionAtterrissage>\n", argv[0]);
        return 1;
    }

    // Obtention d'un clé
    key_t clef = ftok(argv[0], 'p');

    // Obtention d'un identifiant
    sem_id = semget(clef, 1, IPC_CREAT | IPC_EXCL | 0666);

    // Initialisation du sémaphore à 1
    semctl(sem_id, 0, SETVAL, 1);

    pid_t pid;
    int nbAvionDecollage = atoi(argv[1]);
    int nbAvionAtterrissage = atoi(argv[2]);

    // On créer simultanement des avions qui décollent et atterrissent
    switch (pid = fork()) {
        case -1:
            perror("fork()");
        case 0 :
            creerAvions(nbAvionAtterrissage, atterrisage);
            break;
        default:
            creerAvions(nbAvionDecollage, decollage);
            waitpid(pid, NULL, 0); // le père attent la fin de son fils
    }

    // Destruction de la sémaphore
    semctl(sem_id, 0, IPC_RMID, 0);

    return 0;
}

void randomSleep() {
    sleep((unsigned int) (rand() % 11));
}

void P() {
    op.sem_num = 0; // Numéro de notre sémaphore: le premier et le seul
    op.sem_op = -1; // Pour un P() on décrémente
    op.sem_flg = 0; // On ne s'en occupe pas
    semop(sem_id, &op, 1);
}

void V() {
    op.sem_num = 0; // Numéro de notre sémaphore: le premier et le seul
    op.sem_op = 1;  // Pour un V() on incrémente
    op.sem_flg = 0; // On ne s'en occupe pas
    semop(sem_id, &op, 1);
}

void creerAvions(int nbAvion, void (*f)(int)) {
    for (int j = 0; j < nbAvion; ++j) {
        switch (fork()) {
            case -1 :
                perror("fork()");
            case 0 :
                (*f)(getpid());
                exit(0); // le fil avion à fini sa tâche
            default:
                wait(NULL);
        }
    }
}

void decollage(int numAvion) {
    printf("Vol numero %i souhaite atterrir\n", numAvion);
    randomSleep();
    P(); // lock la piste
    printf("\tVol numero %i en phase d'atterrissage\n", numAvion);
    sleep(TMP_ATTERRISSAGE);
    printf("\t\tPiste liberee par vol numero %i\n", numAvion);
    V(); // unlock la piste
}

void atterrisage(int numAvion) {
    printf("Vol numero %i souhaite decoller\n", numAvion);
    randomSleep();
    P(); // lock la piste
    printf("\tVol numero %i en phase de decollage\n", numAvion);
    sleep(TMP_DECOLLAGE);
    printf("\t\tPiste liberee par vol numero %i\n", numAvion);
    V(); // unlock la piste
}
