#pragma once
//stl
#include <iostream>
#include <sstream>
#include <vector>
#include <filesystem>
//boost
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
//robotics
#include <robotics/linq.hpp>
//local
#include "../models/web_after_config_info.h"
#include "../models/web_general_config_info.h"
#include "../models/web_user_config_info.h"

class xml_context {
public:
	template<typename _Type>
	static v3::linq_ptr<_Type>& from(bool reset = true) {
		if constexpr (std::is_same<web_after_config_info, _Type>::value) {
			if (reset) {
				instance()->linq_web_after_config_info_->reset();
			}
			return *instance()->linq_web_after_config_info_;
		}
		else if constexpr (std::is_same<web_general_config_info, _Type>::value) {
			if (reset) {
				instance()->linq_web_general_config_info_->reset();
			}
			return *instance()->linq_web_general_config_info_;
		}
		else if constexpr (std::is_same<web_user_config_info, _Type>::value) {
			if (reset) {
				instance()->linq_web_user_config_info_->reset();
			}
			return *instance()->linq_web_user_config_info_;
		}
	}
private:
	static xml_context* instance() {
		static xml_context g_xml_context;
			return &g_xml_context;
	}
	xml_context() {
		create_file();
		load();
		linq_web_after_config_info_.reset(new v3::linq_ptr<web_after_config_info>(web_after_config_info_list_));
		linq_web_after_config_info_->changed_bind(&xml_context::on_table_save<web_after_config_info>, this);
		linq_web_general_config_info_.reset(new v3::linq_ptr<web_general_config_info>(web_general_config_info_list_));
		linq_web_general_config_info_->changed_bind(&xml_context::on_table_save<web_general_config_info>, this);
		linq_web_user_config_info_.reset(new v3::linq_ptr<web_user_config_info>(web_user_config_info_list_));
		linq_web_user_config_info_->changed_bind(&xml_context::on_table_save<web_user_config_info>, this);
	}
	void load() {
		boost::property_tree::ptree root;
		try {
			read_xml("./config/config.xml", root);
			web_after_config_info_list_.clear();
			for (auto& it : root.get_child("root.after_config")) {
				if (it.first != "item") continue;
				std::shared_ptr<web_after_config_info> item(new web_after_config_info);
				item->id = it.second.get<int>("<xmlattr>.id", 0);
				item->modify_time = it.second.get<std::string>("<xmlattr>.modify_time", v3::datetime::current_datetime().to_string("yyyy-MM-dd HH:mm:ss"));
				item->value = it.second.get<std::string>("<xmlattr>.value", "");
				item->enable = it.second.get<bool>("<xmlattr>.enable", false);
				item->remarks = it.second.get<std::string>("<xmlattr>.remarks", "");
				web_after_config_info_list_.push_back(item);
			}
			web_general_config_info_list_.clear();
			for (auto& it : root.get_child("root.general_config")) {
				if (it.first != "item") continue;
				std::shared_ptr<web_general_config_info> item(new web_general_config_info);
				item->id = it.second.get<int>("<xmlattr>.id", 0);
				item->modify_time = it.second.get<std::string>("<xmlattr>.modify_time", v3::datetime::current_datetime().to_string("yyyy-MM-dd HH:mm:ss"));
				item->key = it.second.get<int>("<xmlattr>.key", 0);
				item->value = it.second.get<std::string>("<xmlattr>.value", "");
				item->enable = it.second.get<bool>("<xmlattr>.enable", false);
				item->remarks = it.second.get<std::string>("<xmlattr>.remarks", "");
				web_general_config_info_list_.push_back(item);
			}
			web_user_config_info_list_.clear();
			for (auto& it : root.get_child("root.user_config")) {
				if (it.first != "item") continue;
				std::shared_ptr<web_user_config_info> item(new web_user_config_info);
				item->id = it.second.get<int>("<xmlattr>.id", 0);
				item->modify_time = it.second.get<std::string>("<xmlattr>.modify_time", v3::datetime::current_datetime().to_string("yyyy-MM-dd HH:mm:ss"));
				item->name = it.second.get<std::string>("<xmlattr>.name", "");
				item->code = it.second.get<std::string>("<xmlattr>.code", "");
				item->password = it.second.get<std::string>("<xmlattr>.password", "");
				item->enable = it.second.get<bool>("<xmlattr>.enable", false);
				item->remarks = it.second.get<std::string>("<xmlattr>.remarks", "");
				web_user_config_info_list_.push_back(item);
			}
		}
		catch (std::exception const& ec) {
			LOG_ERROR << "读取xml配置异常" << ec;
		}
	}
	void save() {
		std::lock_guard<std::mutex> lock(mutex_);
		boost::property_tree::ptree root;
		try {
			auto& tmp_after_config = root.add("root.after_config", "");
			for (auto& it : web_after_config_info_list_) {
				auto& item = tmp_after_config.add("item", "");
				item.put("<xmlattr>.id", it->id);
				item.put("<xmlattr>.modify_time", it->modify_time.to_string("yyyy-MM-dd HH:mm:ss"));
				item.put("<xmlattr>.value", it->value);
				item.put("<xmlattr>.enable", it->enable);
				item.put("<xmlattr>.remarks", it->remarks);
			}
			auto& tmp_general_config = root.add("root.general_config", "");
			for (auto& it : web_general_config_info_list_) {
				auto& item = tmp_general_config.add("item", "");
				item.put("<xmlattr>.id", it->id);
				item.put("<xmlattr>.modify_time", it->modify_time.to_string("yyyy-MM-dd HH:mm:ss"));
				item.put("<xmlattr>.key", it->key);
				item.put("<xmlattr>.value", it->value);
				item.put("<xmlattr>.enable", it->enable);
				item.put("<xmlattr>.remarks", it->remarks);
			}
			auto& tmp_user_config = root.add("root.user_config", "");
			for (auto& it : web_user_config_info_list_) {
				auto& item = tmp_user_config.add("item", "");
				item.put("<xmlattr>.id", it->id);
				item.put("<xmlattr>.modify_time", it->modify_time.to_string("yyyy-MM-dd HH:mm:ss"));
				item.put("<xmlattr>.name", it->name);
				item.put("<xmlattr>.code", it->code);
				item.put("<xmlattr>.password", it->password);
				item.put("<xmlattr>.enable", it->enable);
				item.put("<xmlattr>.remarks", it->remarks);
			}
			boost::property_tree::xml_writer_settings<std::string> settings(' ', 4);
			write_xml("./config/config.xml", root, std::locale(), settings);
		}
		catch (std::exception const& ec) {
			LOG_ERROR << "保存xml配置异常" << ec;
		}
	}
private:
	template<typename _Table>
	void on_table_save(_Table const& value, _Table const& value_tmp, v3::linq_change_type type, bool end) {
		if (end) {
			save();
		}
	}
	bool create_file(std::filesystem::path const&filename = "./config/config.xml") {
	    if (!std::filesystem::exists(filename.parent_path())) {
	        if (!std::filesystem::create_directories(filename.parent_path()))
	            return false;
	    }
	    if (!std::filesystem::exists(filename)) {
	        std::ofstream file(filename);
	        if (!file.is_open())
	            return false;
	        file.close();
			web_user_config_info_list_.push_back(std::make_shared<web_user_config_info>(web_user_config_info{
				1,
				robotics::v3::datetime::current_datetime(),
				"管理员",
				"admin",
				"123456",
				true,
				"管理员"}));
			save();
	    }
	    return true;
	}
private:
	std::mutex mutex_;
	std::vector<std::shared_ptr<web_after_config_info>> web_after_config_info_list_;
	std::shared_ptr<v3::linq_ptr<web_after_config_info>> linq_web_after_config_info_;
	std::vector<std::shared_ptr<web_general_config_info>> web_general_config_info_list_;
	std::shared_ptr<v3::linq_ptr<web_general_config_info>> linq_web_general_config_info_;
	std::vector<std::shared_ptr<web_user_config_info>> web_user_config_info_list_;
	std::shared_ptr<v3::linq_ptr<web_user_config_info>> linq_web_user_config_info_;
};