/*
 * @Author: wzh
 * @Date: 2023-08-22 15:12:20
 * @LastEditors: mypx mypx_coder@163.com
 * @LastEditTime: 2025-09-22 13:30:48
 * @Description: Enhanced logging system implementation with color support
 *
 * Copyright (c) 2023 by Inomec, All Rights Reserved.
 */
#include "et_log.h"
#include <assert.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* global logging configuration variables */
static int  logging_level    = DEBUG_LEVEL; /* current logging level */
static bool fn_line_enable   = true;        /* enable function name and line number */
static bool timestamp_enable = true;        /* enable timestamp in logs */
static et_timestamp_func_t timestamp_func = NULL; /* timestamp function pointer */

/* specific log level colors */
#define COLOR_DEBUG COLOR_BOLD COLOR_WHITE   /* debug log color */
#define COLOR_INFO  COLOR_BOLD COLOR_MAGENTA /* info log color */
#define COLOR_WARN  COLOR_BOLD COLOR_YELLOW  /* warning log color */
#define COLOR_ERROR COLOR_BOLD COLOR_RED     /* error log color */

/* extract filename from full path */
#define FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)

/**
 * @brief macro for unified output across different platforms
 * @param fmt - format string
 * @param ... - arguments
 */
#define LOG_OUTPUT(fmt, ...)      \
    do                            \
    {                             \
        printf(fmt, ##__VA_ARGS__); \
    } while (0)

/**
 * @brief Get current timestamp
 * @return Current timestamp value
 */
static unsigned long get_timestamp(void)
{
    if (timestamp_func != NULL) {
        return timestamp_func();
    }
    return 0;
}

/**
 * @brief optimized log output function with single printf call
 * @param color - color code string
 * @param level - log level string
 * @param func - function name
 * @param line - line number
 * @param fmt - format string
 * @param args - variable arguments
 * 
 * optimization improvements:
 * - single printf call instead of multiple calls
 * - reduced string concatenation operations
 * - better buffer management
 * - improved performance for high-frequency logging
 */
static void log_output(const char *color, const char *level, const char *func, int line, const char *fmt, va_list args)
{
    char buffer[ET_LOG_BUF_SIZE];
    char log_buffer[ET_LOG_BUF_SIZE];
    int  buffer_pos = 0;

    /* format the message first */
    vsnprintf(buffer, sizeof(buffer), fmt, args);

    /* build complete log message in single buffer */
    if (timestamp_enable)
    {
        unsigned long timestamp = get_timestamp();

        if (fn_line_enable)
            buffer_pos = snprintf(log_buffer, sizeof(log_buffer), "%s[%lu] [%s] %s:%d] %s%s\r\n", color, timestamp,
                                  level, func, line, buffer, COLOR_RESET);
        else
            buffer_pos = snprintf(log_buffer, sizeof(log_buffer), "%s[%lu] [%s] %s%s\r\n", color, timestamp, level,
                                  buffer, COLOR_RESET);
    }
    else
    {
        if (fn_line_enable)
            buffer_pos = snprintf(log_buffer, sizeof(log_buffer), "%s[%s] %s:%d] %s%s\r\n", color, level, func, line,
                                  buffer, COLOR_RESET);
        else
            buffer_pos = snprintf(log_buffer, sizeof(log_buffer), "%s[%s] %s%s\r\n", color, level, buffer, COLOR_RESET);
    }

    /* ensure buffer null-termination and output */
    if (buffer_pos > 0 && buffer_pos < (int)sizeof(log_buffer))
    {
        log_buffer[buffer_pos] = '\0';
        printf("%s", log_buffer);
    }
    else
    {
        /* fallback to original method if buffer overflow */
        printf("%s%s%s", color, buffer, COLOR_RESET);
    }
}

/**
 * @brief output raw message with color formatting (optimized)
 * @param color - color code string
 * @param fmt - format string
 * @param args - variable arguments
 * 
 * optimization improvements:
 * - removed incorrect va_end(args) call (caller should handle va_list)
 * - unified output interface for better maintainability
 */
static void log_output_raw_color(const char *color, const char *fmt, va_list args)
{
    char buffer[ET_LOG_BUF_SIZE];
    vsnprintf(buffer, sizeof(buffer), fmt, args);

#if (SYSTEM_TYPE == SYS_RTTHREAD)
    rt_kprintf("%s%s%s", color, buffer, COLOR_RESET);
#else
    printf("%s%s%s", color, buffer, COLOR_RESET);
#endif
}

/**
 * @brief print formatted message to console (optimized)
 * @param fmt - format string
 * @param ... - variable arguments
 * 
 * optimization improvements:
 * - unified output interface using helper macro
 * - reduced code duplication
 * - better error handling
 */
void et_print(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
#if (SYSTEM_TYPE == SYS_RTTHREAD)
    char buffer[ET_LOG_BUF_SIZE]; /* buffer length control */
    vsnprintf(buffer, sizeof(buffer), fmt, args);
    rt_kprintf("%s", buffer);
#else
    vprintf(fmt, args);
#endif
    va_end(args);
}


/**
 * @brief macro to reduce code duplication in log level functions
 * @param level - log level constant
 * @param color - color code for this level
 * @param level_str - string representation of level
 */
#define LOG_LEVEL_FUNCTION(level, color, level_str)                       \
    void et_##level_str(const char *func, int line, const char *fmt, ...) \
    {                                                                     \
        if (logging_level >= level)                                       \
        {                                                                 \
            va_list args;                                                 \
            va_start(args, fmt);                                          \
            log_output(color, #level_str, func, line, fmt, args);         \
            va_end(args);                                                 \
        }                                                                 \
    }

/**
 * @brief output debug level log message (optimized)
 * @param func - function name
 * @param line - line number
 * @param fmt - format string
 * @param ... - variable arguments
 * 
 * optimization improvements:
 * - uses macro to reduce code duplication
 * - consistent error handling
 * - better maintainability
 */
LOG_LEVEL_FUNCTION(DEBUG_LEVEL, COLOR_DEBUG, dbg)

/**
 * @brief output info level log message (optimized)
 * @param func - function name
 * @param line - line number
 * @param fmt - format string
 * @param ... - variable arguments
 */
LOG_LEVEL_FUNCTION(INFO_LEVEL, COLOR_INFO, info)

/**
 * @brief output warning level log message (optimized)
 * @param func - function name
 * @param line - line number
 * @param fmt - format string
 * @param ... - variable arguments
 */
LOG_LEVEL_FUNCTION(WARNING_LEVEL, COLOR_WARN, warn)

/**
 * @brief output error level log message (optimized)
 * @param func - function name
 * @param line - line number
 * @param fmt - format string
 * @param ... - variable arguments
 */
LOG_LEVEL_FUNCTION(ERROR_LEVEL, COLOR_ERROR, err)

/**
 * @brief print float value with specified precision (optimized)
 * @param num - float value to print
 * @param precision - number of decimal places (0-6)
 * 
 * optimization improvements:
 * - unified output interface using LOG_OUTPUT macro
 * - reduced code duplication from conditional compilation
 * - better error handling and validation
 * - improved readability and maintainability
 */
void et_print_float(float num, int precision)
{
    /* validate and clamp precision range */
    precision = (precision < 0) ? 0 : (precision > 6) ? 6 : precision;

    /* handle special floating-point values */
    if (isnan(num) || isinf(num))
    {
        LOG_OUTPUT("Invalid");
        return;
    }

    /* validate numeric range for 32-bit integer representation */
    if (num > 2147483647.0f || num < -2147483648.0f)
    {
        LOG_OUTPUT("OutRange:%d", (int)num);
        return;
    }

    /* extract sign and make positive for processing */
    int sign = (num < 0) ? -1 : 1;
    if (sign == -1)
    {
        num = -num;
        LOG_OUTPUT("-");
    }

    /* extract integer and fractional parts */
    int   integer_part    = (int)num;
    float fractional_part = num - integer_part;

    /* output integer part */
    LOG_OUTPUT("%d", integer_part);

    /* output fractional part with specified precision */
    if (precision > 0)
    {
        LOG_OUTPUT(".");
        for (int i = 0; i < precision; i++)
        {
            fractional_part *= 10;
            int digit        = (int)fractional_part;
            LOG_OUTPUT("%d", digit);
            fractional_part -= digit;
        }
    }
}


/**
 * @brief assert with custom message (optimized)
 * @param condition - condition to check
 * @param condition_str - string representation of condition
 * @param file - file name
 * @param line - line number
 * @param message - error message
 * 
 * optimization improvements:
 * - enhanced error message formatting
 * - added buffer overflow protection
 * - better debug information
 * - consistent error handling
 */
void et_assert_msg(int condition, const char *condition_str, const char *file, int line, const char *message)
{
    if (!condition)
    {
        char assert_buffer[ET_LOG_BUF_SIZE];
        int  buffer_len = snprintf(
            assert_buffer, sizeof(assert_buffer), "Assertion failed: %s, file %s, line %d: %s\r\n",
            condition_str ? condition_str : "NULL", file ? file : "UNKNOWN", line, message ? message : "No message");

        /* ensure null-termination and handle buffer overflow */
        if (buffer_len > 0 && buffer_len < (int)sizeof(assert_buffer))
        {
            assert_buffer[buffer_len] = '\0';
            fprintf(stderr, "%s", assert_buffer);
        }
        else
        {
            /* fallback to simple output if buffer overflow */
            fprintf(stderr, "Assertion failed at %s:%d\r\n", file ? file : "UNKNOWN", line);
        }

        abort(); /* terminate program abnormally for debugging */
    }
}

/**
 * @brief print raw message with color formatting (optimized)
 * @param color - color code string
 * @param fmt - format string
 * @param ... - variable arguments
 * 
 * optimization improvements:
 * - added parameter validation
 * - better error handling
 * - consistent with other optimized functions
 */
void et_print_raw_with_color(const char *color, const char *fmt, ...)
{
    /* validate parameters */
    if (!color || !fmt)
    {
        LOG_OUTPUT("Invalid parameters in et_print_raw_with_color\r\n");
        return;
    }

    va_list args;
    va_start(args, fmt);
    log_output_raw_color(color, fmt, args);
    va_end(args);
}

/**
 * @brief initialize logging system configuration (optimized)
 * @param level - log level (DEBUG:3, INFO:2, WARN:1, ERROR:0)
 * @param line_state - enable/disable function name and line number
 * @param ts_state - enable/disable timestamp
 * 
 * optimization improvements:
 * - enhanced parameter validation
 * - added configuration feedback
 * - better error handling
 * - consistent initialization pattern
 */
void et_log_init(int level, bool line_state, bool ts_state)
{
    /* validate and clamp log level to valid range */
    level = (level > DEBUG_LEVEL) ? DEBUG_LEVEL : (level < ERROR_LEVEL) ? ERROR_LEVEL : level;

    /* set logging configuration with validation */
    logging_level    = level;
    fn_line_enable   = line_state;
    timestamp_enable = ts_state;
    timestamp_func   = NULL;  // Reset to default behavior

    /* optional: log initialization success (commented out to avoid recursion) */
    /* LOG_OUTPUT("Logging system initialized: level=%d, fn_line=%s, timestamp=%s\r\n", 
               level, line_state ? "enabled" : "disabled", ts_state ? "enabled" : "disabled"); */
}

/**
 * @brief Initialize logging system with external timestamp function
 * @param level - log level (DEBUG:3, INFO:2, WARN:1, ERROR:0)
 * @param line_state - enable/disable function name and line number
 * @param ts_state - enable/disable timestamp
 * @param timestamp_func - function pointer to get timestamp, can be NULL to disable
 */
void et_log_init_ex(int level, bool line_state, bool ts_state, et_timestamp_func_t ts_func)
{
    /* validate and clamp log level to valid range */
    level = (level > DEBUG_LEVEL) ? DEBUG_LEVEL : (level < ERROR_LEVEL) ? ERROR_LEVEL : level;

    /* set logging configuration with validation */
    logging_level    = level;
    fn_line_enable   = line_state;
    timestamp_enable = ts_state;
    timestamp_func   = ts_func;

    /* optional: log initialization success (commented out to avoid recursion) */
    /* LOG_OUTPUT("Logging system initialized: level=%d, fn_line=%s, timestamp=%s, custom_ts=%s\r\n", 
               level, line_state ? "enabled" : "disabled", ts_state ? "enabled" : "disabled",
               ts_func ? "yes" : "no"); */
}