/*========================== begin_copyright_notice ============================
Copyright (C) 2021-2024 Intel Corporation

SPDX-License-Identifier: MIT
============================= end_copyright_notice ===========================*/

#include "gtpin_crt_utils.h"
#include <assert.h>

#include <stdlib.h>
#include <limits.h>
#include <ctype.h>
#include <cstring>
#include <cerrno>
#include <vector>

#if defined(TARGET_WINDOWS)
# include <cctype>
# include <Windows.h>
#else
# include <unistd.h>
# include <sys/stat.h>
# include <sys/types.h>
#endif

namespace gtpin
{

/* ============================================================================================= */
// String utilities
/* ============================================================================================= */
std::string ToLower(const std::string& str)
{
    std::string temp(str);
    std::transform(temp.begin(), temp.end(), temp.begin(), [](unsigned char c) -> unsigned char { return (unsigned char)std::tolower(c); });
    return temp;
}

std::string ToUpper(const std::string& str)
{
    std::string temp(str);
    std::transform(temp.begin(), temp.end(), temp.begin(), [](unsigned char c) -> unsigned char { return (unsigned char)std::toupper(c); });
    return temp;
}

void SplitString(const std::string& str, const char delim, std::vector<std::string>& tokens)
{
    std::stringstream stream(str);
    std::string token;

    while (!stream.eof())
    {
        std::getline(stream, token, delim);
        tokens.push_back(token);
    }
}

std::string TrimString(const std::string& str, char tr)
{
    size_t first = str.find_first_not_of(tr);
    if (std::string::npos == first)
    {
        return str;
    }
    size_t last = str.find_last_not_of(tr);
    return str.substr(first, (last - first + 1));
}

std::string RemoveTrailing(const std::string& str, char tr)
{
    size_t last = str.find_last_not_of(tr);
    if (std::string::npos == last)
    {
        return str;
    }
    return str.substr(0, last + 1);
}

std::string GlueString(const std::string& str, char sep)
{
    std::string out = str;
    std::transform(out.begin(), out.end(), out.begin(), [&](char c) -> char { return ((c == ' ') ? sep : c); });
    return out;
}

bool StrToInt(const std::string& str, uint32_t& res)
{
    if (std::strspn(str.c_str(), "0123456789") == str.size())
    {
        res = std::atoi(str.c_str());
        return true;
    }
    return false;
}

/* ============================================================================================= */
// File system utilities
/* ============================================================================================= */
char PathSeparator()
{
#if defined(TARGET_WINDOWS)
    return '\\';
#else
    return '/';
#endif
}

#if defined(TARGET_WINDOWS)
/// Translate string to wstring
static std::wstring ToWstring(const std::string& src)
{
    int wlen = MultiByteToWideChar(CP_ACP, 0, src.c_str(), (uint32_t)src.length() + 1, 0, 0); // compute the buffer length
    std::vector<wchar_t> buffer(wlen);
    int numOfChars = MultiByteToWideChar(CP_ACP, 0, src.c_str(), (uint32_t)src.length() + 1, &buffer[0], wlen); // writing to the buffer
    if (numOfChars == 0)
    {
        return L"";
    }
    return std::wstring(&buffer[0], numOfChars); // constructing wstring
}
#endif

bool MakeDirectory(const std::string& dir)
{
#if defined(TARGET_WINDOWS)
    /*
     * To remove 248 character limitation, use Unicode version of string and prepend "\\?\" to the path
     * https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
     */
    std::wstring stemp = ToWstring(dir);
    std::wstring prep = L"\\\\?\\";
    std::wstring wdirname = prep + stemp;
    LPCWSTR dirname = wdirname.c_str();
    if (!CreateDirectoryW(dirname, NULL) && ERROR_ALREADY_EXISTS != GetLastError())
#else
    int status = mkdir(dir.c_str(), 0777);
    if ((status != 0) && EEXIST != errno)
#endif
    {
        return false;
    }
    return true;
}

std::string MakeSubDirectory(const std::string& dir, const std::string& subDir)
{
#if defined(TARGET_LINUX)
    std::string fullDirPath = JoinPath(dir, subDir.substr(0, NAME_MAX));  //on linux, file/dir name can not exceed NAME_MAX
#else
    std::string fullDirPath = JoinPath(dir, subDir);
#endif
    if (!MakeDirectory(fullDirPath))
    {
        return "";
    }
    return fullDirPath;
}

std::string GetFullPath(const std::string& path)
{
#if defined (TARGET_WINDOWS)
    char fullPath[_MAX_PATH];
    if (_fullpath(fullPath, path.c_str(), _MAX_PATH) == nullptr)
    {
        return "";
    }
#else // linux
    char fullPath[PATH_MAX];
    if (realpath(path.c_str(), fullPath) == nullptr)
    {
        return "";
    }
#endif
    return fullPath;
}

bool IsRelativePath(const std::string& path)
{
#if defined(TARGET_WINDOWS)
    return (((path.size() <= 2) || (!isalpha(path[0])) || (path[1] != ':') || (path[2] != PathSeparator())));
#else
    return (path.empty() || (path[0] != PathSeparator()));
#endif
}

std::string GetBaseDirectory(const std::string& path)
{
    size_t pos = path.find_last_of(PathSeparator());
    return path.substr(0, pos);
}

std::string GetFilenameFromPath(const std::string& path)
{
    const std::size_t lastSepPos = path.rfind(std::string(1, PathSeparator()));
    if (lastSepPos == std::string::npos)
    {
        return path;    // no separators in the path; the filename is simply the path
    }
    else
    {
        return path.substr(lastSepPos + 1);
    }
}

bool CmpPathnames(const std::string& path0, const std::string& path1)
{
#if defined(TARGET_WINDOWS)
    return (ToUpper(path0) == ToUpper(path1)); // case-insensitve match
#else
    return (path0 == path1); // case-sensitive match
#endif
}

std::string NormalizeFilename(const std::string& filename, const std::string& extension)
{

    // See  https://msdn.microsoft.com/en-us/library/aa365247#naming_conventions.
    const std::string forbiddenChars = "\\/:*?\"<>| ";
    auto transformForbiddenChar = [&](char c) -> char
    {
        return (std::string::npos != forbiddenChars.find(c)) ? '_' : c;
    };

#if defined(TARGET_LINUX)
    assert(extension.length() < NAME_MAX);
    std::string normalizedFilename = filename.substr(0, (NAME_MAX - extension.length())) + extension;  //on linux, file/dir name can not exceed NAME_MAX
#else
    std::string normalizedFilename = filename + extension;
#endif
    std::transform(normalizedFilename.begin(), normalizedFilename.end(), normalizedFilename.begin(), transformForbiddenChar);
    return normalizedFilename;
}

}


