/* -------------------------------------------------------------------------
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * This file is part of the MindStudio project.
 *
 * MindStudio is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *    http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * -------------------------------------------------------------------------
 */

#include "csrc/common/utils.h"

#include <linux/limits.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <unistd.h>

#include <ctime>
#include <unordered_map>

#include "csrc/common/plog_manager.h"
#include "securec.h"

namespace Mspti
{
namespace Common
{
const std::unordered_map<std::string, std::string> INVALID_CHAR = {
    {"\n", "\\n"},         {"\f", "\\f"},  {"\r", "\\r"}, {"\b", "\\b"},  {"\t", "\\t"}, {"\v", "\\v"},
    {"\u007F", "\\u007F"}, {"\"", "\\\""}, {"'", "\'"},   {"\\", "\\\\"}, {"%", "\\%"},  {">", "\\>"},
    {"<", "\\<"},          {"|", "\\|"},   {"&", "\\&"},  {"$", "\\$"},   {";", "\\;"},  {"`", "\\`"}};

uint64_t Utils::GetClockMonotonicRawNs()
{
    struct timespec ts;
    if (memset_s(&ts, sizeof(timespec), 0, sizeof(timespec)) != EOK)
    {
        return 0;
    }
    clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
    return static_cast<uint64_t>(ts.tv_sec) * SECTONSEC + static_cast<uint64_t>(ts.tv_nsec);
}

uint64_t Utils::GetClockRealTimeNs()
{
    struct timespec ts;
    if (memset_s(&ts, sizeof(timespec), 0, sizeof(timespec)) != EOK)
    {
        return 0;
    }
    clock_gettime(CLOCK_REALTIME, &ts);
    return static_cast<uint64_t>(ts.tv_sec) * SECTONSEC + static_cast<uint64_t>(ts.tv_nsec);
}

uint64_t Utils::GetHostSysCnt()
{
    uint64_t cycles;
#if defined(__aarch64__)
    asm volatile("mrs %0, cntvct_el0" : "=r"(cycles));
#elif defined(__x86_64__)
    constexpr uint32_t uint32Bits = 32U;
    uint32_t hi = 0;
    uint32_t lo = 0;
    __asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi));
    cycles = (static_cast<uint64_t>(lo)) | ((static_cast<uint64_t>(hi)) << uint32Bits);
#elif defined(__arm__)
    const uint32_t uint32Bits = 32U;
    uint32_t hi = 0;
    uint32_t lo = 0;
    asm volatile("mrrc p15, 1, %0, %1, c14" : "=r"(lo), "=r"(hi));
    cycles = (static_cast<uint64_t>(lo)) | ((static_cast<uint64_t>(hi)) << uint32Bits);
#else
    cycles = 0;
#endif
    return cycles;
}

uint32_t Utils::GetPid()
{
    static thread_local uint32_t pid = static_cast<uint32_t>(getpid());
    return pid;
}

uint32_t Utils::GetTid()
{
    static thread_local uint32_t tid = static_cast<uint32_t>(syscall(SYS_gettid));
    return tid;
}

std::string Utils::RealPath(const std::string& path)
{
    if (path.empty() || path.size() > PATH_MAX)
    {
        return "";
    }
    char realPath[PATH_MAX] = {0};
    if (realpath(path.c_str(), realPath) == nullptr)
    {
        return "";
    }
    return std::string(realPath);
}

std::string Utils::RelativeToAbsPath(const std::string& path)
{
    if (path.empty() || path.size() > PATH_MAX)
    {
        return "";
    }
    if (path[0] != '/')
    {
        char pwd_path[PATH_MAX] = {0};
        if (getcwd(pwd_path, PATH_MAX) != nullptr)
        {
            return std::string(pwd_path) + "/" + path;
        }
        return "";
    }
    return std::string(path);
}

bool Utils::FileExist(const std::string& path)
{
    if (path.empty() || path.size() > PATH_MAX)
    {
        return false;
    }
    return (access(path.c_str(), F_OK) == 0) ? true : false;
}

bool Utils::FileReadable(const std::string& path)
{
    if (path.empty() || path.size() > PATH_MAX)
    {
        return false;
    }
    return (access(path.c_str(), R_OK) == 0) ? true : false;
}

bool Utils::CheckCharValid(const std::string& str)
{
    for (auto& item : INVALID_CHAR)
    {
        if (str.find(item.first) != std::string::npos)
        {
            MSPTI_LOGE("The path contains invalid character: %s.", item.second.c_str());
            return false;
        }
    }
    return true;
}

std::string Utils::GetEnv(const std::string& name)
{
    const char* value = std::getenv(name.c_str());
    return value ? std::string(value) : std::string();
}

bool Utils::StartsWith(const std::string& str, const std::string& prefix)
{
    if (prefix.size() > str.size())
    {
        return false;
    }
    return str.compare(0, prefix.size(), prefix) == 0;
}
}  // namespace Common
}  // namespace Mspti