
#ifndef ETHERCAT_LOGGING_H
#define ETHERCAT_LOGGING_H

#pragma once

#include "../ethercat_types.h"
#include <stdint.h>
#include <string.h>

struct dll;
#ifndef DLL_T_FWD_DECLARED
#define DLL_T_FWD_DECLARED
typedef struct dll dll_t;
#endif

#ifdef __cplusplus
extern "C" {
#endif

#ifndef DYNAMIC_LOAD

void CloseDebugLog(void);

#endif

typedef enum {
    DX_LOG_ERROR   = 0,
    DX_LOG_WARNING = 1,
    LOG_MESSAGE = 2,
    LOG_MAILBOX = 3,
    LOG_PDO     = 4,
    DX_LOG_DEBUG   = 5,
    LOG_LOCAL   = 6
} dx_log_category_t;

typedef struct {

    int64_t timestamp_ms;

    dx_log_category_t category;

    char message[512];

    int slave_index;
} log_entry_t;

typedef void (*log_entry_callback_t)(const log_entry_t* entry);

static inline const char* log_category_name(dx_log_category_t category)
{
    switch (category) {
        case DX_LOG_ERROR:   return "ERROR";
        case DX_LOG_WARNING: return "WARNING";
        case LOG_MESSAGE: return "MESSAGE";
        case LOG_MAILBOX: return "MAILBOX";
        case LOG_PDO:     return "PDO";
        case DX_LOG_DEBUG:   return "DEBUG";
        case LOG_LOCAL:   return "LOCAL";
        default:                return "UNKNOWN";
    }
}

#if defined(DYNAMIC_LOAD) && defined(DLL_T_DEFINED)

static inline void log_set_callback(dll_t* dll,
                                          log_callback_t callback)
{
    if (dll && dll->SetLogCallback)
        dll->SetLogCallback(callback);
}

static inline void log_set_pdo_logging(dll_t* dll, int enable)
{
    if (dll && dll->SetPDOLogging)
        dll->SetPDOLogging(enable);
}

static inline void log_set_mailbox_logging(dll_t* dll, int enable)
{
    if (dll && dll->SetMailboxLogging)
        dll->SetMailboxLogging(enable);
}

static inline void log_set_debug_logging(dll_t* dll, int enable)
{
    if (dll && dll->SetDebugLogging)
        dll->SetDebugLogging(enable);
}

static inline void log_close_debug(dll_t* dll)
{
    if (dll && dll->CloseDebugLog)
        dll->CloseDebugLog();
}

#endif

#define LOG_BUFFER_MAX 1024

typedef struct {
    log_entry_t entries[LOG_BUFFER_MAX];
    int head;
    int count;
} log_buffer_t;

static inline void log_buffer_init(log_buffer_t* buf)
{
    if (!buf) return;
    memset(buf, 0, sizeof(log_buffer_t));
}

static inline void log_buffer_add(log_buffer_t* buf,
                                        const log_entry_t* entry)
{
    if (!buf || !entry) return;

    memcpy(&buf->entries[buf->head], entry, sizeof(log_entry_t));

    buf->head = (buf->head + 1) % LOG_BUFFER_MAX;

    if (buf->count < LOG_BUFFER_MAX)
        buf->count++;
}

static inline const log_entry_t* log_buffer_get(
    const log_buffer_t* buf, int index)
{
    if (!buf || index < 0 || index >= buf->count)
        return NULL;

    int start;
    if (buf->count < LOG_BUFFER_MAX) {

        start = 0;
    } else {

        start = buf->head;
    }
    int physical = (start + index) % LOG_BUFFER_MAX;
    return &buf->entries[physical];
}

static inline int log_buffer_count(const log_buffer_t* buf)
{
    if (!buf) return 0;
    return buf->count;
}

static inline void log_buffer_clear(log_buffer_t* buf)
{
    if (!buf) return;
    buf->head = 0;
    buf->count = 0;
}

#ifdef __cplusplus
}
#endif

#endif
