#ifndef Json_H
#define Json_H
#include <atomic>
#include <functional>
#include <iostream>
#include <list>
#include <optional>
#include <string>
#include <vector>
#define Json_GET(type, name)     [[nodiscard]] type get_##name(const std::string &key) const { \
    const Json* that = get(key); \
    if (!that) { \
        std::cout << "get" #type " error json not found!  key:" << key << "  json: " <<  to_json_string() << std::endl; \
        return false; \
    } \
    return that-> name##_val(); \
}


template <typename T_Number>
T_Number to_number(const std::string& val) {
    if constexpr (std::is_integral<T_Number>::value) {
        if constexpr (std::is_unsigned<T_Number>::value) {
            return static_cast<T_Number>(std::stoull(val));
        } else if constexpr (std::is_signed<T_Number>::value) {
            return static_cast<T_Number>(std::stoll(val));
        }
    } else if constexpr (std::is_floating_point<T_Number>::value) {
        return static_cast<T_Number>(std::stold(val));
    }
    return T_Number();
}


class Json {
public:
    static std::function<void(const char*, const std::string&, const char*, int)> handle_error;
    explicit operator std::string() const;
    friend std::ostream& operator<<(std::ostream& os, const Json& obj);
    [[nodiscard]] const Json* get(const std::string& key) const;
    [[nodiscard]] std::string get_string(const std::string& key) const;
    [[nodiscard]] const std::string& get_inside_string(const std::string& key) const;
    //键值对
    Json(std::string key, const char* charArrayVal);
    Json(std::string key, std::nullptr_t);
    Json(std::string key, std::string stringVal);
    Json(std::string key, bool boolVal);
    Json(std::string key, const Json& body);

    [[nodiscard]] bool bool_val() const;
    Json_GET(bool, bool)
    template <typename T_Number>
    T_Number get_number(const std::string& key) const {
        const Json* that = get(key);
        if (!that) {
            std::cout << "json not found!  key:" << key << "  json: " << to_json_string() << std::endl;
            return false;
        }
        return that->number_val<T_Number>();
    }
    template <typename T_Number>
    T_Number number_val() const {
        return to_number<T_Number>(val);
    }
    // template <typename T_Number>
    // Json(std::string key, const T_Number& intVal) : key(std::move(key)),
    //                                             val(std::to_string(intVal)), valueType(Number) {
    // }

    template <typename T_Number>
    Json(std::string key, const T_Number& intVal, typename std::enable_if<
        !std::is_same<T_Number, bool>::value &&
        !std::is_same<T_Number, std::atomic<bool>>::value
    >::type* = nullptr)
        : key(std::move(key)), val(std::to_string(intVal)), valueType(Number) {
    }


    void append_list(const std::vector<Json>& arr);
    void append(const Json& json);
    void prepend(const Json& json);
    static Json array(std::string key, const std::vector<Json>& arr);
    static Json object(std::string key, const std::vector<Json>& children);
    static Json array(const std::vector<Json>& arr = {});
    static Json object(const std::vector<Json>& children = {});
    //不加explicit 让他允许隐式类型转换
    Json(std::nullptr_t); //只能填写 nullptr
    Json(const char* charArrayVal);
    Json(std::string stringVal);
    Json(const int& intVal);
    Json(const double& doubleVal);
    Json(const bool& boolVal);

    enum JsonType {
        String,
        Object,
        Array,
        Null,
        Number,
        Bool
    } valueType = Null;

    std::string key, val;
    std::vector<Json> children;
    [[nodiscard]] std::string to_json_string(const std::string& object_split = "\n", const std::string& array_split = "\n", const std::string& indentStr = " ", const int& indent = 0) const;
    Json();
private:
    static std::string get_indent(const int& indent, const std::string& indentStr);
    void append_value_string(std::string& ret, const int& indent, bool isEnd, const std::string& object_split,
                             const std::string& array_split, const std::string& indentStr, JsonType fatherType) const;
};

std::optional<Json> parse_json(const std::string& s);
std::optional<Json> parse_json_file(std::string& fileName);
std::optional<Json> parse_json_file(std::string&& fileName);



#undef Json_GET

// #define J(FIELD) Json(#FIELD, this->FIELD)
// #define JEnum(FIELD) Json(#FIELD, to_string(this->FIELD))

#define Ret_J(FIELD) ret.append(Json(#FIELD, this->FIELD));
#define Ret_JEnum(FIELD) ret.append(Json(#FIELD, to_string(this->FIELD)));

#define ORet_J(FIELD) if(FIELD.has_value()) ret.append(Json(#FIELD, FIELD.value())); \
else ret.append(Json(#FIELD, nullptr));

#define ORet_JEnum(FIELD) if(FIELD.has_value()) ret.append(Json(#FIELD, to_string(FIELD.value()))); \
else ret.append(Json(#FIELD, nullptr));

#define ORet_J2(FIELD) if(FIELD.has_value()) ret.append(Json(#FIELD, FIELD.value()));


#define ORet_JEnum2(FIELD) if(FIELD.has_value()) ret.append(Json(#FIELD, to_string(FIELD.value())));


#define J_Get_Number(FIELD) FIELD = that_json->get_number<decltype(FIELD)>(#FIELD);

#define JA_Get_Number(FIELD) FIELD.store(that_json->get_number<decltype(FIELD)::value_type>(#FIELD));
#endif
