#include "Tactility/Mutex.h"

#include "Tactility/Check.h"
#include "Tactility/CoreDefines.h"
#include "Tactility/Log.h"

namespace tt {

#define MUTEX_DEBUGGING false

#if MUTEX_DEBUGGING
#define TAG "mutex"
    void tt_mutex_info(Mutex mutex, const char* label) {
        MutexData* data = (MutexData*)mutex;
        if (data == NULL) {
            TT_LOG_I(TAG, "mutex %s: is NULL", label);
        } else {
            TT_LOG_I(TAG, "mutex %s: handle=%0X type=%d owner=%0x", label, data->handle, data->type, tt_mutex_get_owner(mutex));
        }
    }
#else
#define tt_mutex_info(mutex, text)
#endif

static inline SemaphoreHandle_t createSemaphoreHandle(Mutex::Type type) {
    switch (type) {
        case Mutex::Type::Normal:
            return xSemaphoreCreateMutex();
        case Mutex::Type::Recursive:
            return xSemaphoreCreateRecursiveMutex();
        default:
            tt_crash("Mutex type unknown/corrupted");
    }
}

Mutex::Mutex(Type type) : handle(createSemaphoreHandle(type)), type(type) {
    tt_mutex_info(data, "alloc");
    assert(handle != nullptr);
}

bool Mutex::lock(TickType_t timeout) const {
    assert(!kernel::isIsr());
    assert(handle != nullptr);
    tt_mutex_info(mutex, "acquire");

    switch (type) {
        case Type::Normal:
            return xSemaphoreTake(handle.get(), timeout) == pdPASS;
        case Type::Recursive:
            return xSemaphoreTakeRecursive(handle.get(), timeout) == pdPASS;
        default:
            tt_crash();
    }
}

bool Mutex::unlock() const {
    assert(!kernel::isIsr());
    assert(handle != nullptr);
    tt_mutex_info(mutex, "release");

    switch (type) {
        case Type::Normal:
            return xSemaphoreGive(handle.get()) == pdPASS;
        case Type::Recursive:
            return xSemaphoreGiveRecursive(handle.get()) == pdPASS;
        default:
            tt_crash();
    }
}

ThreadId Mutex::getOwner() const {
    assert(!kernel::isIsr());
    assert(handle != nullptr);
    return (ThreadId)xSemaphoreGetMutexHolder(handle.get());
}

} // namespace
