/*
 * Copyright 2023 KylinSoft Co., Ltd.
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <https://www.gnu.org/licenses/>.
 */

#include "processinfohelper.h"
#include <QDebug>
#include <QFile>
#include <fstream>
#include <iostream>
#include <libproc2/pids.h> // libproc2 的主头文件
#include <sstream>
#include <string>
#include <unordered_map>

namespace process_info_helper
{

std::string cmdline(int pid)
{
    if (pid <= 0) {
        return std::string();
    }
    std::string cmdlineFilePath = "/proc/" + std::to_string(pid) + "/cmdline";
    std::ifstream cmdlineFile(cmdlineFilePath);

    if (!cmdlineFile.is_open()) {
        qWarning() << "cmdlineFile open failed!" << pid;
        return std::string();
    }

    std::stringstream buffer;
    buffer << cmdlineFile.rdbuf();
    std::string cmdline = buffer.str();

    // Replace '\0' with ' '
    for (size_t i = 0; i < cmdline.length(); ++i) {
        if (cmdline[i] == '\0') {
            cmdline[i] = ' ';
        }
    }

    if (!cmdline.empty() && cmdline.at(cmdline.length() - 1) == ' ') {
        cmdline.pop_back();
    }

    return cmdline;
}

std::string commContent(int pid)
{
    if (pid <= 0) {
        return std::string();
    }
    std::string commPath = "/proc/" + std::to_string(pid) + "/comm";
    std::ifstream commFile(commPath);

    if (!commFile.is_open()) {
        qWarning() << "commFile open failed!" << pid;
        return std::string();
    }

    std::stringstream buffer;
    buffer << commFile.rdbuf();
    std::string commContent = buffer.str();

    // Replace '\0' with ' '
    for (size_t i = 0; i < commContent.length(); ++i) {
        if (commContent[i] == '\0') {
            commContent[i] = ' ';
        }
    }

    if (!commContent.empty() && commContent.at(commContent.length() - 1) == ' ') {
        commContent.pop_back();
    }

    return commContent;
}

bool processExists(int pid)
{
    std::ifstream file("/proc/" + std::to_string(pid));
    return file.good();
}

int parentPid(int pid)
{
    QString procStatus = QString("/proc/%1/status").arg(pid);
    QFile file(procStatus);
    if (!file.open(QIODevice::ReadOnly)) {
        qWarning() << "Get parent id failed, " << pid << file.errorString();
        return -1;
    }
    QTextStream ts(&file);
    QString line;
    while (ts.readLineInto(&line)) {
        if (line.contains("PPid:")) {
            QString strPpid = line.split(":").last();
            strPpid.remove(QChar::Space);
            return strPpid.toUInt();
        }
    }
    return -1;
}

std::vector<int> childPids(int parentPid)
{
    std::vector<int> pids;
    struct pids_info *info = NULL;

    // 设置我们需要获取的数据项
    enum pids_item items[] = {
        PIDS_ID_PID, // 进程ID
        PIDS_ID_PPID // 父进程ID
    };
    const int numitems = sizeof(items) / sizeof(enum pids_item);

    // 初始化进程信息结构
    if (procps_pids_new(&info, items, numitems) < 0) {
        qWarning() << "Failed to initialize pids_info";
        return pids;
    }

    // 获取进程信息
    struct pids_stack *stack;
    while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
        // 使用 PIDS_VAL 宏获取值
        int ppid = PIDS_VAL(1, s_int, stack, info); // PIDS_ID_PPID
        if (ppid == parentPid) {
            int pid = PIDS_VAL(0, s_int, stack, info); // PIDS_ID_PID
            pids.emplace_back(pid);
        }
    }

    procps_pids_unref(&info);

    return pids;
}

std::vector<int> childPids(const std::vector<int> &parentPids)
{
    std::vector<int> childPids;
    std::unordered_map<int, int> mp;
    for (int i = 0; i < parentPids.size(); ++i) {
        mp[parentPids[i]]++;
    }

    struct pids_info *info = NULL;

    // 设置我们需要获取的数据项
    enum pids_item items[] = {
        PIDS_ID_PID, // 进程ID
        PIDS_ID_PPID // 父进程ID
    };
    const int numitems = sizeof(items) / sizeof(enum pids_item);

    // 初始化进程信息结构
    if (procps_pids_new(&info, items, numitems) < 0) {
        qWarning() << "Failed to initialize pids_info";
        return childPids;
    }

    // 获取进程信息
    struct pids_stack *stack;
    while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
        // 使用 PIDS_VAL 宏获取值
        int ppid = PIDS_VAL(1, s_int, stack, info); // PIDS_ID_PPID
        if (mp.find(ppid) != mp.end()) {
            int pid = PIDS_VAL(0, s_int, stack, info); // PIDS_ID_PID
            childPids.emplace_back(pid);
        }
    }

    procps_pids_unref(&info);

    if (childPids.empty()) {
        return childPids;
    }

    // 函数其余部分保持不变
    std::vector<int> nextSubPids = process_info_helper::childPids(childPids);
    if (nextSubPids.empty()) {
        return childPids;
    }

    for (const auto &pid : childPids) {
        if (std::find(nextSubPids.cbegin(), nextSubPids.cend(), pid) != nextSubPids.cend()) {
            nextSubPids.erase(std::remove(nextSubPids.begin(), nextSubPids.end(), pid), nextSubPids.end());
        }
    }

    childPids.insert(childPids.end(), nextSubPids.begin(), nextSubPids.end());

    return childPids;
}

std::string cgroup(int pid)
{
    if (pid <= 0) {
        return std::string();
    }
    std::string cgroupFilePath = "/proc/" + std::to_string(pid) + "/cgroup";
    std::ifstream cgroupFile(cgroupFilePath);

    if (!cgroupFile.is_open()) {
        qWarning() << "cgroupFile open failed!" << pid;
        return std::string();
    }

    std::string cgroup, line;
    while (std::getline(cgroupFile, line)) {
        auto lastPos = line.rfind("::");
        if (lastPos != std::string::npos) {
            cgroup = line.substr(lastPos + 2);
            break;
        }
    }
    cgroupFile.close();
    return cgroup;
}

std::vector<int> pidsOfCgroup(const std::string &cgroupPath)
{
    std::ifstream cgroupFile(cgroupPath + "/cgroup.procs");

    if (!cgroupFile.is_open()) {
        return {};
    }

    std::string line;
    std::vector<int> pids;
    while (std::getline(cgroupFile, line)) {
        pids.emplace_back(std::stoi(line));
    }
    cgroupFile.close();
    return pids;
}

bool isShellScript(const QString &filePath)
{
    // QProcess process;
    // process.start("file", QStringList() << "--mime-type" << filePath);
    // if (!process.waitForFinished()) {
    //     qDebug() << "执行file命令失败";
    //     return false;
    // }

    // QString output = process.readAllStandardOutput();
    // return (output.contains("text/x-shellscript") || output.contains("shell script"));
    std::ifstream file(filePath.toStdString());
    if (!file.is_open()) return false;

    std::string firstLine;
    std::getline(file, firstLine);
    file.close();

    return firstLine.find("#!") == 0;

}

}
