#include "api-interrupt-mgr.h"

#include <m-list.h>
#include <cmsis_os2.h>

LIST_DEF(list_interrupt, InterruptCallbackItem, M_POD_OPLIST);
list_interrupt_t interrupts;
osMutexId_t interrupt_list_mutex;

bool api_interrupt_init() {
    interrupt_list_mutex = osMutexNew(NULL);
    return (interrupt_list_mutex != NULL);
}

void api_interrupt_add(InterruptCallback callback, InterruptType type, void* context) {
    if(osMutexAcquire(interrupt_list_mutex, osWaitForever) == osOK) {
        // put uninitialized item to the list
        // M_POD_OPLIST provide memset(&(a), 0, sizeof (a)) constructor
        // so item will not be ready until we set ready flag
        InterruptCallbackItem* item = list_interrupt_push_new(interrupts);

        // initialize item
        item->callback = callback;
        item->type = type;
        item->context = context;
        item->ready = true;

        // TODO remove on app exit
        //flapp_on_exit(api_interrupt_remove, callback);

        osMutexRelease(interrupt_list_mutex);
    }
}

void api_interrupt_remove(InterruptCallback callback) {
    if(osMutexAcquire(interrupt_list_mutex, osWaitForever) == osOK) {
        // iterate over items
        list_interrupt_it_t it;
        for(list_interrupt_it(it, interrupts); !list_interrupt_end_p(it);
            list_interrupt_next(it)) {
            const InterruptCallbackItem* item = list_interrupt_cref(it);

            // if the iterator is equal to our element
            if(item->callback == callback) {
                list_interrupt_remove(interrupts, it);
                break;
            }
        }

        osMutexRelease(interrupt_list_mutex);
    }
}

void api_interrupt_call(InterruptType type, void* hw) {
    // that executed in interrupt ctx so mutex don't needed
    // but we need to check ready flag

    // iterate over items
    list_interrupt_it_t it;
    for(list_interrupt_it(it, interrupts); !list_interrupt_end_p(it); list_interrupt_next(it)) {
        const InterruptCallbackItem* item = list_interrupt_cref(it);

        // if the iterator is equal to our element
        if(item->type == type && item->ready) {
            item->callback(hw, item->context);
        }
    }
}