/*
 * Creator: Naman Dixit
 * Notice: © Copyright 2020 Naman Dixit
 */

#if !defined(COMMAND_H_INCLUDE_GUARD)


#define COMMAND_SYSTEM(req_type_name, req_var_name, req_member_type,    \
                       res_type_name, res_var_name, res_member_type)    \
                                                                        \
    typedef struct req_type_name {                                      \
        req_member_type member;                                         \
        struct req_type_name *next;                                     \
    } req_type_name;                                                    \
                                                                        \
    global_variable req_type_name *global_##req_var_name##_first;       \
    global_variable req_type_name *global_##req_var_name##_last;        \
    global_variable req_type_name *global_##req_var_name##_divider;     \
    global_variable pthread_mutex_t global_##req_var_name##_lock = PTHREAD_MUTEX_INITIALIZER; \
    global_variable pthread_cond_t global_##req_var_name##_cond_var = PTHREAD_COND_INITIALIZER; \
                                                                        \
    internal_function                                                   \
    void req_var_name##Init (void)                                      \
    {                                                                   \
        global_##req_var_name##_first = calloc(1, sizeof(*global_##req_var_name##_first)); \
        global_##req_var_name##_last = global_##req_var_name##_first;   \
        global_##req_var_name##_divider = global_##req_var_name##_first; \
                                                                        \
        return;                                                         \
    }                                                                   \
                                                                        \
    internal_function                                                   \
    void req_var_name##Enqueue (req_member_type c)                      \
    {                                                                   \
        global_##req_var_name##_last->next = calloc(1, sizeof(*(global_##req_var_name##_last->next))); \
        global_##req_var_name##_last->next->member = c;                 \
        global_##req_var_name##_last = global_##req_var_name##_last->next; \
                                                                        \
        while (global_##req_var_name##_first != global_##req_var_name##_divider) { \
            req_type_name *temp = global_##req_var_name##_first;        \
            global_##req_var_name##_first = global_##req_var_name##_first->next; \
            free(temp);                                                 \
        }                                                               \
                                                                        \
        pthread_cond_signal(&global_##req_var_name##_cond_var);         \
                                                                        \
        return;                                                         \
    }                                                                   \
                                                                        \
    internal_function                                                   \
    void req_var_name##Dequeue (req_member_type *c, _Atomic B64 *keep_alive) \
    {                                                                   \
        pthread_mutex_lock(&global_##req_var_name##_lock);              \
                                                                        \
        while (global_##req_var_name##_divider == global_##req_var_name##_last) { \
            if ((keep_alive != NULL) && (atomic_load(keep_alive) == false)) { \
                *c = (req_member_type){0};                              \
                pthread_cond_signal(&global_##req_var_name##_cond_var); \
                pthread_mutex_unlock(&global_##req_var_name##_lock);    \
                return;                                                 \
            }                                                           \
            pthread_cond_wait(&global_##req_var_name##_cond_var, &global_##req_var_name##_lock); \
        }                                                               \
                                                                        \
        *c = global_##req_var_name##_divider->next->member;             \
        global_##req_var_name##_divider = global_##req_var_name##_divider->next; \
                                                                        \
        pthread_mutex_unlock(&global_##req_var_name##_lock);            \
    }                                                                   \
                                                                        \
                                                                        \
                                                                        \
                                                                        \
    typedef struct res_type_name {                                      \
        res_member_type member;                                         \
        struct res_type_name *next;                                     \
    } res_type_name;                                                    \
                                                                        \
    global_variable res_type_name *global_##res_var_name##_first;       \
    global_variable res_type_name *global_##res_var_name##_last;        \
    global_variable res_type_name *global_##res_var_name##_divider;     \
    global_variable pthread_mutex_t global_##res_var_name##_lock = PTHREAD_MUTEX_INITIALIZER; \
                                                                        \
    internal_function                                                   \
    void res_var_name##Init (void)                                      \
    {                                                                   \
        global_##res_var_name##_first = calloc(1, sizeof(*global_##res_var_name##_first)); \
        global_##res_var_name##_last = global_##res_var_name##_first;   \
        global_##res_var_name##_divider = global_##res_var_name##_first; \
    }                                                                   \
                                                                        \
    internal_function                                                   \
    void res_var_name##Enqueue (res_member_type c)                      \
    {                                                                   \
        pthread_mutex_lock(&global_##res_var_name##_lock);              \
                                                                        \
        global_##res_var_name##_last->next = calloc(1, sizeof(*(global_##res_var_name##_last->next))); \
        global_##res_var_name##_last->next->member = c;                 \
        global_##res_var_name##_last = global_##res_var_name##_last->next; \
                                                                        \
        while (global_##res_var_name##_first != global_##res_var_name##_divider) { \
            res_type_name *temp = global_##res_var_name##_first;        \
            global_##res_var_name##_first = global_##res_var_name##_first->next; \
            free(temp);                                                 \
        }                                                               \
                                                                        \
        pthread_mutex_unlock(&global_##res_var_name##_lock);            \
                                                                        \
        return;                                                         \
    }                                                                   \
                                                                        \
    internal_function                                                   \
    B32 res_var_name##Dequeue (res_member_type *c)                      \
    {                                                                   \
        if (global_##res_var_name##_divider != global_##res_var_name##_last) { \
            *c = global_##res_var_name##_divider->next->member;         \
            global_##res_var_name##_divider = global_##res_var_name##_divider->next; \
            return true;                                                \
        }                                                               \
                                                                        \
        return false;                                                   \
    }


#define COMMAND_H_INCLUDE_GUARD
#endif
