#ifndef JSONREADWITHVERIFY_H
#define JSONREADWITHVERIFY_H

#include "_nlohmann_json_wrapper.h"
#include "ParaHelper.h"
#include "alog.h"

/**  @addtogroup ksjson
  *  @{
  *    @addtogroup readWithVerify
  *    @brief 从json转换到数值前判断key是否存在，确认数值是否在一定[min, max]范围内，或在一个数组列表范围之内。
  *    @{
*/

/// \brief 尝试从obj读取键值为key的数据，没找到不赋值返回false，找到就给指针赋值并返回true。
/// \details 通常用于已经初始化为默认值的变量或结构体，找到就读，没找到就保持默认值。
/// 注意保存的格式要跟写入的格式一致，别写个string当int读，肯定会出问题的。这里就没法检验格式是否正确了。
///
/// \param obj nlohmann::json对象
/// \param key 键值，类型KeyT支持类型const char *, std::string
/// \param dst 目标变量，类型ValueT可以是所有支持的数据类型，包括nlohmann::json标准的数据类型和定义了to_json和from_json的类型。
/// \return 没找到返回0，找到返回1。
template <typename KeyT, typename = typename JsonKey<KeyT>::Type, typename ValueT>
int tryRead(const jsonobj& obj, KeyT key, ValueT& dst)
{
	auto it = obj.find(key);
	if (it == obj.end() || it->is_null()) return 0; //没找到
	it->template get_to<ValueT>(dst);
	return 1;
}

/// 尝试从obj读取键值为key的数据，没找到设置为缺省值并返回false，找到就读取数值给指针赋值并返回true。
/// 类型ValueT可以是所有支持的数据类型，包括nlohmann::json标准的数据类型和定义了to_json和from_json的类型。
/// \param obj nlohmann::json对象
/// \param dst 目标变量。
/// \param key 键值，类型KeyT支持类型const char *, std::string
/// \param def 找不到键值使用的缺省值。def的数据类型ValueT2必须与dst的数据类型ValueT可以直接隐式转换或复制构造的
/// \return 没找到返回false，找到返回true。
template <typename KeyT, typename = typename JsonKey<KeyT>::Type, typename ValueT, typename ValueT2>
int readWithDefault(const jsonobj& obj, KeyT key, ValueT& dst, ValueT2 def)
{
	auto it = obj.find(key);
	if (it == obj.end() || it->is_null()) {//没找到
		dst = def;
		return 0;
	}
	it->template get_to<ValueT>(dst);
	return 1;
}

/// \brief 在\ref readWithDefault 基础上增加了校验读到的值是否在[min, max]范围之内。
/// \details 类型ValueT通常是数值类型。其它支持的数据类型如果它支持用大于号/小于号比较的类型也行，估计很少会用到。
/// \param obj nlohmann::json对象
/// \param key 键值，类型KeyT支持类型const char *, std::string
/// \param dst 目标变量。
/// \param def 找不到键值使用的缺省值。def的数据类型必须与dst的数据类型可以直接转换的
/// \param min 取值范围的下限，min << value << max
/// \param max 取值范围的上限，min << value << max
/// \return 返回读取状态。
/// 没找到返回0，找到但数值超界返回-1，找到并在有效范围内返回1。
template <typename KeyT, typename = typename JsonKey<KeyT>::Type, typename ValueT, typename ValueT2, typename ValueT3>
int readInRange(const jsonobj& obj, KeyT key, ValueT& dst, const ValueT2& def, const ValueT3& min, const ValueT3& max)
{
	auto it = obj.find(key);
	if (it == obj.end() || it->is_null()) {//没找到
		dst = ValueT(def);
		return 0;
	}
	ValueT tmp = it->template get<ValueT>();
	if (tmp < ValueT(min)) {
		dst = ValueT(min);
		return -1;
	}
	else if (tmp > ValueT(max)) {//超界
		dst = ValueT(max);
		return -1;
	}
	// 返回读取值
	dst = tmp;
	return 1;
}

/// \brief 重载\ref readInRange 用sParaRange<ValueT>做取值范围的校验。
/// \details 用sParaRange的成员变量Def, Min, Max替代\ref readInRange的def, min, max参数
template <typename KeyT, typename = typename JsonKey<KeyT>::Type, typename ValueT>
inline int readInRange(const jsonobj& obj, KeyT key, ValueT& dst, const sParaRange<ValueT>& range)
{
	return readInRange(obj, key, dst, range.Def, range.Min, range.Max);
}

/// \brief json --> ValueT，并校验数值是否在数组list[num]范围类
/// \details
/// - 没找到：返回值为0，dst设置为def<br>
/// - 找到但数值布置在列表范围内：返回值-1，dst设置为def<br>
/// - 找到并在列表范围内：返回1，dst设置为解析值。<br>
template <typename KeyT, typename = typename JsonKey<KeyT>::Type, typename ValueT, typename NumT, typename = ArraySizeT<NumT>::Type>
int readInList(const jsonobj& obj, KeyT key, ValueT& dst, const ValueT& def, const ValueT* list, NumT num)
{
	auto it = obj.find(key);
	if (it == obj.end() || it->is_null()) {//没找到
		dst = def;
		return 0;
	}
	ValueT tmp = it->template get<ValueT>();
	for (int i = 0; i < num; i++) { //不在列表中
		if (tmp == list[i]) {
			dst = tmp;
			return 1;
		}
	}
	dst = def;

	return -1;
}

/// \brief json --> ValueT，并校验数值是否在QVector<ValueT>列表范围类
/// \details
/// - 没找到：返回值为0，dst设置为def<br>
/// - 找到但数值布置在列表范围内：返回值-1，dst设置为def<br>
/// - 找到并在列表范围内：返回1，dst设置为解析值。<br>
template <typename KeyT, typename = typename JsonKey<KeyT>::Type, typename ValueT>
int readInList(const jsonobj& obj, KeyT key, ValueT& dst, const ValueT& def, std::vector<ValueT> list)
{
	auto it = obj.find(key);
	if (it == obj.end() || it->is_null()) {//没找到
		dst = def;
		return 0;
	}
	ValueT tmp = it->template get<ValueT>();
	if (list.contains(tmp)) {
		dst = tmp;
		return 1;
	}
	dst = def;

	return -1;
}

/// \brief json --> ValueT，并校验数值是否在QList<ValueT>列表范围类
/// \details
/// - 没找到：返回值为0，dst设置为def<br>
/// - 找到但数值布置在列表范围内：返回值-1，dst设置为def<br>
/// - 找到并在列表范围内：返回1，dst设置为解析值。<br>
template <typename KeyT, typename = typename JsonKey<KeyT>::Type, typename ValueT>
int readInList(const jsonobj& obj, KeyT key, ValueT& dst, const ValueT& def, std::list<ValueT> list)
{
	auto it = obj.find(key);
	if (it == obj.end() || it->is_null()) {//没找到
		dst = def;
		return 0;
	}
	ValueT tmp = it->template get<ValueT>();
	if (list.contains(tmp)) {
		dst = tmp;
		return 1;
	}
	dst = def;

	return -1;
}

/// \brief 从json读入string, 与strlist里的字符串比较，确定枚举值。
/// \details
/// \param strlist 字符串列表
/// \param num 字符串个数
/// \param vallist 枚举值列表。<br>
/// vallist可以为null，代表枚举值如果是从0开始顺序递增的，dst设置为strlist数组的下标。<br>
/// \return <br>
/// - 没找到：返回值为0，dst设置为def<br>
/// - 找到但字符串不在列表范围内：返回值-1，dst设置为def<br>
/// - 找到并在列表范围内：返回1。如果vallist为null，dst设置strlist数组的下表；如果vallist非空，dst设置为对于的数组。<br>
template <typename KeyT, typename = typename JsonKey<KeyT>::Type, typename ValueT>
int readEnum(const jsonobj& obj, KeyT key, ValueT& dst, int def, const char* const*  strlist, int num, const int* vallist = nullptr)
{
	auto it = obj.find(key);
	if (it == obj.end() || it->is_null()) {//没找到
		alog->warn("readEnum 没找到{}，返回缺省值", key);
		dst = ValueT(def);
		return 0;
	}
	if (it->is_string()) {
		std::string str = it->template get<std::string>();
		for (int i = 0; i < num; i++) {
			if (str.compare(strlist[i]) == 0) {
				if (vallist) dst = ValueT(vallist[i]);
				else dst = ValueT(i);
				return 1;
			}
		}
		alog->warn("readEnum {}没有匹配到字符串{}，返回缺省值", key);
		dst = ValueT(def);
	}
	else if (it->is_number_integer()) {
		int val = it->template get<int>();
		if (vallist) {
			for (int i = 0; i < num; i++) {
				if (val == vallist[i]) {
					dst = ValueT(val);
					return 1;
				}
			}
			alog->warn("readEnum {}数值不在列表中，返回缺省值", key);
			dst = ValueT(def);
		}
		else {
			if (val >= 0 && val < num) {
				dst = ValueT(val);
				return 1;
			}
			else {
				alog->warn("readEnum {}数值超界，返回缺省值", key);
				dst = ValueT(def);
			}
		}
	}
	else {
		alog->error("{}的json类型错误，应该是字符串或整形，未修改结果", key);
	}
	
	return -1;
}

// 与上一版本的区别是没有缺省值
template <typename KeyT, typename = typename JsonKey<KeyT>::Type, typename ValueT>
int readEnum(const jsonobj& obj, KeyT key, ValueT& dst, const char* const * strlist, int num, const int* vallist = nullptr)
{
	auto it = obj.find(key);
	if (it == obj.end() || it->is_null()) {//没找到
		alog->warn("readEnum 没找到{}，返回缺省值", key);
		return 0;
	}
	if (it->is_string()) {
		std::string str = it->template get<std::string>();
		for (int i = 0; i < num; i++) {
			if (str.compare(strlist[i]) == 0) {
				if (vallist) dst = ValueT(vallist[i]);
				else dst = ValueT(i);
				return 1;
			}
		}
		alog->warn("readEnum {}没有匹配到字符串{}，未赋值", key);
	}
	else if (it->is_number_integer()) {
		int val = it->template get<int>();
		if (vallist) {
			for (int i = 0; i < num; i++) {
				if (val == vallist[i]) {
					dst = ValueT(val);
					return 1;
				}
			}
			alog->warn("readEnum {}数值不在列表中，未赋值", key);
		}
		else {
			if (val >= 0 && val < num) {
				dst = ValueT(val);
				return 1;
			}
			else {
				alog->warn("readEnum {}数值超界，未赋值", key);
			}
		}
	}
	else {
		alog->error("{}的json类型错误，应该是字符串或整形，未修改结果", key);
	}

	return -1;
}

/// \brief 从json读入string, 与strlist里的字符串比较，确定枚举值。
/// \details
/// \param strlist 字符串列表
/// \param vallist 枚举值列表。<br>
/// \return <br>
/// - 没找到：返回值为0，dst设置为def<br>
/// - 找到但字符串不在列表范围内：返回值-1，dst设置为def<br>
/// - 找到并在列表范围内：返回1。如果vallist为null，dst设置strlist数组的下表；如果vallist非空，dst设置为对于的数组。<br>
template <typename KeyT, typename = typename JsonKey<KeyT>::Type, typename ValueT, typename ValutT2>
int readEnum(const jsonobj& obj, KeyT key, ValueT& dst, ValutT2 def, const char** strlist, const std::vector<ValueT>& vallist)
{
	auto it = obj.find(key);
	if (it == obj.end() || it->is_null()) {//没找到
		dst = ValueT(def);
		return 0;
	}
	std::string str = it->template get<std::string>();
	for (int i = 0; i < vallist.size(); i++) {
		if (str.compare(strlist[i]) == 0) {
			dst = vallist[i];
			return 1;
		}
	}
	dst = ValueT(def);
	return -1;
}

/// 将C++枚举数组转换为字符串数组写入json
template <typename KeyT, typename = typename JsonKey<KeyT>::Type, typename ValueT>
void writeEnumArray(jsonobj& obj, KeyT key, const char* const* strlist, int strnum, const std::vector<ValueT> &valuelist)
{
	auto arr=jsonobj::array();
	for(auto &val : valuelist) {
		if (val < 0 || val >= strnum) {
			alog->error("writeEnum {}数值超界", key);
			return;
		}
		arr.push_back(strlist[val]);
	}
	obj[key] = arr;
}

template <typename KeyT, typename = typename JsonKey<KeyT>::Type, typename ValueT>
int readEnumArray(const jsonobj& obj, KeyT key, std::vector<ValueT>& dst, const char* const* strlist, int num)
{
	auto it = obj.find(key);
	if (it == obj.end()) {//没找到
		alog->warn("readEnumArray 没找到{}", key);
		return 0;
	}
	if (!it->is_array()) {
		alog->error("readEnumArray {} 不是数组", key);
		return 0;
	}

	if (it->size() == 0) {
		alog->error("readEnumArray {} 是空数组", key);
		return 0;
	}

	dst.clear();
	for (auto& x : *it) {
		if (x.is_string()) {
			std::string str=x.get<std::string >();
			int i = 0;
			for (; i < num; i++) {
				if (str.compare(strlist[i]) == 0) {
					dst.push_back(ValueT(i));
					break;
				}
			}
			if(i==num )alog->warn("readEnumArray {}没有匹配到字符串{}，未赋值", key, str);
		}
		else {
			alog->error("{}的json类型错误，应该是字符串，未修改结果", key);
		}
	}

	

	return int(dst.size());
}


template <typename KeyT, typename = typename JsonKey<KeyT>::Type>
int readStr(const jsonobj& obj, const KeyT& key, char* dst, int dstsize)
{
	auto it = obj.find(key);
	if (it == obj.end() || it->is_null()) {//没找到，或空
		memset(dst, 0, dstsize);
		return 0;
	}
	if (!it->is_string()) { //不是字符串
		return -1;
	}
	std::string str = it->template get<std::string>();
	auto len = str.length();
	if (len >= dstsize) {
		return -2;
	}
	else if (len == 0) {
		*dst = 0;
		return 1;
	}
	else {
		memcpy(dst, str.c_str(), len);
		*(dst + len) = 0;
		return 1;
	}
}

/// <summary>
///     读入char型数值，如果长度刚好填满，不用0结束
/// </summary>
/// <typeparam name="KeyT"></typeparam>
/// <param name="obj"></param>
/// <param name="key"></param>
/// <param name="dst"></param>
/// <param name="dstsize"></param>
/// <returns></returns>
template <typename KeyT, typename = typename JsonKey<KeyT>::Type>
int readCharArray(const jsonobj& obj, const KeyT& key, char* dst, int dstsize, char fill)
{
	auto it = obj.find(key);
	if (it == obj.end() || it->is_null()) {//没找到，或空
		memset(dst, 0, dstsize);
		return 0;
	}
	if (!it->is_string()) { //不是字符串
		return -1;
	}
	std::string str = it->template get<std::string>();
	auto len = str.length();
	if (len > dstsize) {
		return -2;
	}
	else {
		memcpy(dst, str.c_str(), len);
		if (len < dstsize) {
			memset(dst + len, fill, dstsize - len);
		}
		return 1;
	}
}



/**   @} readWithCheck*/
/** @} json*/

#endif // JSONREADWITHVERIFY_H
