#ifndef PARAHELPER_H
#define PARAHELPER_H

/**
  * \file ParaHelper.h
  *
  * \brief 定义一些帮助设置参数用的结构体，如\ref sParaRange
  *
**/

#include "_nlohmann_json_wrapper.h"
#include "StringHelper.h"
#include "ParaHelper_core.h"

/**  @addtogroup ksjson
  *  @{
  *     @addtogroup ParaHelper
  *     @brief 定义sParaRange结构体，用于设置参数的缺省值、最小值、最大值，已经步进值，小数位数等属性。
  *     @{ */

#ifndef ConstExpr
    /// 需要在一个类/结构体内部定义静态常量表达示时，
    /// 将"static constexpr auto"简写为ConstExpr
    #define ConstExpr static constexpr const auto
#endif

/// 给变量添加注释的宏。
/// var是变量
/// str1、str2是变量含义和详细描述。
/// 只支持const wchar_t*类型的字符串，方便在UI显示。
#define ParaComment(var, str1, str2) \
        ConstExpr var##_title = str1; \
        ConstExpr var##_descr = str2;

static inline std::string combine_strs(const wchar_t* s1, const wchar_t* s2)
{
    if (wcslen(s2)) 
        return utf16_8(std::wstring(s1) + L": " + s2);
	else
		return utf16_8(s1);
}

// 变量转json并附加注释，适用于可以直接转json的变量
#define TO_JSON_COMMENT(j, name, var) \
        j[#name##"?"] = combine_strs(var##_title, var##_descr); \
        j[#name] = var;

// wstring型变量转json并附加注释
#define TO_JSON_COMMENT_WSTR(j, name, var) \
        j[#name##"?"] = combine_strs(var##_title, var##_descr); \
        j[#name] = utf16_8(var);

// wstring型vector变量转json并附加注释
#define TO_JSON_COMMENT_WSTRVECTOR(j, name, var) {\
        j[#name##"?"] = combine_strs(var##_title, var##_descr); \
		jsonobj jarr=jsonobj::array(); \
		for(auto& s : var) jarr.push_back(utf16_8(s)); \
		j[#name] = jarr; \
	}

// 类转json并附加注释，适合自定义了to_json()函数的类或结构体
#define TO_JSON_COMMENT_CLASS(j, name, var) \
        j[#name##"?"] = combine_strs(var##_title, var##_descr); \
		to_json(j[#name], var);

/// sParaRange -> json
template <typename T>
void to_json(jsonobj& j, const sParaRange<T> & p) {
    j["def"] = p.Def;
    j["min"] = p.Min;
    j["max"] = p.Max;
    j["step"] = p.Step;
    if(typeid (T) == typeid (float) || typeid (T) == typeid (double)){
        //浮点型才保存Decimal
        j["decimal"] = p.Decimal;
    }
}


/// json -> sParaRange
/// 手写的json文件有两种格式：
/// 一种每一项用key:value单独设置，必须存在def,min,max;
/// 一种写成数组，必须存在3项。
/// 程序写的json见to_json()
template <typename T>
void from_json(const jsonobj& j, sParaRange<T>& p) {
    if(!j.is_array()){ //key:value方式单独设置各项
        //注意：def, min, max 必须存在，要不然这会抛异常的！
        j["def" ].get_to<T>(p.Def);
        j["min" ].get_to<T>(p.Min);
        j["max" ].get_to<T>(p.Max);

        // 判断step是否存在再读
        auto it = j.find("step");
        if(it!=j.end()) it->get_to<T>(p.Step);

        // 判断decimal是否存在再读
        it = j.find("decimal");
        if(it!=j.end())  it->get_to<int>(p.Decimal);
    }else{
        //至少要有三项。TODO: 如何报错？
        int size = int(j.size());
        if(size<3) return;
        j[0].get_to<T>(p.Def);
        j[1].get_to<T>(p.Min);
        j[2].get_to<T>(p.Max);

        //有4项则读step
        if(size >= 4) j[3].get_to<T>(p.Step);

        //有5项则读decimal
        if(size >= 5) p.Decimal = int(j[4].get<T>());
    }
}


/**    @} ParaHelper */
/**  @} ksjson */

#endif // PARAHELPER_H
