#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>

struct element {
    int data;
    struct element *next;
};

struct queue {
    struct element *head;
    struct element *tail;
};

void queue_init(struct queue *fa) {
    fa->head = NULL;
    fa->tail = NULL;
}

bool queue_is_empty(struct queue fa) {
    return (fa.head == NULL && fa.tail == NULL);
}

int queue_tail(struct queue fa) {
    assert(!queue_is_empty(fa) && "Careful the queue is empty.");
    return fa.tail->data;
}
int queue_head(struct queue fa) {
    assert(!queue_is_empty(fa) && "Careful the queue is empty.");
    return fa.head->data;
}

void queue_enqueue(struct queue *fa, int val) {
    struct element *elem = malloc(sizeof(*elem));
    elem->data = val;
    elem->next = NULL;
    if (queue_is_empty(*fa)) {
        fa->tail = elem;
        fa->head = elem;
    } else {
        fa->tail->next = elem;
        fa->tail = elem;
    }
}

int queue_dequeue(struct queue *fa) {
    if (queue_is_empty(*fa)) {
        assert(false && "Empty queue, impossible to dequeue.");
        // do something
        return -1;
    } else if (fa->head != fa->tail) {
        struct element *tmp_elem = fa->head;  // 1
        int tmp_val = fa->head->data;         // 2
        fa->head = fa->head->next;            // 3
        free(tmp_elem);                       // 4
        return tmp_val;                       // 5
    } else {
        struct element *tmp_elem = fa->head;  // 1
        int tmp_val = fa->head->data;         // 2
        fa->head = NULL;                      // 3
        fa->tail = NULL;                      // 3
        free(tmp_elem);                       // 4
        return tmp_val;                       // 5
    }
}

void queue_destroy(struct queue *fa) {
    while (!queue_is_empty(*fa)) {
        queue_dequeue(fa);
    }
}

int main() {
    struct queue fa;  // mémoire est allouée
    queue_init(&fa);
    printf("is fa empty? %s\n", queue_is_empty(fa) ? "yes" : "no");

    printf("enqueuing 10\n");
    queue_enqueue(&fa, 10);
    printf("enqueuing 20\n");
    queue_enqueue(&fa, 20);
    printf("enqueuing 30\n");
    queue_enqueue(&fa, 30);
    printf("is fa empty? %s\n", queue_is_empty(fa) ? "yes" : "no");

    int v1 = queue_dequeue(&fa);
    printf("v1 contains? %d\n", v1);
    printf("head contains? %d\n", queue_head(fa));
    printf("tail contains? %d\n", queue_tail(fa));
    int v2 = queue_dequeue(&fa);
    int v3 = queue_dequeue(&fa);
    printf("v2 contains? %d\n", v2);
    printf("v3 contains? %d\n", v3);
    printf("is fa empty? %s\n", queue_is_empty(fa) ? "yes" : "no");

    printf("enqueuing 10\n");
    queue_enqueue(&fa, 10);
    printf("enqueuing 20\n");
    queue_enqueue(&fa, 20);
    printf("enqueuing 30\n");
    queue_enqueue(&fa, 30);
    printf("is fa empty? %s\n", queue_is_empty(fa) ? "yes" : "no");
    printf("destroying\n");
    queue_destroy(&fa);
    printf("is fa empty? %s\n", queue_is_empty(fa) ? "yes" : "no");

    return EXIT_SUCCESS;
}
