#ifndef BitSet_H
#define BitSet_H
#include <bitset>
#ifdef __linux__
#include <cstring>
#endif
#include <iostream>
#include <memory_resource>
#include <string>
#include <type_traits>

namespace yC {
    template <typename T = std::pmr::string>
    typename std::enable_if<std::is_same<T, std::pmr::string>::value, T>::type
    create_string(size_t size, std::pmr::memory_resource* resource = nullptr) {
            std::pmr::memory_resource* r = resource ? resource : std::pmr::get_default_resource();
            std::pmr::string ret(r);
            ret.resize(size);
            return ret;  // 返回 std::pmr::string
    }

    template <typename T = std::string>
    typename std::enable_if<std::is_same<T, std::string>::value, T>::type
    create_string(size_t size, std::pmr::memory_resource* resource = nullptr) {
        std::string ret;
        ret.resize(size);
        return ret;  // 返回 std::string
    }

    template <typename T>
    struct cacl_type {
        using type = T;
        size_t size(T& a) {
            return a.size();
        }
        static void* data(T& a) {
            return (void*)a.data();
        }
        static size_t size(const T& a) {
            return a.size();
        }
        static void* data(const T& a) {
            return (void*)a.data();
        }
    };
    template <>
    struct cacl_type<const char*> {
        using type = std::string;
        static size_t size(const char* a) {return strlen(a);}
        static void* data(const char* a) {return (void*)a;}
    };

    template <size_t N>
    struct cacl_type<char[N]> {
        using type = std::string;
        static size_t size(const char (&a)[N]) { return N - 1; }
        static void* data(const char (&a)[N]) { return (void*)a; }
    };

    template <size_t N>
    struct cacl_type<const char[N]> {
        using type = std::string;
        static size_t size(const char (&a)[N]) { return N - 1; }
        static void* data(const char (&a)[N]) { return (void*)a; }
    };
}



#define Base     template <typename allocator_type = std::allocator<char>, \
typename STR_Type = std::basic_string<char, std::char_traits<char>, allocator_type>, \
typename ret_type = typename cacl_type<STR_Type>::type \
>
// #define Base     template <\
// typename STR_Type, \
// typename ret_type = typename cacl_type<STR_Type>::type \
// >


namespace yC {
    // 检查当前机器是否是大端字节序
    extern const bool is_big_endian;
    void big_endian_2_platform(void* dest, const char* d, size_t size);
    void little_endian_2_platform(void* dest, const char* d, size_t size);



    void big_endian_2_platform(const std::string& big_endian, char* dest);
    void little_endian_2_platform(const std::string& little_endian, char* dest);


    std::string big_endian_2_platform(const std::string& big_endian);
    std::string little_endian_2_platform(const std::string& little_endian);
    std::string platform_2_big_endian(void* memory, int size);
    std::string platform_2_little_endian(void* memory, int size);




    inline size_t get_xor_hex_size(size_t n1, size_t n2){return std::max(n1, n2);}
    void xor_hex_Ex(void* dest, const char* s1, size_t n1, const char* s2, size_t n2, bool big_dian, bool is_upper);
    Base
    ret_type xorHex(const STR_Type& s1, const STR_Type& s2, std::pmr::memory_resource* resource = nullptr) {
        auto n1 = cacl_type<STR_Type>::size(s1);
        auto n2 = cacl_type<STR_Type>::size(s2);
        auto size = get_xor_hex_size(n1, n2);
        auto ret = create_string<ret_type>(size, resource);
        xor_hex_Ex((void*)ret.data(), (const char*)cacl_type<STR_Type>::data(s1), n1, (const char*)cacl_type<STR_Type>::data(s2), n2, true, true);
        return ret;
    }


    inline size_t get_xor_bin_size(size_t n1, size_t n2){return std::max(n1, n2);}
    void xor_bin_Ex(void* dest, const char* s1, size_t n1, const char* s2, size_t n2, bool big_dian);
    Base
    ret_type xorBin(const STR_Type& s1, const STR_Type& s2, std::pmr::memory_resource* resource = nullptr) {
        auto n1 = cacl_type<STR_Type>::size(s1);
        auto n2 = cacl_type<STR_Type>::size(s2);
        auto size = get_xor_bin_size(n1, n2);
        auto ret = create_string<ret_type>(size, resource);
        xor_bin_Ex((void*)ret.data(), (const char*)cacl_type<STR_Type>::data(s1), n1, (const char*)cacl_type<STR_Type>::data(s2), n2, true);
        return ret;
    }




    inline size_t get_bin2hex_size(size_t num) {return (num + 3) / 4;}
    void bin2hex_Ex(void* dest, const char* data, size_t size, bool big_dian, bool isupper);
    Base
    ret_type bin2hex(const STR_Type& mem, std::pmr::memory_resource* resource = nullptr) {
        auto size = cacl_type<STR_Type>::size(mem);
        auto ret = create_string<ret_type>(get_bin2hex_size(size), resource);
        bin2hex_Ex((void*)ret.data(), (const char*)cacl_type<STR_Type>::data(mem), size, true, true);
        return ret;
    }



    inline size_t get_hex2bin_size(size_t num) {return num * 4;}
    void hex2bin_Ex(void* dest, const char* data, size_t size, bool big_dian);
    Base
    ret_type hex2bin(const STR_Type& mem, std::pmr::memory_resource* resource = nullptr) {
        auto size = cacl_type<STR_Type>::size(mem);
        auto ret = create_string<ret_type>(get_hex2bin_size(size), resource);
        hex2bin_Ex((void*)ret.data(), (const char*)cacl_type<STR_Type>::data(mem), size, true);
        return ret;
    }

    inline size_t get_bin2mem_size(size_t num) {return num/8;};
    void bin2mem(void* dest, const char* data, size_t size);
    Base
    STR_Type bin2mem(const STR_Type& mem, std::pmr::memory_resource* resource = nullptr) {
        auto size = cacl_type<STR_Type>::size(mem);
        auto ret = create_string<STR_Type>(get_bin2mem_size(size), resource);
        bin2mem((void*)ret.data(), (const char*)cacl_type<STR_Type>::data(mem), size);
        return ret;
    }

    // inline ret_str bin2mem(param_str mem, std::pmr::memory_resource* resource = nullptr) {
    //         auto size = mem.size();
    //         auto ret = ret_str();
    //         ret.resize(get_bin2mem_size(size));
    //         bin2mem((void*)ret.data(), (const char*)mem.data(), size);
    //         return ret;
    // }


    inline size_t get_hex2mem_size(size_t hex_hum) {return hex_hum/2;};
    void hex2mem_Ex(void* dest, const char* data, size_t size);
    Base
    STR_Type hex2mem(const STR_Type& mem, std::pmr::memory_resource* resource = nullptr) {
        auto size = cacl_type<STR_Type>::size(mem);
        auto ret = create_string<STR_Type>(get_hex2mem_size(size), resource);
        hex2mem_Ex((void*)ret.data(), (const char*)cacl_type<STR_Type>::data(mem), size);
        return ret;
    }

    // inline ret_str hex2mem(param_str hex, std::pmr::memory_resource* resource = nullptr) {
    //     auto size = hex.size();
    //     auto ret = ret_str();
    //     ret.resize(get_hex2mem_size(size));
    //     hex2mem_Ex((void*)ret.data(), (const char*)hex.data(), size);
    //     return ret;
    // }


    inline size_t get_mem2hex_size(size_t num, size_t middle_len) { return num * 2 + (num - 1) * middle_len;}
    void mem2hex_Ex2(void* dest, void* mem, size_t num, bool uppercase, const char* middle, size_t middle_len = std::numeric_limits<size_t>::max());
    Base
    STR_Type mem2hex(const STR_Type& mem, bool uppercase = true, const std::string& middle = "", std::pmr::memory_resource* resource = nullptr) {
        auto size = cacl_type<STR_Type>::size(mem);
        auto ret = create_string<STR_Type>(get_mem2hex_size(size, middle.size()), resource);
        mem2hex_Ex2(ret.data(), cacl_type<STR_Type>::data(mem), size, uppercase, middle.data(), middle.size());
        return ret;
    }

    // inline ret_str mem2hex(param_str& mem, bool uppercase = true, const std::string& middle = "", std::pmr::memory_resource* resource = nullptr) {
    //     auto size = get_mem2hex_size(mem.size(), middle.size());
    //     auto ret = ret_str();
    //     mem2hex_Ex2(ret.data(), (void*)mem.data(), size, uppercase, middle.data(), middle.size());
    //     return ret;
    // }


    template <int start, int len>
    void get_bin(std::string& msg, int& n) {
        n = std::bitset<len>(msg.substr(start, len)).to_ullong();
    }
    template <int start, int len, typename Enum>
    void get_bin(std::string& msg, Enum& n) {
        n = static_cast<Enum>(std::bitset<len>(msg.substr(start, len)).to_ullong());
    }
    template <int start, int len, typename Enum>
    Enum get_bin(std::string& msg) {
        return static_cast<Enum>(std::bitset<len>(msg.substr(start, len)).to_ullong());
    }
    template <int start, int len>
    int get_bin(const std::string& msg) {
        return std::bitset<len>(msg.substr(start, len)).to_ullong();
    }
    template <int start, int len>
    void set_bin(std::string& msg, unsigned long long n) {
        msg.replace(start, len, std::bitset<len>(n).to_string());
    }
    template <int start, int len>
    void set_bin(std::string& msg, const std::string& value) {
        if (value.size() != len) {
            std::cout << "len == " << len << "  bits.size() == " << value.size() << std::endl;
            throw std::out_of_range("Start position and length exceed bit string size");
        }
        msg.replace(start, len, value);
    }
    template <int len>
    std::string to_bin(unsigned long long n) {
        return std::bitset<len>(n).to_string();
    }
    template <int num>
    std::string to_bin(double n) {
        return std::bitset<num>(static_cast<unsigned long long>(n)).to_string();
    }
    template <typename T>
    T bin2(const std::string& binstr) {
        return std::stoull(binstr, nullptr, 2);
    }
    template <typename T>
    unsigned long long hex2(const std::string& hexstr) {
        return std::stoull(hexstr, nullptr, 16);
    }
    template <>
    inline int bin2<int>(const std::string& binstr) {
        int bit_limit = sizeof(int) * 8;
        if (binstr.size() > bit_limit) {
            std::cout << "Bitset.h inline int bin2<int> error! Binary string exceeds int bit limit" << std::endl;
        }
        return std::stoi(binstr, nullptr, 2);
    }
    // 设置dest的从off位起n位，写入value的低n位
    template <typename T = std::uint64_t>
    void set_bits(void* dest, size_t off, size_t n, T value) {
        auto d = static_cast<uint8_t*>(dest);
        size_t bit_pos = off;
        size_t val_pos = 0;
        while (n > 0) {
            size_t byte_idx = bit_pos / 8;
            size_t bit_in_byte = bit_pos % 8;
            size_t bits_in_this_byte = std::min(n, 8 - bit_in_byte);
            // 为当前字节构建掩码
            uint8_t mask = ((1u << bits_in_this_byte) - 1) << bit_in_byte;
            // 取value对应的低bits_in_this_byte位
            uint8_t v = (value >> val_pos) & ((1u << bits_in_this_byte) - 1);
            // 清除目标字节对应位置后设置
            d[byte_idx] = (d[byte_idx] & ~mask) | ((v << bit_in_byte) & mask);
            bit_pos += bits_in_this_byte;
            val_pos += bits_in_this_byte;
            n -= bits_in_this_byte;
        }
    }



    // 大批量位处理性能不好  但是通常也没有这样的场景
    void bit_move(void* d, size_t doff, const void* s, size_t soff, size_t n);
    void bit_xor(void* d, size_t doff, const void* s, size_t soff, size_t n);
    void bit_or(void* d, size_t doff, const void* s, size_t soff, size_t n);
    void bit_and(void* d, size_t doff, const void* s, size_t soff, size_t n);
    template <typename T = std::uint64_t>
    T get_bits(void* src, size_t off, size_t n) {
        auto s = static_cast<const uint8_t*>(src);
        size_t bit_pos = off;
        size_t val_pos = 0;
        T result = 0;
        while (n > 0) {
            size_t byte_idx = bit_pos / 8;
            size_t bit_in_byte = bit_pos % 8;
            size_t bits_in_this_byte = std::min(n, 8 - bit_in_byte);
            uint8_t mask = ((1u << bits_in_this_byte) - 1) << bit_in_byte;
            uint8_t bits = (s[byte_idx] & mask) >> bit_in_byte;
            result |= (T(bits) << val_pos);
            bit_pos += bits_in_this_byte;
            val_pos += bits_in_this_byte;
            n -= bits_in_this_byte;
        }
        return result;
    }
}

#undef Base
#endif
