/* KMBuilderUtils.h
 *
 * Copyright (C) 2023 OpenKylin, Inc
 */

#ifndef __BUILDER_UTILS_H__
#define __BUILDER_UTILS_H__

#include <list>
#include <vector>
#include <string>
#include <filesystem>
#include <libelf.h>
#include <gelf.h>

#include <sstream>
#include <iostream>
#include <algorithm>
#include <system_error>
#include <sstream>
#include <fstream>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <fcntl.h>
#include <thread>
#include <sys/wait.h>

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <regex>

namespace pt = boost::property_tree;
namespace fs = std::filesystem;
using namespace std;

class KMBuilderUtils
{
public:
    KMBuilderUtils();
    ~KMBuilderUtils();

    static list<string> split(string str, char c);
    static const string getArch();
    static bool ensureDir(fs::path dir);

    /**
     * @brief : 替换id字符串前缀-
     * @param origId : id字符串
     * @return : 
     */
    static string replaceIdPrefix(const string &origId);
    /**
     * @brief : 利用重载函数,执行参数拼接
     * @param prefix : 前缀字符串
     * @param content : 字符串正文
     * @param suffix : 后缀字符串
     * @return : 拼接之后的字符串
     */
    static string argSplice(string prefix, string content);
    static string argSplice(string prefix, string content, string suffix);
    static string argSplice(string prefix, int content);

    /**
     * @brief : 获取子路径
     * @param parentPath : 父路径
     * @param childName : 子路径
     * @return : 拼接之后的全路径
     */
    static std::string getChildPath(const std::string &parentPath, const std::string &childName);
    /**
     * @brief : 判断路径是否存在
     * @param path : 路径
     * @return : true-存在; false-不存在
     */
    static bool pathExists(const std::string &path);

    /**
     * @brief : 拼接命令行参数并提交执行
     * @param appDir : 路径
     * @param envVars : 环境变量
     * @param extraArgs : 运行参数
     * @param commandline : 命令
     * @return : true-成功; false-失败
     */
    static bool command(string appDir, vector<string> envVars, vector<string> extraArgs, string commandLine);

    /**
     * @brief : 文件修改时间置0
     * @param fd : 文件描述符
     * @param path : 文件路径
     * @return : true-成功; false-失败
     */
    static bool zeroMtime(int fd, const string &path);

    /**
     * @brief : 启动进程
     * @param dir : 工作目录
     * @param flags : 进程标志
     * @param args : 进程参数
     * @return : true-成功; false-失败
     */
    static bool spawn(const string &dir, int flags, const vector<string> &args);

    /**
     * @brief : 沙箱启动进程
     * @param dir : 工作目录
     * @param flags : 进程标志
     * @param args : 进程参数
     * @return : true-成功; false-失败
     */
    static bool hostSpawn(const string &dir, int flags, const vector<string> &args);

    /**
     * @brief : 清除文件的硬链接
     * @filePath : 文件路径
     * @return: true-成功；false-失败
     */
    static bool breakHardlink(const std::filesystem::path &filePath);

    /**
     * @brief : 创建目录软连接
     * @param : target 目标目录
     * @param : link 连接符号
     * @param : ec 错误信息
     * @return: true-创建成功；false-创建失败
     */
    static bool createDirectorySymlink(const std::string &target, const std::string &link, std::error_code &ec);

    /**
     * @brief : 是否在沙箱
     * @return: true-是；false-不是
     */
    static bool isInSanbox();

    /**
     * pathMatchPrefix:
     * @brief : 判断路径string是否匹配pattern前缀，返回匹配结束后的部分字符串
     * @param : [in] pattern, 前缀模式
     * @param : [in] string, 路径
     * @return: 返回匹配结束后的部分字符串，不匹配返回null；
     */
    static const char *pathMatchPrefix(const char *pattern, const char *string);

    /**
     * @brief : 是否有符号表
     * @param elf : elf文件
     * @return : true-是; false-否
     */
    static bool hasSymbol(Elf *elf);

    /**
     * @brief : 是否是ELF文件
     * @param path : 文件路径
     * @param isShared : 是否是共享库
     * @param isStripped : 是否是已剥离的共享库
     * @return : true-是; false-否
     */
    static bool isElfFile(const string &path, bool &isShared, bool &isStripped);

    /**
     * @brief : 获取目录下所有文件
     * @param : dir 查找的目标目录
     * @param : files 所有的文件
     */
    static void getFilesInDir(const std::string &dir, vector<string> &files);

    /**
     * @brief : 获取debuginfo文件引用
     * @param : filename 文件名
     * @param : fileRefs 文件引用
     * @return : true-成功; false-失败
     */
    static bool getDebuginfoFileRref(const string &filename, vector<string> &fileRefs);

    static const string getCommandResoult(const string &exe, const list<string> &args);

    /**
     * @brief : 路径模式匹配
     * @param : pattern 正则
     * @param : path 文件路径
     * @return : 匹配到的内容
     */
    static bool matchesPathPattern(string path, string pattern);

    /**
     * @brief : 路径前缀模式匹配
     * @param : pattern 正则
     * @param : string 文件路径
     * @return : 匹配到的内容
     */
    static string pathMatchPrefix(const std::string &pattern, const std::string &string);

    /**
     * @brief : 获取文件名
     */
    static string getBasename(string path);

    /**
     * @brief : 获取目录名
     */
    static string getDirname(const string &path);

    /**
     * @brief : 获取子目录名
     */
    static string getChildDir(const string &base, const string &child);

    /**
     * @brief : 将文件内容存储到string中
     */
    static std::string fileGetContents(const std::string &filePath);

    /**
     * @brief : 将配置文件内容写入文件（添加）
     */
    static bool fileSetContents(const string &filePath, const string &content);

    /**
     * @brief : 将配置文件内容写入文件（重写）
     */
    static bool fileReSetContents(const string &filePath, const string &content);

    /**
     * @brief :子进程输出读取线程  
     */
    static void readSubprocessOutput(int fd, std::ostream &os);

    /**
     * @brief :函数用于将 vector<string> 转换为 char* 数组   
     */
    static char **vecToArgv(const std::vector<std::string> &vec, int &argc);

    /**
     * @brief :spawnv函数,附带输出
     */
    static bool spawnv(const std::filesystem::path &dir, string &output, int flags, const std::vector<string> &args);

    /**
     * @brief :通过调用kaiming本身来获取结果
     */
    static string kaiming(int type, ...);

    /**
     * @brief : 转移T*指针的所有权
     */
    template<typename T>
    static T *stealPointer(T **p)
    {
        if (p == nullptr || *p == nullptr)
            return nullptr;

        T *r = *p;
        *p = nullptr;
        return r;
    }

    /**
     * @brief : 执行外部程序
     * @param : [in] exec, 外部程序名称，不包含路径
     * @param : [in] args, 执行外部程序需要传入的参数列表
     * @param : [out] result, 外部程序执行时的标准输出和标准错误信息
     * @return: true - 成功执行；false - 出现错误
     */
    static bool spawn(const std::string &exec, const std::vector<std::string> &args, std::string &result);

    /**
     * @brief : 执行外部程序
     * @param : [in] exec, 外部程序
     * @param : [in] args, 执行外部程序需要传入的参数列表
     * @return: true - 成功执行；false - 出现错误
    */
    static bool spawn(const std::string &exec, const std::vector<std::string> &args = {});

    /**
     * @brief : 执行外部程序
     * @param : [in] exec, 外部程序名称，不包含路径
     * @param : [in] workingDirectory, 执行外部程序工作目录
     * @param : [in] args, 执行外部程序需要传入的参数列表
     * @param : [out] result, 外部程序执行时的标准输出和标准错误信息
     * @return: true - 成功执行；false - 出现错误
     */
    static bool spawn(const std::string &exec, const std::string &workingDirectory, const std::vector<std::string> &args, std::string &result);

    /**
     * @brief : 根据分隔符拆分字符串
     * @param : [in] str，源字符串
     * @param : [in] split，分隔符
     * @return: std::vector<std::string>，拆分后的字符串数组
     */
    static std::vector<std::string> splitString(const std::string &str, const std::string &split);

    /**
     * @brief : 将字符串数组合并成一个字符串，各个元素之间用split分割
     * @param : [in] vector，待合并的字符串数组
     * @param : [in] split，分隔符
     * @return: 合并后的长字符串
     */
    static std::string join(const std::vector<std::string>& vector, const std::string &split);

    static bool argumentNeedsQuoting(const char *arg);

    /**
     * isEmptyDirectory:
     * @brief : 判断路径是否空目录，使用前先判断是否是目录
     * @param : [in] path，路径
     * @return: true-空目录或不存在; false-不是空目录或不是目录
     */
    static bool isEmptyDirectory(const std::string &path);

    /**
     * removeEmptyDirectory:
     * @brief : 递归删除空子目录
     * @param : [in] path，路径
     * @return: true-操作成功; false-操作失败
     */
    static bool removeEmptyDirectory(const std::string &path, std::error_code &ec);

    /**
     * @brief : 递归删除目录或文件
     * @param : [in] path, 待目录或文件
     * @param : [out] std::error_code，保存错误信息
     * @return : true-成功；false-失败
     */
    static bool removeAll(const std::string &path, std::error_code &ec);
    static bool removeAll(const std::string &path);
    static bool rmRfAll(const std::string &path);

    /**
     * @brief : 判断字符串前缀
     * @param : [in] src，原始字符串
     * @param : [in] prefix，字符串前缀
     * @return : true-字符串src是以prefix为前缀；false-字符串src不是以prefix为前缀
     */
    static bool startsWith(const std::string &src, const std::string &prefix);

    /**
     * @brief : 判断字符串后缀
     * @param : [in] src，原始字符串
     * @param : [in] sufix，字符串后缀
     * @return : true-字符串src是以sufix为后缀；false-字符串src不是以sufix为后缀
     */
    static bool endsWith(const std::string &src, const std::string &sufix);

    /**
     * @brief : 组装文件名
     * @param : [in] path, 前缀路径
     * @param : [in] name, 后续文件或目录名
     * @return: 组装后的文件路径
     */
    static std::string buildFilename(const std::string &path, const std::string &name);
    static std::string buildFilename(const std::string &path, const std::string &name1, const std::string &name2);

    /**
     * @brief : 将字符串str中的子串from替换为to
     * @param : [in | out] str, 原字符串
     * @param : [in] from, 需要替换的旧字符子串
     * @param : [in] to, 替换后的新子串
     * @return: 替换后的字符串引用，同str
     */
    static std::string &replace(std::string &str, const std::string &from, const std::string &to);

    /**
     * @brief : 递归创建目录
     * @param : [in] path, 目录
     * @param : [out] std::error_code，保存错误信息
     * @return : true-存在；false-不存在
     */
    static bool mkpath(const std::string &path, std::error_code &ec);
    static bool mkpath(const std::string &path);
    static bool mkpath(const std::string &path, unsigned int mode);

    /**
     * @brief : 在字符串数组v中查找是否包含字符串e
     * @param : [in] v, 字符串数组
     * @param : [in] e, 字符串
     * @return: true-包含；false-不包含
     */
    static bool contains(const std::vector<string> &v, const std::string &e);
    static bool contains(const std::string &src, const std::string &sub);

    /**
     * @brief : 下载url数据到文件dest
     * @param : [in] url，支持HTTP、HTTPS、FTP、FTPS协议
     * @param : [in] 目标文件名，默认则为url的basename
     * @return: true-下载成功；false-下载失败
     * @note  : 使用wget命令实现，优势：可利用其断点续传和代理
     */ 
    static bool downloadUrl(const std::string &url, const std::string &dest = "");

    /**
     * @brief :从data字符串数据中读取INI配置
     */
    static bool loadIniData(const std::string &data, pt::ptree &pt);
    /**
     * @brief :将INI配置转化成string字符串
     */
    static string ptreeToIniString(const pt::ptree &pt);
    /**
     * @brief :从file中读取INI配置
     */
    static bool loadIniFile(const std::string &filename, pt::ptree &pt);
    /**
     * @brief :查找pt中是否包含这个group
     */
    static bool hasGroup(const pt::ptree &pt, const std::string &group);
    /**
     * @brief :查找pt中是所有group名字，并且返回一个vector
     */
    static vector<string> getAllGroupNames(const boost::property_tree::ptree &pt);
    /**
     * @brief :从pt中擦除一个group
     */
    static void removeGroup(boost::property_tree::ptree &pt, const std::string &group);
    /**
     * @brief :查找group中是否包含这个Key
     */
    static bool groupHasKey(const pt::ptree &pt, const std::string &group, const std::string &key);
    /**
     * @brief :查找group中所有Key的名字
     */
    static std::vector<std::string> getKeys(const pt::ptree &groupPt, const std::string &groupName);
    /**
     * @brief :根据group和key，获取value，并根据指定符号分割为vector
     */
    static std::vector<std::string> getStringListFromPt(pt::ptree &groupPt, const string &groupName, const string &key);
    /**
     * @brief :将分号分割的字符串作为多个value写入指定groupName的key
     */
    static void writeStringListToPt(pt::ptree &groupPt, const string &groupName, const string &key, const vector<string> &stringList);
    /**
     * @brief :根据group和key，获取value
     */
    static std::string getValue(const pt::ptree &pt, const std::string &group, const std::string &key);
    /**
     * @brief :根据group和key，写入value
     */
    static void setValue(pt::ptree &pt, const std::string &group, const std::string &key, const std::string &value);
    /**
     * @brief :将pt持久化到指定文件
     */
    static bool saveToIniFile(const pt::ptree &pt, const std::string &filename);
    /**
     * @brief : 组合路径并调用migrateLocaleDir执行数据迁移
     * @return: true-成功; false-失败
     */
    static bool migrateLocaleDirs(string rootDir);
    /**
     * @brief : 迁移sourceDir下数据到其他组合之后的目录下
     * @return: true-成功; false-失败
     */
    static bool migrateLocaleDir(string sourceDir, string separateDir, string subdir);

    /**
     * @brief :获取HASH MAP中的keys
     */
    static vector<string> getKeysAsVector(const std::unordered_map<string, bool> &map);

    /**
     * @brief :获取HASH MAP中的key是否存在
     */
    static bool ifContains(const std::unordered_map<string, bool> &map, string key);

    /**
     * @brief : 列举出路径下所有文件(不包含目录)
     * @param path : 
     * @return : 
     */
    static vector<string> enumerateDirectoryPath(const string &path, bool ignoreSymlink = false);

    /**
     * @brief :组合两个vector为一个
     */
    static vector<string> concatVectors(const vector<string> &vec1, const vector<string> &vec2);

    /**
     * @brief :获取符号链接（symlink）的目标路径
     */
    static std::string getSymlinkTarget(const std::string &path);

    /**
     * @brief :查看路径是否属于链接
     */
    static bool isStringSymlink(const std::string &pathString);
};

#endif // !__BUILDER_FLOW_H__
