/*
 * swo_log_backend.c
 *
 * Created on: 21.04.2025
 * Author: Kilian
 *
 * SWO backend implementation for the logging system
 */

#include "swo_log_backend.h"
#include "log.h"

/* Global variables */
#if USE_CHANNEL_MASK_VARIABLE
volatile uint32_t MASK_VARIABLE = 0b11111;  // LOG_NOISY off by default
#endif

static bool swo_backend_registered = false;

/* SWO utility functions */
static inline bool __ITM_channel_enabled(uint32_t channel) {
#if !USE_MULTIPLE_CHANNELS
#if USE_CHANNEL_MASK_VARIABLE
  return ((ITM->TER & (1UL << DEBUG_CHANNEL)) != 0UL) &&
         ((MASK_VARIABLE & (1UL << channel)) != 0UL);
#else
  channel = DEBUG_CHANNEL;
#endif
#endif
  return ((ITM->TER & (1UL << channel)) != 0UL);
}

// adapted from ITM_SendChar() in the CMSIS-Core
// channel should be between 0 and 31
static inline uint32_t __swo_putc(uint32_t c, unsigned int channel) {
#if !USE_MULTIPLE_CHANNELS
  channel = DEBUG_CHANNEL;
#endif
  if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) && /* ITM enabled */
      ((ITM->TER & (1UL << channel)) != 0UL))     /* ITM Port enabled */
  {
    while (ITM->PORT[channel].u32 == 0UL) {
      __NOP();
    }
    ITM->PORT[channel].u8 = (uint8_t)c;
  }
  return (c);
}

#define DEBUG_CHANNEL_ENABLED(channel)                                         \
  ({                                                                           \
    unsigned int ch = (channel);                                               \
    (ch < 32) ? __ITM_channel_enabled(ch) : false;                             \
  })

[[gnu::nonnull(2), gnu::null_terminated_string_arg(2)]]
static inline void __swo_print(unsigned int channel, const char *str) {
  if (!__ITM_channel_enabled(channel)) {
    return;
  }
  while (*str) {
    __swo_putc(*str++, channel);
  }
}

/* SWO backend functions */
static void swo_backend_init(void);
static bool swo_backend_is_enabled(log_level_t level);
static void swo_backend_write(const log_message_t* message);
static void swo_backend_flush(void);
static void swo_backend_clear(void);

/* SWO backend definition */
static const log_backend_t swo_backend = {
  .name = "SWO",
  .init = swo_backend_init,
  .is_enabled = swo_backend_is_enabled,
  .write = swo_backend_write,
  .flush = swo_backend_flush,
  .clear = swo_backend_clear
};

/* Return the SWO backend definition */
const log_backend_t* swo_log_get_backend(void) {
    return &swo_backend;
}

/* Check if the SWO backend is registered */
bool swo_log_is_backend_registered(void) {
    return swo_backend_registered;
}

/* Initialize the SWO backend and register it */
void swo_log_backend_init(void) {
    /* Register the backend */
    swo_backend_registered = log_register_backend(&swo_backend);
}

/* SWO backend initialization */
static void swo_backend_init(void) {
    /* SWO is initialized by the debugger, nothing to do here */
}

/* Check if the SWO backend is enabled for the given log level */
static bool swo_backend_is_enabled(log_level_t level) {
#if USE_MULTIPLE_CHANNELS
    return DEBUG_CHANNEL_ENABLED(level);
#else
    return (level <= LOG_LEVEL_NOISY) && DEBUG_CHANNEL_ENABLED(level);
#endif
}

/* Write a message to the SWO backend */
static void swo_backend_write(const log_message_t* message) {
    if (message == NULL) {
        return;
    }
    
#if USE_MULTIPLE_CHANNELS
    __swo_print(message->level, message->message);
#else
    __swo_print(DEBUG_CHANNEL, message->message);
#endif
}

/* Flush the SWO backend - not needed for SWO */
static void swo_backend_flush(void) {
    /* SWO is a real-time interface, no buffering, nothing to flush */
}

/* Clear the console for SWO */
static void swo_backend_clear(void) {
#if USE_ANSI_ESCAPE_CODES
    __swo_print(DEBUG_CHANNEL, "\033[2J\033[H"); // ANSI escape code to clear screen and move cursor to top-left
#endif
}