#include <stdio.h>
#include <tkPort.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/wait.h>
#include <errno.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 fournis par l'utilisateur
    if (argc < 3) {
        printf("Usage : %s <nbAvionDecollage> <nbAvionAtterrissage>\n", argv[0]);
        return 1;
    }

    srand((unsigned) time(NULL) * getpid());

    // Obtention d'un clé
    key_t clef = ftok(argv[0], 'p');
    if (clef == -1) {
        perror("ftok()");
        exit(1);
    }

    // Obtention d'un identifiant
    sem_id = semget(clef, 1, IPC_CREAT | IPC_EXCL | 0666);
    if (sem_id == -1) {
        perror("semget()");
        exit(1);
    }

    // Initialisation du sémaphore à 1
    if (-1 == semctl(sem_id, 0, SETVAL, 1)) {
        perror("semctl() init sémaphore");
        exit(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);
            exit(0);
        default:
            creerAvions(nbAvionDecollage, decollage);
//            waitpid(pid, NULL, 0);
    }

    // wait for any child process
    while ((wait(NULL)) != -1);

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

    return 0;
}

void randomSleep() {
    unsigned int seconds = (unsigned int) (rand() % 10 + 1);
    printf("s'endort pour %i\n", seconds);
    sleep(seconds);
}

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
    if (semop(sem_id, &op, 1) == -1) {
        perror("semop()");
        exit(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
    if (semop(sem_id, &op, 1) == -1) {
        perror("semop()");
        exit(1);
    }
}

void creerAvions(int nbAvion, void (*f)(int)) {
    pid_t pid;
    for (int j = 0; j < nbAvion; ++j) {
        pid = fork();
        switch (pid) {
            case -1 :
                perror("fork()");
            case 0 :
                srand((unsigned) time(NULL) * getpid());
                (*f)(getpid());
                exit(0); // le fil avion à fini sa tâche
            default:
                break;
        }
    }
    // wait for any child process
    while ((wait(NULL)) != -1);
}

void decollage(int numAvion) {
    randomSleep();
    printf("Vol numero %i souhaite decoller\n", numAvion);
    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
}

void atterrisage(int numAvion) {
    randomSleep();
    printf("Vol numero %i souhaite atterrir\n", numAvion);
    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
}
