#ifndef CJSONFILE_H
#define CJSONFILE_H

#include "_nlohmann_json_wrapper.h"
#include <fstream>
#include <iomanip>
#include <filesystem>
#include <atlstr.h>
namespace fs = std::filesystem;
/**  @addtogroup ksjson
  *  @{
  *     @addtogroup files
  *     @brief json/bson file classes.
  *     @{ */

  /**
   * @brief The CJsonFile class
   */
class CJsonFile // header only class
{
public:

	CJsonFile()
		:needSave(0)
	{

	}

	/// \brief 构造函数
	/// \param filepath 指定文件绝对路径，只保存了文件名，并没有打开文件。
	/// 如果未设置，后续必须通过setFilePath()指定文件的绝对路径
	template<typename T>
	CJsonFile(T filepath)
	{
		setFilePath(filepath);
	}

	/// 设置文件路径
	/// \param path 文件的绝对路径

	void setFilePath(const char* path) { _filePath = path; }
	void setFilePath(const wchar_t* path) { _filePath = path; }
	void setFilePath(const fs::path& path) { _filePath = path; }
#ifdef UNICODE	
	void setFilePath(const CString& path) { _filePath = (const wchar_t*)path; }
#endif	

	// 判断是否设置了路径，没有判断路径是否有效
	bool hasFilePath() const { return !_filePath.empty(); }

	bool exists() const { return !_filePath.empty() && fs::exists(_filePath); }

	virtual ~CJsonFile() = default;

protected:
	/// 参数文件完整路径。可以在构造函数里指定，或用setPath()指定
	fs::path _filePath;

public:
	/// 文件内容与内存变量之间中转的json格式cache。
	jsonobj cache;

	/// 标记参数是否需要保存。
	int needSave = 0;

	// 错误字符串
	std::string errStr;

	/// 清空json缓存
	void clearCache() { cache = jsonobj(); }

	/// 获取文件路径
	const fs::path& filePath() const { return _filePath; }

	void reset() {
		cache.clear();
		_filePath.clear();
	}

	enum {
		jFileNotExist=0,
		jFileOk =1,
		jFileFailOpenRead=-1,
		jFileFailOpenWrite=-2,
		jFileParseError=-3,
		jFileNull=-4,
		jFileOutputError=-5,
		jfileJsonToStructError=-6,
		jfileJsonFromStructError = -7,
	};

	// ----保存和加载的函数相关 -----
	/// 从文件加载json/bson到cache，加载后关闭文件。
	/// 需要在构造函数中或用setFilePath()设置文件的完整路径
	/// \return 1: 文件打开ok，解析json正确；0：文件打开失败；-1：文件打开成功，解析json出错
	virtual int load()
	{
		// 调用处必须保证已经设置了路径
		_ASSERT(!_filePath.empty());

		if (!fs::exists(_filePath)) {
			errStr = fmt::format("json文件不存在：{}", _filePath.string());
			return jFileNotExist;
		}

		std::ifstream in(_filePath);
		if (in.is_open()) {
			try {
				in >> cache;
				needSave = 0;
				in.close();
				return jFileOk;
			}
			catch (jsonobj::exception& e) {
				//throw parse_error.101 in case of an unexpected token
				//throw parse_error.102 if to_unicode fails or surrogate error
				//throw parse_error.103 if to_unicode fails
				errStr = fmt::format("解析json出错：{}\n{}", _filePath.string(), e.what());
				in.close();
				return jFileParseError;
			}
		}
		else {
			errStr = fmt::format("json文件只读打开失败：{}", _filePath.string());
			return jFileFailOpenRead;
		}
	}

	/// 保存cache到指定的文件，覆盖文件内容，写完后关闭文件，但内存中保存json格式的cache。
	/// 需要在构造函数中或用setFilePath()设置文件的完整路径。
	/// \return 1: 文件保存成功；0: 文件创建失败；-1: json中有字符串不是utf-8
	virtual int save()
	{
		// 调用处必须保证已经设置了路径
		_ASSERT(!_filePath.empty());

		std::ofstream o(_filePath, std::ofstream::trunc);
		if (o.is_open()) {
			try {
				o << std::setw(4) << cache << std::endl;
				o.flush();
				needSave = 0;
				return jFileOk;
			}
			catch (jsonobj::exception& e) {
				// throw type_error.316 if a string stored inside the JSON value is not UTF-8 encoded
				errStr = fmt::format("转换json文本出错：{}\n{}", _filePath.string(), e.what());
				return jFileOutputError;
			}
		}
		else {
			errStr = fmt::format("json文件写入打开失败：{}", _filePath.string());
			return jFileFailOpenWrite;
		}

	}

	template <typename T>
	int save(const T& src, bool clearBeforeSave=false) {
		if (clearBeforeSave) clearCache();
		try {
			to_json(cache, src);
		}
		catch (jsonobj::exception& e) {
			errStr = fmt::format("json<= 结构体出错：{}\n{}", _filePath.string(), e.what());
			return jfileJsonFromStructError;
		}
		
		return save();
	}

	template <typename T>
	int load(T& dst) {

		errStr.clear();
		int ret = jFileOk;
		do {
			ret = load();
			if (ret != 1) break;
			if (cache.is_null()) {
				errStr = fmt::format("读到的json是空的：{}", _filePath.string());
				ret = jFileNull;
				break;
			}
			try {
				from_json(cache, dst);
			}
			catch (jsonobj::exception& e) {
				errStr = fmt::format("json => 结构体出错：{}\n{}", _filePath.string(), e.what());
				ret = jfileJsonToStructError;
				break;
			}
			return jFileOk;
		} while (0);
		alog->error(errStr);
		return ret;
	}

	/// 将cache中名为name的部分转到结构体T的实例dst
	template <typename T>
	int copyToStruct(const char* name, T& dst)
	{
		if (cache.contains(name)) {
			try {
				cache[name].get_to<T>(dst);
			}
			catch (jsonobj::exception& e) {
				errStr = fmt::format("json {} => 结构体出错：{}\n{}", name, _filePath.string(), e.what());
				return jfileJsonToStructError;
			}
			return 1;
		}
		return 0;
	}

	/// 将结构体T的实例src内容转成json保存到cache中名为name的字段
	template <typename T>
	void saveToCache(const char* name, const T& src)
	{
		try {
			cache[name] = src;
		}
		catch (jsonobj::exception& e) {
			errStr = fmt::format("json {} <= 结构体出错：{}\n{}", name, _filePath.string(), e.what());
			return jfileJsonFromStructError;
		}
		needSave++;
	}
};

class CBsonFile : public CJsonFile
{
public:
	/// 重载load函数，将json改成bson。
	virtual int load()
	{
		return 1;
	}

	/// 重载load函数，将json改成bson。
	virtual int save()
	{
		return 1;
	}
};

/**    @} files */
/**  @} ksjson */
#endif // CJSONFILE_H
