#include "usbadc.h"
#include <cstring>
#include <cstdlib>
#include <mutex>

#include <zf_log.h>
static wchar_t *str_to_widestr(const char *str)
{
    wchar_t *result;
    mbstate_t mbs;
    size_t len;
    memset(&mbs, 0, sizeof(mbstate_t));
    len = mbsrtowcs(NULL, &str, 0, &mbs);
    if(len == (size_t)(-1))
    {
        return NULL;
    }
    result = (wchar_t*)malloc(sizeof(wchar_t)*(len+1));
    if(result && mbsrtowcs(result, &str, len+1, &mbs) != len)
    {
        free(result);
        return NULL;
    }
    return result;
}
static void zf_log_out_dummy_callback(const zf_log_message *msg, void *data) {}
ZF_LOG_DEFINE_GLOBAL_OUTPUT = {0, 0, zf_log_out_dummy_callback};
struct userimpl_data_t
{
    void *payload;
    usbadc_logging_callback_t cb;
};
static std::mutex callback_setter_mutex;
static void zf_log_out_userimpl_callback(const zf_log_message *msg, void *data)
{
    userimpl_data_t impl;
    {
        std::lock_guard<std::mutex> lock(callback_setter_mutex);
        impl = *(userimpl_data_t *)data;
    }
    unsigned int log_level;
    switch(msg->lvl)
    {
        case ZF_LOG_DEBUG:
        case ZF_LOG_VERBOSE:
            log_level = LOGLEVEL_DEBUG;
            break;
        case ZF_LOG_INFO:
            log_level = LOGLEVEL_INFO;
            break;
        case ZF_LOG_WARN:
            log_level = LOGLEVEL_WARNING;
            break;
        case ZF_LOG_ERROR:
        case ZF_LOG_FATAL:
            log_level = LOGLEVEL_ERROR;
            break;
        default:
            return;
    }
    wchar_t *wmsg = str_to_widestr(msg->buf);
    impl.cb(log_level, wmsg, impl.payload);
    free(wmsg);
}
void usbadc_set_logging_callback(usbadc_logging_callback_t cb, void *data)
{
    std::lock_guard<std::mutex> lock(callback_setter_mutex);
    free(ZF_LOG_GLOBAL_OUTPUT->arg);
    if(cb != NULL)
    {
        userimpl_data_t *impl = (userimpl_data_t *)malloc(sizeof(userimpl_data_t));
        impl->cb = cb;
        impl->payload = data;
        zf_log_set_output_v(ZF_LOG_PUT_STD, (void *)impl, &zf_log_out_userimpl_callback);
    }
    else
    {
        zf_log_set_output_v(0, 0, zf_log_out_dummy_callback);
    }
}

void usbadc_logging_callback_stderr_wide(int loglevel, const wchar_t *message, void *user_data)
{
    if(loglevel > LOGLEVEL_WARNING) return;
    fwprintf(stderr, L"%ls\n", message);
}

void usbadc_logging_callback_stderr_narrow(int loglevel, const wchar_t *message, void *user_data)
{
    if(loglevel > LOGLEVEL_WARNING) return;
    fprintf(stderr, "%ls\n", message);
}

