#include "torch_npu/csrc/toolkit/profiler/common/utils.h"
#include <vector>
#include <sstream>
#include <algorithm>
#include <sys/types.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netpacket/packet.h>

namespace {
template<typename T>
std::string IntToHexStr(T number)
{
    std::stringstream strStream;
    strStream << std::hex << number;
    return strStream.str();
}

std::string Join(const std::vector<std::string> &elems, const std::string &sep)
{
    std::stringstream result;
    for (size_t i = 0; i < elems.size(); ++i) {
        if (i == 0) {
            result << elems[i];
        } else {
            result << sep << elems[i];
        }
    }
    return result.str();
}

uint64_t CalcHashId(const std::string &data)
{
    static const uint32_t UINT32_BITS = 32;
    uint32_t prime[2] = {29, 131};
    uint32_t hash[2] = {0};
    for (char d : data) {
        hash[0] = hash[0] * prime[0] + static_cast<uint32_t>(d);
        hash[1] = hash[1] * prime[1] + static_cast<uint32_t>(d);
    }
    return (static_cast<uint64_t>(hash[0]) << UINT32_BITS) | hash[1];
}
}

namespace torch_npu {
namespace toolkit {
namespace profiler {

uint64_t Utils::GetHostUid()
{
    static const uint8_t SECOND_LEAST_BIT = 1 << 1;
    struct ifaddrs *ifaddr = nullptr;
    if (getifaddrs(&ifaddr) == -1) {
        if (ifaddr != nullptr) {
            freeifaddrs(ifaddr);
        }
        return 0;
    }
    std::vector<std::string> universalMacAddrs;
    std::vector<std::string> localMacAddrs;
    for (struct ifaddrs *ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
        if (ifa->ifa_addr == nullptr || ifa->ifa_addr->sa_family != AF_PACKET) {
            continue;
        }
        if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) {
            continue;
        }
        struct sockaddr_ll *lladdr = reinterpret_cast<struct sockaddr_ll*>(ifa->ifa_addr);
        uint32_t len = static_cast<uint32_t>(lladdr->sll_halen);
        if (len > 0) {
            std::string addr;
            for (uint32_t i = 0; i < len; ++i) {
                std::string hexAddr = IntToHexStr(static_cast<uint16_t>(lladdr->sll_addr[i]));
                addr += (hexAddr.length() > 1) ? hexAddr : ("0" + hexAddr);
            }
            if ((lladdr->sll_addr[0] & SECOND_LEAST_BIT) == 0) {
                universalMacAddrs.emplace_back(addr);
            } else {
                localMacAddrs.emplace_back(addr);
            }
        }
    }
    if (ifaddr != nullptr) {
        freeifaddrs(ifaddr);
    }
    if (universalMacAddrs.empty() && localMacAddrs.empty()) {
        return 0;
    }
    auto &macAddrs = universalMacAddrs.empty() ? localMacAddrs : universalMacAddrs;
    std::sort(macAddrs.begin(), macAddrs.end());
    return CalcHashId(Join(macAddrs, "-"));
}

int Utils::safe_strcpy_s(char* dest, const char* src, size_t destSize)
{
    if (dest == nullptr || src == nullptr || destSize == 0) {
        return -1;
    }
    size_t i = 0;
    for (; i < destSize - 1 && src[i] != '\0'; ++i) {
        dest[i] = src[i];
    }
    dest[i] = '\0';
    if (src[i] != '\0') {
        dest[0] = '\0';
        return -1;
    }
    return 0;
}

}
}
}