/**
 * @file sh_vset.h
 * @author LokLiang
 * @brief shell 模块专用，可用于通过命令行中的参数设置变量的基本接口
 * @version 0.1
 * @date 2023-09-22
 *
 * @copyright Copyright (c) 2023
 *
 * 本模块实现解析字符，并根据解析的结果对常见类型的的变量赋值的功能。
 *
 * 特性：
 * - 自动分析设置变量的具体类型
 * - 对取值范围限制
 * - 可通过命令行的形式进行设置
 * - 每增加一项写入接口最低消耗 38 个字节的代码空间
 *
 * 使用：
 * 1. 使用 vset_init() 指定内部的 sh 模块对象
 * 2. 使用对应的宏作为设置函数，其中：
 * - @b SET_VAR     用于设置无符号整形、带符号整型、单精度的浮点数和字符串的变量
 * - @b SET_ENUM    用于设置枚举型的变量，支持 SET_VAR 所支持的全部类型
 * - @b SET_CP_ENUM 对应 SET_ENUM 所设置的变量的可用自动补全函数
 * 3. 如果输入的参数为 ? 可用于打印当前的设置范围。
 * 4. 通过选项设置参数，其中：
 * - @b PSET_FN     指定一个已定义的 const sh_vset_param_t* 的结构作为选项
 * - @b PSET_CP     根据一个已定义的 const sh_vset_param_t* 执行自动补选项
 *
 * 实例1: 以下这些函数可被 SH_SETUP_CMD 中定义的 FUNC 执行，或者作为 sh_vset_param_t::set_func 的成员
 * static int _value_set_u8(const char *argv[])    { return SET_VAR(&var_u8, 1, 200); }
 * static int _value_set_s16(const char *argv[])   { return SET_VAR(&var_s16, -1000, 1000); }
 * static int _value_set_float(const char *argv[]) { return SET_VAR(&var_float, -1000, var_u8); }
 * static int _value_set_str(const char *argv[])   { return SET_VAR(&var_str, 0, 0); }
 * static int _value_set_enum(const char *argv[])  { return SET_ENUM(&var_s32, "enable=1,disable=0"); }
 *
 * 实例2: 实现选项+参数的格式
 * static sh_vset_param_t const s_param_template[] = {
 *     {"--u8", "该选项的帮助信息", value_set_u8, NULL},
 *     {"--s16", "该选项的帮助信息", value_set_s16, NULL},
 *     {"--float", "该选项的帮助信息", value_set_float, NULL},
 *     {"--str", "该选项的帮助信息", value_set_str, NULL},
 *     {"--enum", "该选项的帮助信息", value_set_enum, "enable=1,disable=0"},
 * static int _value_set(sh_t *sh_hdl, int argc, const char *argv[])            { PSET_FN(s_param_template); } // 这个函数可作为 SH_SETUP_CMD 中定义的 FUNC
 * static void _value_cp(sh_t *sh_hdl, int argc, const char *argv[], bool flag) { PSET_CP(s_param_template); } // 这个函数可作为 SH_SETUP_CMD 中定义的 SUB
 *
 */

#ifndef __SH_VSET_H__
#define __SH_VSET_H__

#include "sys_types.h"
#include "sh.h"

/* 描述支待的待设置变量的具体类型 */
typedef enum
{
    __TYPE_ATTR_CHR,
    __TYPE_ATTR_BOOL,
    __TYPE_ATTR_U8,
    __TYPE_ATTR_S8,
    __TYPE_ATTR_U16,
    __TYPE_ATTR_S16,
    __TYPE_ATTR_U32,
    __TYPE_ATTR_S32,
    __TYPE_ATTR_U64,
    __TYPE_ATTR_S64,
    __TYPE_ATTR_FLOAT,
    __TYPE_ATTR_DOUBLE,
    __TYPE_ATTR_STR,
    __TYPE_ATTR_OTHER,
} __type_attr_t;

/**
 * @brief vset_cb
 * 当一个参数的设置值合法，即将被执行设置前回调的函数。当回调退出后才会更新到目标变量中。
 * @param new_value 指向 sh_vset 内部的新值的栈内存地址。提示：可配合 __typeof() 强制转换为确定的类型。
 */
typedef void (*vset_cb)(sh_t *sh_hdl, void *new_value);

#define __GENERIC_ATTR(VAR) (__builtin_types_compatible_p(__typeof(VAR), char)                ? __TYPE_ATTR_CHR    \
                             : __builtin_types_compatible_p(__typeof(VAR), volatile char)     ? __TYPE_ATTR_CHR    \
                             : __builtin_types_compatible_p(__typeof(VAR), bool)              ? __TYPE_ATTR_BOOL   \
                             : __builtin_types_compatible_p(__typeof(VAR), volatile bool)     ? __TYPE_ATTR_BOOL   \
                             : __builtin_types_compatible_p(__typeof(VAR), uint8_t)           ? __TYPE_ATTR_U8     \
                             : __builtin_types_compatible_p(__typeof(VAR), volatile uint8_t)  ? __TYPE_ATTR_U8     \
                             : __builtin_types_compatible_p(__typeof(VAR), int8_t)            ? __TYPE_ATTR_S8     \
                             : __builtin_types_compatible_p(__typeof(VAR), volatile int8_t)   ? __TYPE_ATTR_S8     \
                             : __builtin_types_compatible_p(__typeof(VAR), uint16_t)          ? __TYPE_ATTR_U16    \
                             : __builtin_types_compatible_p(__typeof(VAR), volatile uint16_t) ? __TYPE_ATTR_U16    \
                             : __builtin_types_compatible_p(__typeof(VAR), int16_t)           ? __TYPE_ATTR_S16    \
                             : __builtin_types_compatible_p(__typeof(VAR), volatile int16_t)  ? __TYPE_ATTR_S16    \
                             : __builtin_types_compatible_p(__typeof(VAR), uint32_t)          ? __TYPE_ATTR_U32    \
                             : __builtin_types_compatible_p(__typeof(VAR), volatile uint32_t) ? __TYPE_ATTR_U32    \
                             : __builtin_types_compatible_p(__typeof(VAR), int32_t)           ? __TYPE_ATTR_S32    \
                             : __builtin_types_compatible_p(__typeof(VAR), volatile int32_t)  ? __TYPE_ATTR_S32    \
                             : __builtin_types_compatible_p(__typeof(VAR), uint64_t)          ? __TYPE_ATTR_U64    \
                             : __builtin_types_compatible_p(__typeof(VAR), volatile uint64_t) ? __TYPE_ATTR_U64    \
                             : __builtin_types_compatible_p(__typeof(VAR), int64_t)           ? __TYPE_ATTR_S64    \
                             : __builtin_types_compatible_p(__typeof(VAR), volatile int64_t)  ? __TYPE_ATTR_S64    \
                             : __builtin_types_compatible_p(__typeof(VAR), float)             ? __TYPE_ATTR_FLOAT  \
                             : __builtin_types_compatible_p(__typeof(VAR), volatile float)    ? __TYPE_ATTR_FLOAT  \
                             : __builtin_types_compatible_p(__typeof(VAR), double)            ? __TYPE_ATTR_DOUBLE \
                             : __builtin_types_compatible_p(__typeof(VAR), volatile double)   ? __TYPE_ATTR_DOUBLE \
                             : __builtin_types_compatible_p(__typeof(VAR), char[])            ? __TYPE_ATTR_STR    \
                                                                                              : __TYPE_ATTR_OTHER)

typedef int (*vset_var_fn)(const char *argv[]);

typedef struct // 用于长选项设置的描述结构
{
    const char *option;   // 选项，如 "--value"
    const char *help;     // 对该选项的描述
    vset_var_fn set_func; // 与 option 对应的，使用宏 SET_VAR() 或 SET_ENUM() 设置变量的函数。如果值为 NULL 表示该选项无参数，同时对应的输入参数被保留
    const char *enum_str; // 仅在类型为 vset_enum_fn 时有效，为对应的选项提供可选的补全参数选项，值为 NULL 或 "" 时默认候选参数为 '?'
} sh_vset_param_t;

int vset_unsigned(void *dest, __type_attr_t attr, vset_cb cb, const char *argv[], unsigned int low, unsigned int high);
int vset_integer(void *dest, __type_attr_t attr, vset_cb cb, const char *argv[], signed int low, signed int high);
int vset_float(void *dest, __type_attr_t attr, vset_cb cb, const char *argv[], float low, float high);
int vset_str(void *dest, __type_attr_t attr, vset_cb cb, const char *argv[], unsigned bufsize);
int vset_enum(void *dest, __type_attr_t attr, vset_cb cb, const char *argv[], const char *enum_str);
void vset_cp_enum(int argc, bool flag, const char *enum_str);

int vset_option_set(sh_t *sh_hdl, int *argc, const char *argv[], const sh_vset_param_t *p, unsigned size);
bool vset_option_cp(sh_t *sh_hdl, int argc, const char *argv[], bool flag, const sh_vset_param_t *p, unsigned size);

/* 自动分析并设置变量的值，带设置回调 */
#define SET_VAR_CB(NAME, LOW, HIGH, CB)                                                            \
    ((__GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_U8 ||                                                 \
      __GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_U16 ||                                                \
      __GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_U32 ||                                                \
      __GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_U64)                                                  \
         ? vset_unsigned(NAME, __GENERIC_ATTR(*(NAME)), CB, argv, LOW, HIGH)                       \
         : ((__GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_CHR ||                                         \
             __GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_BOOL ||                                        \
             __GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_S8 ||                                          \
             __GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_S16 ||                                         \
             __GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_S32 ||                                         \
             __GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_S64)                                           \
                ? vset_integer(NAME, __GENERIC_ATTR(*(NAME)), CB, argv, LOW, HIGH)                 \
                : ((__GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_FLOAT ||                                \
                    __GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_DOUBLE)                                 \
                       ? vset_float(NAME, __GENERIC_ATTR(*(NAME)), CB, argv, LOW, HIGH)            \
                       : ((__GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_STR)                             \
                              ? vset_str(NAME, __GENERIC_ATTR(*(NAME)), CB, argv, sizeof(*(NAME))) \
                              : -1))))

/* 设置数据类型为 枚举型 的变量，带设置回调  */
#define SET_ENUM_CB(NAME, ENUM_STR, CB)           \
    (vset_enum(NAME, __GENERIC_ATTR(*(NAME)), CB, \
               argv, ENUM_STR))

/* 自动分析并设置变量的值 */
#define SET_VAR(NAME, LOW, HIGH) SET_VAR_CB(NAME, LOW, HIGH, NULL)

/* 设置数据类型为 枚举型 的变量 */
#define SET_ENUM(NAME, ENUM_STR) SET_ENUM_CB(NAME, ENUM_STR, NULL)

/* 对应 SET_ENUM 所设置的变量的可用自动补全函数 */
#define SET_CP_ENUM(ENUM_STR)               \
    do                                      \
    {                                       \
        vset_cp_enum(argc, flag, ENUM_STR); \
    } while (0)

#define PSET_FN(OPT) vset_option_set(sh_hdl, &argc, argv, OPT, sizeof(OPT))     /* 作为 SH_CMD_FN() 的实际执行函数 */
#define PSET_CP(OPT) vset_option_cp(sh_hdl, argc, argv, flag, OPT, sizeof(OPT)) /* 作为 SH_CMD_CP_FN() 的实际执行函数 */

typedef void (*vset_global_cb)(sh_t *sh_hdl);

void vset_init(sh_t *sh_hdl, vset_global_cb global_cb);

void vset_force_cb(void);

/** 定义设置变量的简化形式 ***************************************************************************************/

/* 对应 SET_VAR 所设置的变量的可用自动补全函数，默认补全一个 '?' 号 */
void vset_cp_hint(sh_t *sh_hdl, int argc, const char *argv[], bool flag);

/**
 * @brief 实例化一个变量的设置函数。
 * @param NAME  函数名称
 * @param VAR   静态变量地址，支持的类型有：无符号整形、带符号整型、单精度的浮点数
 * @param LOW   变量的最小值。变量值可用
 * @param HIGH  变量的最大值。变量值可用
 * @param CB    设置变量的回调函数。注意：只有在设置变量的值合法并且新的值与原值不相等时才会被调用。也可取值为 NULL
 */
#define VSET_VAR(NAME, VAR, LOW, HIGH, CB)                     \
    SH_CMD_FN(NAME) { return SET_VAR_CB(VAR, LOW, HIGH, CB); } \
    SH_CMD_CP_FN(_SH_CONCAT(NAME, _tab)) { vset_cp_hint(sh_hdl, argc, argv, flag); }

/**
 * @brief 实例化一个字符串的设置函数。
 * @param NAME  函数名称
 * @param VAR   静态字符串数组的地址
 * @param CB    设置变量的回调函数。注意：只有在设置变量的值合法并且新的值与原值不相等时才会被调用。也可取值为 NULL
 */
#define VSET_STR(NAME, VAR, CB)                           \
    SH_CMD_FN(NAME) { return SET_VAR_CB(VAR, 0, 0, CB); } \
    SH_CMD_CP_FN(_SH_CONCAT(NAME, _tab)) { vset_cp_hint(sh_hdl, argc, argv, flag); }

/**
 * @brief 实例化一个枚举型变量的设置函数。
 * @param NAME      函数名称
 * @param VAR       静态变量地址，支持的类型有：无符号整形、带符号整型，不支持浮点数
 * @param ENUM_STR  枚举型变量的可选值
 * @param CB        设置变量的回调函数。注意：只有在设置变量的值合法并且新的值与原值不相等时才会被调用。也可取值为 NULL
 */
#define VSET_ENUM(NAME, VAR, ENUM, CB)                     \
    SH_CMD_FN(NAME) { return SET_ENUM_CB(VAR, ENUM, CB); } \
    SH_CMD_CP_FN(_SH_CONCAT(NAME, _tab)) { SET_CP_ENUM(ENUM); }

/**
 * @brief 实例化一个选项的设置函数。
 * @param NAME  函数名称
 * @param ...   可变参数，选项描述结构体的数组。成员格式： {"选项", "选项的帮助信息", 设置函数, 枚举型变量的可选值 }
 * @example
 * @code
 * static int _vset_int(const char *argv[]) { return SET_VAR_CB(&g_int32, -10, 10, _vset_cb_int32); }
 * static int _vset_str(const char *argv[]) { return SET_VAR_CB(&g_str, 0, 0, _vset_cb_char); }
 * static int _vset_enum(const char *argv[]) { return SET_ENUM_CB(&g_int32, s_enum_str, _vset_cb_int32); }
 * VSET_OPTION(
 *     _example,
 *     {"--int32", "int32_t 类型的变量", _vset_int, NULL},
 *     {"--str", "字符串 类型的变量", _vset_str, NULL},
 *     {"--enum", "枚举类型的变量", _vset_enum, s_enum_str})
 * @endcode
 */
#define VSET_OPTION(NAME, ...)                                    \
    static sh_vset_param_t const _SH_CONCAT(_opts_, NAME)[] = {   \
        __VA_ARGS__};                                             \
    SH_CMD_FN(NAME) { return PSET_FN(_SH_CONCAT(_opts_, NAME)); } \
    SH_CMD_CP_FN(_SH_CONCAT(NAME, _tab)) { PSET_CP(_SH_CONCAT(_opts_, NAME)); }

/**
 * @brief 可替代 SH_SETUP_CMD 定义命令列表 SH_REGISTER_CMD 或 SH_DEF_SUB_CMD 中的成员
 * @param CMD   命令（字符串，不要有空格）
 * @param HELP  命令的帮助信息（字符串）
 * @param FUNC  由 VSET_VAR()、VSET_STR()、VSET_ENUM()、VSET_OPTION() 定义的函数
 * @example
 * @code
 * SH_DEF_SUB_CMD(_register_sample,
 *                VSET_SETUP_CMD("ex1", "设置单个 数值 变量", _ex1),   // ex1 <数值>
 *                VSET_SETUP_CMD("ex2", "设置单个 字符串 变量", _ex2), // ex2 <字符串>
 *                VSET_SETUP_CMD("ex3", "设置单个 枚举 变量", _ex3),   // ex3 <枚举>
 *                VSET_SETUP_CMD("ex4", "设置有多个选项的变量", _ex4), // ex4 [选项<值>] [选项<值>] ...
 * );
 * @endcode
 */
#define VSET_SETUP_CMD(CMD, HELP, FUNC) SH_SETUP_CMD(CMD, HELP, FUNC, FUNC##_tab)

/**
 * @note 对本函数的进一步说明。
 *
 * 在 sh 模块设计完成后，发现除了用于执行某些定义好的的操作外，更多情况下是用于设置变量。
 * 为了更好的支持这种需求，本模块应运而生。
 * 本模块的设计目标是通过命令行的形式设置变量，同时支持对变量的取值范围限制。
 * 本模块的设计思路是通过宏定义，将变量的类型和取值范围传递给 SET_VAR() 或 SET_ENUM() 进行具体的设置。
 * 本模块主要使用到的宏定义有：
 * - @b SET_VAR()
 * - @b SET_ENUM()
 * - @b SET_CP_ENUM()
 * - @b PSET_FN()
 * - @b PSET_CP()
 *
 * 这些都是根据实际的使用场景定义的。
 * 根据 sh 模块的主体功能，分为两个部分（可在使用 SH_SETUP_CMD() 定义命令时的第三和第四个参数来体现）：
 * - 一个是用于执行某些操作的函数。
 * - 另一个是用于像 bash 一样，按 Tab 键自动补全的函数。
 * 对于前者，可在实际执行函数中直接使用 SET_VAR() 或 SET_ENUM() 来设置变量。
 * 对于后者，可在实际补全函数中直接使用 SET_CP_ENUM() 来提示可选项和可选值。
 *
 * 为什么设置宏要分两种？
 * SET_VAR() 和 SET_ENUM() 的本质区别是， SET_VAR() 用于设置无符号整形、带符号整型、单精度的浮点数和字符串的变量，它们的取值范围是连续的。
 *      值得注意的是： SET_VAR() 也支持字符串的设置，但是字符串的取值范围是无法限制的。
 * 而 SET_ENUM() 用于设置枚举型的变量，它的取值范围是允许离散的，并且可用一些字符串来表示具体的取值。
 *
 * 为什么补全函数只有 SET_CP_ENUM() ？
 * 因为 SET_ENUM() 的取值范围是离散的，所以需要一个额外的函数来提供可选的补全参数选项。对于连续的取值范围，不需要这个函数。
 *
 * 设置宏较为独立，虽然它是为了配合本模块而设计的，但是它的功能是独立的，在传输的参数中的 sh_hdl 主要是为了回显一些错误信息。
 * 补全宏则是为了配合 sh 模块的补全功能而设计的，它的功能是为了提供可选的补全参数选项。
 *
 * 小结：
 * - SET_VAR() 和 SET_ENUM() 用于设置变量的值。
 * - SET_CP_ENUM() 用于提供可选的补全参数选项。
 * - 它们可能在命令执行函数和补全函数中直接使用。
 *
 * 接着新问题来了，使用 SET_VAR() 或 SET_ENUM() 时，只能设置一个变量，如果这个命令要包含多种设置，该怎么办？
 * 为了解决这个问题，本模块引入了 sh_vset_param_t 结构体，它是一个数组，用于描述支待的待设置变量的具体类型。
 * 它统一描述了该命令的所有选项，包括选项的名称、选项的描述、选项的设置函数和选项的可选值。
 * 在命令执行函数和补全函数中，通过调用 PSET_FN() 和 PSET_CP() 来执行具体的设置。
 * 这两个宏的参数就是 sh_vset_param_t 结构体的数组。
 * 这个结构体的定义是固定的，它的成员包括：
 * - 选项，如 "--value"
 * - 对该选项的描述
 * - 与 option 对应的，使用宏 SET_VAR() 或 SET_ENUM() 设置变量的函数。如果值为 NULL 表示该选项无参数，同时对应的输入参数被保留
 * - 仅在类型为 vset_enum_fn 时有效，为对应的选项提供可选的补全参数选项，值为 NULL 或 "" 时默认候选参数为 '?'
 * 通过设置这个结构，便可得到一个完整的命令格式: <命令> [选项 <参数>] [选项 <参数>] ...
 *
 * 其他一些细节：
 * - 这些宏都是以最简约的形式定义的，以便在有大量数据的情况下降低阅读难度。在一些特殊情况下，可根据这些提及到的宏的定义展开理解再灵活使用。
 *
 * 总结：
 * SET_VAR(), SET_ENUM() 和 SET_CP_ENUM() 是用于单个变量的设置和补全，它们可以直接在命令执行函数和补全函数中使用，也可以在 sh_vset_param_t 结构体中对应的函数使用。
 * PSET_FN() 和 PSET_CP() 是用于多个变量的设置和补全，它们可以直接在命令执行函数和补全函数中被调用。
 * sh_vset_param_t 结构体是用于描述支待的待设置变量的具体类型，它可以直接在命令执行函数和补全函数中被调用。
 *
 */

#endif
