/*
 * @Author: wzh
 * @Date: 2024-05-06 10:32:36
 * @LastEditors: mypx mypx_coder@163.com
 * @LastEditTime: 2025-09-09 17:26:43
 * @Description: 
 * 
 * Copyright (c) 2024 by wzh, All Rights Reserved. 
 */

#ifndef __ETK_UTILS_H__
#define __ETK_UTILS_H__
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <math.h>
#include "etk_def.h"


/**
 * @ingroup BasicDef
 *
 * @def ETK_ALIGN(size, align)
 * Return the most contiguous size aligned at specified width. ETK_ALIGN(13, 4)
 * would return 16.
 */
#define ETK_ALIGN(size, align) (((size) + (align) - 1) & ~((align) - 1))

/**
  * @ingroup BasicDef
  *
  * @def ETK_ALIGN_DOWN(size, align)
  * Return the down number of aligned at specified width. ETK_ALIGN_DOWN(13, 4)
  * would return 12.
  */
#define ETK_ALIGN_DOWN(size, align) ((size) & ~((align) - 1))

/*
   * ETK_ASSERT is a macro name that can be used in code and takes two
    * arguments: condition and message. If the condition is false,
    * the macro prints an error message with the information and
    * terminates program execution.
  */
#ifndef ETK_ASSERT
#define ETK_ASSERT(condition, message)                                                                           \
    do                                                                                                           \
    {                                                                                                            \
        if (!(condition))                                                                                        \
        {                                                                                                        \
            fprintf(stderr, "Assertion failed: %s, file %s->%s:line %d: %s\r\n", #condition, __FILE__, __func__, \
                    __LINE__, message);                                                                          \
        }                                                                                                        \
    } while (0)
#endif

/**
 * Base checking macro - internal use only
 * @param cond Condition to check
 * @param ret_val Value to return if condition is true
 * @param action Action to perform if condition is true (can be empty)
 * @param log_msg Log message to print (can be NULL)
 */
#define _ERR_BASE_CHECK(cond, ret_val, action, log_msg)                                                  \
    do                                                                                                   \
    {                                                                                                    \
        if (cond)                                                                                        \
        {                                                                                                \
            if (log_msg != NULL)                                                                         \
            {                                                                                            \
                fprintf(stderr, "%s (file: %s, line: %d)\n", (const char *)log_msg, __FILE__, __LINE__); \
            }                                                                                            \
            else                                                                                         \
            {                                                                                            \
                fprintf(stderr, "Error occurred (file: %s, line: %d)\n", __FILE__, __LINE__);            \
            }                                                                                            \
            action;                                                                                      \
            return (ret_val);                                                                            \
        }                                                                                                \
    } while (0)

/**
 * Check if return value is negative, return it if true
 * @param ret Return value to check
 */
#define ERR_CHECK_RET_NEG(ret) _ERR_BASE_CHECK((ret) < 0, (ret), , NULL)

/**
 * Check if return value is non-zero (error), return it if true
 * @param ret Return value to check (0 = success, non-zero = error)
 */
#define ERR_CHECK_RET_NONZERO(ret) _ERR_BASE_CHECK((ret) != 0, (ret), , NULL)

/**
 * Check if return value is non-zero, perform action, then return it
 * @param ret Return value to check (0 = success, non-zero = error)
 * @param action Custom action to execute on error
 */
#define ERR_CHECK_RET_NONZERO_ACT(ret, action) _ERR_BASE_CHECK((ret) != 0, (ret), action, NULL)

/**
 * Check if return value is non-zero, log error, then return it
 * @param ret Return value to check (0 = success, non-zero = error)
 */
#define ERR_CHECK_RET_NONZERO_LOG(ret) _ERR_BASE_CHECK((ret) != 0, (ret), , "Error: Return value " #ret " = %d", (ret))

/**
 * Check if return value is negative, perform action, then return it
 * @param ret Return value to check
 * @param action Custom action to execute on error
 */
#define ERR_CHECK_RET_ACT(ret, action) _ERR_BASE_CHECK((ret) < 0, (ret), action, NULL)

/**
 * Check if expression equals error value, return error value if true
 * @param expr Expression to evaluate
 * @param err_val Value indicating error condition
 */
#define ERR_CHECK_EXPR_RET(expr, err_val) _ERR_BASE_CHECK((expr) == (err_val), (err_val), , NULL)

/**
 * Check if return value is negative, log error, then return it
 * @param ret Return value to check
 */
#define ERR_CHECK_RET_LOG(ret) _ERR_BASE_CHECK((ret) < 0, (ret), , "Error: Return value " #ret " = %d", (ret))

/**
 * Check if pointer is NULL, log error, and perform action
 * @param ptr Pointer to check
 * @param action Custom action to execute on NULL pointer
 */
#define ERR_CHECK_PTR(ptr, action) _ERR_BASE_CHECK((ptr) == NULL, , action, "Error: Pointer " #ptr " is NULL")

/**
 * Extended check: negative return value with custom message and action
 * @param ret Return value to check
 * @param msg Custom error message
 * @param action Custom action to execute on error
 */
#define ERR_CHECK_RET_EXT(ret, msg, action) _ERR_BASE_CHECK((ret) < 0, (ret), action, msg)

/**
 * Extended check: non-zero return value with custom message and action
 * @param ret Return value to check (0 = success, non-zero = error)
 * @param msg Custom error message
 * @param action Custom action to execute on error
 */
#define ERR_CHECK_RET_NONZERO_EXT(ret, msg, action) _ERR_BASE_CHECK((ret) != 0, (ret), action, msg)

typedef void (*task_function_t)(void *args);

/**
 * @brief Checks if two floating-point numbers are approximately equal.
 *
 * This macro compares two floats, `x` and `y`, with a tolerance `epsilon`.
 * If the absolute difference between `x` and `y` is less than `epsilon`,
 * it returns true, indicating they are approximately equal.
 *
 * @param x The first floating-point number.
 * @param y The second floating-point number.
 * @param epsilon The tolerance for comparison, representing the allowed margin of error.
 * @return True if the absolute difference between `x` and `y` is less than `epsilon`.
 */
#define ETK_FLOAT_ALMOST_EQUAL(x, y, epsilon) (fabs((x) - (y)) < (epsilon))

/**
  * @brief Checks if two floating-point numbers are approximately not equal.
  *
  * This macro compares two floats, `x` and `y`, with a tolerance `epsilon`.
  * If the absolute difference between `x` and `y` is greater than `epsilon`,
  * it returns true, indicating they are not approximately equal.
  *
  * @param x The first floating-point number.
  * @param y The second floating-point number.
  * @param epsilon The tolerance for comparison, representing the allowed margin of error.
  * @return True if the absolute difference between `x` and `y` is greater than `epsilon`.
  */
#define ETK_FLOAT_ALMOST_NOT_EQUAL(x, y, epsilon) (fabs((x) - (y)) > (epsilon))

/**
 * @brief Calculates the number of elements in an array.Or rows of a 2D array.
 *
 * @param arr The array whose size is to be determined.
 * @return The number of elements in the array.
 */
#define ETK_ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))

/**
 * @brief Calculates the total number of elements in a 2D array.
 */
#define ETK_2D_ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0][0]))

#define ETK_2D_ARRAY_ROWS(arr) ETK_ARRAY_SIZE(arr)
/**
 * @brief Calculates the number of columns in a 2D array.
 * Calculate the number of columns by dividing the size of a row by the size of a single element
 * @param arr The 2D array whose number of columns is to be determined.
 * @return The number of columns in the 2D array.
 */
#define ETK_2D_ARRAY_COLS(arr) (sizeof(arr[0]) / sizeof(arr[0][0]))

/**
  * Clamps a value to a specified range.
  *
  * This macro takes a value `x` and two boundary values `min_val` and `max_val`.
  * It returns `x` if it lies within the range `[min_val, max_val]`, inclusive.
  * If `x` is less than `min_val`, it returns `min_val`.
  * If `x` is greater than `max_val`, it returns `max_val`.
  *
  * @param x The value to be clamped.
  * @param min_val The minimum value of the range.
  * @param max_val The maximum value of the range.
  * @return The clamped value within the specified range.
  */
#define ETK_CLAMP(x, min_val, max_val) (((x) < (min_val)) ? (min_val) : (((x) > (max_val)) ? (max_val) : (x)))

#define ETK_MAX(a, b) ((a) > (b) ? (a) : (b))
#define ETK_MIN(a, b) ((a) < (b) ? (a) : (b))
#define ETK_ABS(a)    ((a) < 0 ? -(a) : (a))
#define ETK_SIGN(a)   ((a) < 0 ? -1 : 1)
#define ETK_SQRT(a)   sqrtf(a)

/**
 * @brief Check if two floating-point numbers have opposite signs
 * @param a First floating-point number
 * @param b Second floating-point number
 * @return 1: signs are opposite; 0: signs are same or special values (NaN or both zero)
 */
#define ETK_SIGNS_OPPOSITE(a, b) \
    ((isnan(a) || isnan(b) || ((a) == 0.0 && (b) == 0.0)) ? 0 : (((a) < 0) != ((b) < 0)))

#define ETK_AVERAGE(buf, size, result)                                 \
    do                                                                 \
    {                                                                  \
        /* Automatically get the type of array elements */             \
        typeof((buf)[0]) __sum = 0;                                    \
        uint32_t         __i;                                          \
        /* Calculate size once to avoid multiple executions when size is an expression (e.g. size++) */ \
        const uint32_t __size = (size);                                \
        for (__i = 0; __i < __size; __i++)                             \
        {                                                              \
            __sum += (buf)[__i];                                       \
        }                                                              \
        /* Result automatically matches array element type */          \
        (result) = (typeof(__sum))(__sum / __size);                    \
    } while (0)

#endif