#include "debug_log.h"

#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "FreeRTOS.h"
#include "SEGGER_RTT.h"
#include "task.h"

volatile uint32_t g_rtt_log_seq = 0u;
volatile uint32_t g_rtt_log_drop_count = 0u;
volatile uint32_t g_rtt_log_last_drop_seq = 0u;

static void debug_backend_write(const char *msg)
{
    unsigned len;
    unsigned written;

    if ((msg == NULL) || (msg[0] == '\0')) {
        return;
    }

    len = (unsigned)strlen(msg);
    g_rtt_log_seq += 1u;
    written = SEGGER_RTT_Write(0u, msg, len);
    if (written < len) {
        g_rtt_log_drop_count += 1u;
        g_rtt_log_last_drop_seq = g_rtt_log_seq;
    }
}

void debug_log_init(void)
{
    SEGGER_RTT_Init();
}

void debug_log_write(const char *msg)
{
    if (msg == NULL) {
        return;
    }

    debug_backend_write(msg);
}

void debug_log_printf(const char *fmt, ...)
{
    char buffer[256];
    va_list args;
    int len;

    if (fmt == NULL) {
        return;
    }

    va_start(args, fmt);
    len = vsnprintf(buffer, sizeof(buffer), fmt, args);
    va_end(args);

    if (len < 0) {
        return;
    }

    buffer[sizeof(buffer) - 1u] = '\0';
    debug_backend_write(buffer);
}

void debug_log_boot(const char *tag)
{
    debug_log_printf("[BOOT] %s\r\n", (tag != NULL) ? tag : "(null)");
}

void debug_log_fault(const char *tag)
{
    debug_log_printf("[FAULT] %s\r\n", (tag != NULL) ? tag : "(null)");
}

void debug_log_fault_context(const char *tag, const char *file, int line)
{
    debug_log_printf("[FAULT] %s file=%s line=%d free=%lu min=%lu seq=%lu drop=%lu last_drop=%lu\r\n",
                     (tag != NULL) ? tag : "(null)",
                     (file != NULL) ? file : "(null)",
                     line,
                     (unsigned long)xPortGetFreeHeapSize(),
                     (unsigned long)xPortGetMinimumEverFreeHeapSize(),
                     (unsigned long)g_rtt_log_seq,
                     (unsigned long)g_rtt_log_drop_count,
                     (unsigned long)g_rtt_log_last_drop_seq);
}

void debug_log_runtime_snapshot(void)
{
    UBaseType_t default_hwm;
    size_t free_heap;
    size_t min_heap;

    free_heap = xPortGetFreeHeapSize();
    min_heap = xPortGetMinimumEverFreeHeapSize();
    default_hwm = uxTaskGetStackHighWaterMark(NULL);

    debug_log_printf("[RTOS] free=%lu min=%lu self_hwm=%lu\r\n",
                     (unsigned long)free_heap,
                     (unsigned long)min_heap,
                     (unsigned long)default_hwm);
}

void lwip_platform_assert(const char *msg, const char *file, int line)
{
    debug_log_printf("[FAULT] lwip-assert msg=%s file=%s line=%d\r\n",
                     (msg != NULL) ? msg : "(null)",
                     (file != NULL) ? file : "(null)",
                     line);

    taskDISABLE_INTERRUPTS();
    while (1) {
    }
}
