/*
 * Copyright (c) KylinSoft  Co., Ltd. 2024. All rights reserved.
 *
 * kaiming is licensed under the GPL v2.0+.
 * 
 * See the LICENSE file for more details.
 */

#include "KMRemoteSummary.h"

#include "common/KMLogger.h"

class KMRemoteSummary::Private
{
public:
};

KMRemoteSummary::KMRemoteSummary()
    : d(make_shared<Private>())
{
}

KMRemoteSummary::~KMRemoteSummary() { }

/**
 * @brief 通过 curl 命令行下载文件
 *
 * @param url 下载的文件链接
 * @param outputFile  存储到本地的文件路径
 * @return true 下载成功
 * @return false 下载失败
 */
bool KMRemoteSummary::curlCmdDownload(string url, string outputFile)
{
    string curlCmd = "curl -o " + outputFile + " -L -f " + url;

    int curlRes = system(curlCmd.c_str());
    if (curlRes != 0)
    {
        kmlogger.error("Failed to download %s file, please check the url(%s) and try again!", outputFile.c_str(), url.c_str());
        return false;
    }
    return true;
}

/**
 * 回调函数，用于处理下载的数据
 */
size_t KMRemoteSummary::WriteCallback(void *contents, size_t size, size_t nmemb, string *buffer)
{
    size_t totalSize = size * nmemb;
    buffer->append(static_cast<char *>(contents), totalSize);
    return totalSize;
}

/**
 * @brief 通过 curl api 下载文件
 *
 * @param url 下载的文件链接
 * @param outputFile 存储到本地的文件名
 * @return true 下载成功
 * @return false 下载失败
 */
bool KMRemoteSummary::curlApiDownload(string url, string outputFile)
{
    // 初始化 libcurl
    CURL *curl = curl_easy_init();
    if (!curl)
    {
        kmlogger.error("Failed to initialize libcurl.");
        return false;
    }

    // 打开文件用于保存下载的数据
    FILE *file = fopen(outputFile.c_str(), "wb");
    if (!file)
    {
        kmlogger.error("Failed to open %s file for writing!", outputFile.c_str());
        curl_easy_cleanup(curl);
        return false;
    }

    // 设置 libcurl 参数
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1L); // 设置超时时间为1秒

    // 执行请求
    CURLcode res = curl_easy_perform(curl);

    // 关闭文件
    fclose(file);

    // 校验下载结果
    if (res != CURLE_OK)
    {
        // 检查是否因为超时而中断
        if (res == CURLE_OPERATION_TIMEDOUT)
        {
            kmlogger.error("Failed to download file, Operation timed out.");
        }
        else
        {
            kmlogger.error("Failed to download file");
        }
        return false;
    }

    // 清理 libcurl
    curl_easy_cleanup(curl);

    return true;
}

/**
 * @brief 通过 curl api 下载二进制文件，比如 .gz 格式压缩文件
 *
 * @param url 下载的文件链接
 * @param outputFileName 输出本地的压缩文件名
 * @return true
 * @return false
 */
bool KMRemoteSummary::curlApiDownloadBinaryGZFile(const string &url, const string &outputFileName)
{
    CURL *curl = curl_easy_init();
    if (!curl)
    {
        kmlogger.error("Failed to init curl!");
        return false;
    }

    std::string receivedData;
    std::ofstream outputFile(outputFileName, std::ios::binary);

    if (!outputFile.is_open())
    {
        kmlogger.error("Failed to open %s file!", outputFileName.c_str());
        return false;
    }

    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &receivedData);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1L); // 设置超时时间为1秒

    CURLcode res = curl_easy_perform(curl);

    if (res != CURLE_OK)
    {
        // 检查是否因为超时而中断
        if (res == CURLE_OPERATION_TIMEDOUT)
        {
            kmlogger.error("Failed to download file, Operation timed out.");
        }
        else
        {
            kmlogger.error("Failed to download file");
        }
        return false;
    }

    outputFile.write(receivedData.c_str(), receivedData.size());

    curl_easy_cleanup(curl);
    outputFile.close();

    return true;
}

/**
 * @brief 解压缩 .gz 二进制压缩文件
 *
 * @param gzFileName .gz 压缩文件名
 * @param outputFileName 解压后的压缩文件名
 * @return true
 * @return false
 */
bool KMRemoteSummary::decompressGzFile(const std::string &gzFileName, const std::string &outputFileName)
{
    gzFile gzFile = gzopen(gzFileName.c_str(), "rb");
    if (!gzFile)
    {
        kmlogger.error("Failed to open %s file!", gzFileName.c_str());
        return false;
    }

    std::ofstream outputFile(outputFileName, std::ios::binary);
    if (!outputFile.is_open())
    {
        kmlogger.error("Failed to open file %s!", outputFileName.c_str());
        gzclose(gzFile);
        return false;
    }

    const int bufferSize = 8192;
    char buffer[bufferSize];

    int bytesRead;
    while ((bytesRead = gzread(gzFile, buffer, bufferSize)) > 0)
    {
        outputFile.write(buffer, bytesRead);
    }

    gzclose(gzFile);
    outputFile.close();

    return true;
}

/**
 * @brief 按字节从待解析二进制文件 kaiming-repo.idx 的起始位置 startPos，获取长度len的字节序，并存储在 outIndex 中
 *
 * @param filename 解析的kaiming-repo.idx 文件全路径名
 * @param startPos 解析outIndex的开始位置
 * @param len 解析的长度
 * @param outIndex 最终获取的32字节序
 * @return true 解析成功
 * @return false 解析失败
 */
bool KMRemoteSummary::getIndexFromParseSummaryIdxBinaryFile(string filename, int startPos, int len, string &outIndex)
{
    std::ifstream file(filename, std::ios::binary);
    if (!file.is_open())
    {
        kmlogger.error("Failed to open file %s", filename.c_str());
        return false;
    }

    // 定位到文件的起始位置
    file.seekg(startPos);

    // 设置输出的格式
    std::stringstream hexStream;
    hexStream << std::hex << std::setw(2) << std::setfill('0');

    // 逐字节读取文件并输出十六进制格式
    for (std::streamsize i = 0; i < len; ++i)
    {
        char byte;
        if (file.get(byte))
        {
            hexStream << static_cast<unsigned int>(static_cast<unsigned char>(byte));
        }
        else
        {
            kmlogger.error("Failed to read file %s", filename.c_str());
            return false;
        }
    }

    // 将字符串流内容复制到引用的字符串变量中
    outIndex = hexStream.str();

    file.close();

    return true;
}
