
#ifndef _JFXMAP_SERIALIZE_H_
#define _JFXMAP_SERIALIZE_H_

#include <fstream>
#include <map>
#include <sstream>
#include <vector>

#include "ajson.hpp"

#define jfxmap_serialize_override_in(...) \
    AJSON_IN(__VA_ARGS__);                \
    binarySerialize_in(__VA_ARGS__);

#define jfxmap_serialize_override_out(...) \
    AJSON_OUT(__VA_ARGS__);                \
    binarySerialize_out(__VA_ARGS__);

#define jfxmap_serialize(...)        \
    AJSON_IN(__VA_ARGS__);           \
    binarySerialize_in(__VA_ARGS__); \
    AJSON_OUT(__VA_ARGS__);          \
    binarySerialize_out(__VA_ARGS__);

namespace jf {
inline bool IsLittleEndian() {
    static int i = 1;
    return (*(char *)&i);
}

template <typename changeType>
inline const char *Endian(changeType &in) {
    char *p = reinterpret_cast<char *>(&in);
    size_t sz = sizeof(changeType);
    for (size_t i = 0; i < sz / 2; ++i) {
        std::swap(p[i], p[sz - i - 1]);
    }
    return p;
}

template <typename changeType>
inline const char *SerializeToLittleEndian(changeType &in) {
    // 这里固定切出的地图为小端
    if (IsLittleEndian()) {
        char *p = reinterpret_cast<char *>(&in);
        return p;
    } else {
        return Endian(in);
    }
    // jf_log_error("Serialize::SerializeToLittleEndian");
    return nullptr;
}

template <typename changeType>
inline const char *DeserializeEndian(changeType &in) {
    if (IsLittleEndian()) {
        char *p = reinterpret_cast<char *>(&in);
        return p;
    } else {
        return Endian(in);
    }
}

class OutArchive {
   public:
    std::ostringstream o;
    std::string str() { return o.str(); }
};

class InArchive {
   public:
    InArchive(const std::string &in) : i(in) {}
    std::istringstream i;
};

inline OutArchive &operator<<(OutArchive &inout, int in) {
    inout.o.write(SerializeToLittleEndian(in), sizeof(in));
    return inout;
}

inline OutArchive &operator<<(OutArchive &inout, double in) {
    inout.o.write(SerializeToLittleEndian(in), sizeof(in));
    return inout;
}
inline OutArchive &operator<<(OutArchive &inout, float in) {
    inout.o.write(SerializeToLittleEndian(in), sizeof(in));
    return inout;
}
inline OutArchive &operator<<(OutArchive &inout, int64_t in) {
    inout.o.write(SerializeToLittleEndian(in), sizeof(in));
    return inout;
}
inline OutArchive &operator<<(OutArchive &inout, uint64_t in) {
    inout.o.write(SerializeToLittleEndian(in), sizeof(in));
    return inout;
}
inline OutArchive &operator<<(OutArchive &inout, uint32_t in) {
    inout.o.write(SerializeToLittleEndian(in), sizeof(in));
    return inout;
}
inline OutArchive &operator<<(OutArchive &inout, bool in) {
    inout.o.write(SerializeToLittleEndian(in), sizeof(in));
    return inout;
}
inline OutArchive &operator<<(OutArchive &inout, std::string in) {
    int sz = in.size();
    inout.o.write(SerializeToLittleEndian(sz), sizeof(sz));
    if (sz > 0) {
        inout.o.write((char *)in.c_str(), sz);
    }
    return inout;
}
template <typename elemType>
inline OutArchive &operator<<(OutArchive &inout, const std::vector<elemType> &in) {
    int sz = in.size();
    inout.o.write(SerializeToLittleEndian(sz), sizeof(sz));
    for (const elemType &e : in) {
        inout << e;
    }
    return inout;
}

template <typename KeyType, typename ValueType>
inline OutArchive &operator<<(OutArchive &inout, const std::map<KeyType, ValueType> &in) {
    int sz = in.size();
    inout.o.write(SerializeToLittleEndian(sz), sizeof(sz));
    for (auto &iter : in) {
        auto &k = iter.first;
        auto &v = iter.second;
        inout << k;
        inout << v;
    }
    return inout;
}

inline InArchive &operator>>(InArchive &inout, int &in) {
    inout.i.read((char *)&in, sizeof(in));
    DeserializeEndian(in);
    return inout;
}
inline InArchive &operator>>(InArchive &inout, double &in) {
    inout.i.read((char *)&in, sizeof(in));
    DeserializeEndian(in);
    return inout;
}
inline InArchive &operator>>(InArchive &inout, float &in) {
    inout.i.read((char *)&in, sizeof(in));
    DeserializeEndian(in);
    return inout;
}
inline InArchive &operator>>(InArchive &inout, uint64_t &in) {
    inout.i.read((char *)&in, sizeof(in));
    DeserializeEndian(in);
    return inout;
}
inline InArchive &operator>>(InArchive &inout, int64_t &in) {
    inout.i.read((char *)&in, sizeof(in));
    DeserializeEndian(in);
    return inout;
}
inline InArchive &operator>>(InArchive &inout, uint32_t &in) {
    inout.i.read((char *)&in, sizeof(in));
    DeserializeEndian(in);
    return inout;
}
inline InArchive &operator>>(InArchive &inout, bool &in) {
    inout.i.read((char *)&in, sizeof(in));
    DeserializeEndian(in);
    return inout;
}
inline InArchive &operator>>(InArchive &inout, std::string &in) {
    int sz;
    inout.i.read((char *)&sz, sizeof(sz));
    DeserializeEndian(sz);
    if (sz != 0) {
        char *temp = new char[sz];
        inout.i.read(temp, sz);
        in.assign(temp, sz);
        delete[] temp;
    }
    return inout;
}
template <typename elemType>
inline InArchive &operator>>(InArchive &inout, std::vector<elemType> &in) {
    int sz = 0;
    inout.i.read((char *)&sz, sizeof(sz));
    DeserializeEndian(sz);
    in.resize(sz);
    for (int i = 0; i < sz; ++i) {
        elemType e;
        inout >> e;
        in[i] = e;
    }
    return inout;
}

template <typename KeyType, typename ValueType>
inline InArchive &operator>>(InArchive &inout, std::map<KeyType, ValueType> &in) {
    int sz = 0;
    inout.i.read((char *)&sz, sizeof(sz));
    DeserializeEndian(sz);
    for (int i = 0; i < sz; ++i) {
        KeyType k;
        ValueType v;
        inout >> k;
        inout >> v;
        in.insert({k, v});
    }
    return inout;
}
// MapConfig::serialize_type_json1_binary2
template <typename serializeDataType>
inline std::string DoSerialize(const serializeDataType &sd, int serialize_type) {
    if (serialize_type == 1) {
        ajson::string_stream ss;
        ajson::save_to(ss, sd);
        return ss.str();
    } else if (serialize_type == 2 || serialize_type == 3) {
        OutArchive out;
        out << sd;
        return out.str();
    }
    return "";
}
template <typename deserializeDataType>
inline void DoSeserialize(const std::string &str, deserializeDataType &out, int serialize_type) {
    if (serialize_type == 1) {
        ajson::load_from_buff(out, str.c_str());
    } else if (serialize_type == 2 || serialize_type == 3) {
        InArchive in(str);
        in >> out;
    }
}

template <typename SerializeType>
inline std::string Struct2Json(const SerializeType &in) {
    ajson::string_stream ss;
    ajson::save_to(ss, in);
    return ss.str();
}
template <typename DeserializeType>
inline DeserializeType Json2Struct(const std::string &in) {
    DeserializeType d;
    ajson::load_from_buff(d, in.c_str());
    return d;
}

}  // namespace jf

template <typename dataType>
struct serializer_in {};
template <typename dataType>
struct serializer_out {};

#define binarySerialize_in(classType, ...)                                                                  \
    template <>                                                                                             \
    struct serializer_in<classType> : public classType {                                                    \
        inline jf::InArchive &read(jf::InArchive &inout) { return inArchiveRecursive(inout, __VA_ARGS__); } \
        template <typename headType, typename... args>                                                      \
        jf::InArchive &inArchiveRecursive(jf::InArchive &i, headType &head, args &... a) {                  \
            i >> head;                                                                                      \
            inArchiveRecursive(i, a...);                                                                    \
            return i;                                                                                       \
        }                                                                                                   \
        template <typename headType>                                                                        \
        jf::InArchive &inArchiveRecursive(jf::InArchive &i, headType &head) {                               \
            i >> head;                                                                                      \
            return i;                                                                                       \
        }                                                                                                   \
    };                                                                                                      \
    namespace jf {                                                                                          \
    inline jf::InArchive &operator>>(jf::InArchive &inout, classType &in) {                                 \
        serializer_in<classType> s;                                                                         \
        s.read(inout);                                                                                      \
        in = s;                                                                                             \
        return inout;                                                                                       \
    }                                                                                                       \
    }

#define binarySerialize_out(classType, ...)                                                                     \
    template <>                                                                                                 \
    struct serializer_out<classType> : public classType {                                                       \
        inline jf::OutArchive &write(jf::OutArchive &inout) { return OutArchiveRecursive(inout, __VA_ARGS__); } \
        template <typename headType, typename... args>                                                          \
        jf::OutArchive &OutArchiveRecursive(jf::OutArchive &o, const headType &head, const args &... a) {       \
            o << head;                                                                                          \
            OutArchiveRecursive(o, a...);                                                                       \
            return o;                                                                                           \
        }                                                                                                       \
        template <typename headType>                                                                            \
        jf::OutArchive &OutArchiveRecursive(jf::OutArchive &o, const headType &head) {                          \
            o << head;                                                                                          \
            return o;                                                                                           \
        }                                                                                                       \
    };                                                                                                          \
    namespace jf {                                                                                              \
    inline jf::OutArchive &operator<<(jf::OutArchive &inout, const classType &out) {                            \
        serializer_out<classType> s = reinterpret_cast<const serializer_out<classType> &>(out);                 \
        return s.write(inout);                                                                                  \
    }                                                                                                           \
    }

#endif
