#include "CurlHttp.h"
#include <regex>
#include "fmtlog.h"


/* ==================================================================================
 * *********************************** 全局变量 *************************************
 * ================================================================================== */



/* ==================================================================================
 * *********************************** 全局函数 *************************************
 * ================================================================================== */

/**
 * @brief 写入回调函数
 * 
 * @param contents 接收到的内容
 * @param size 内容大小,单位size_t
 * @param nmemb size_t的单位
 * @param userp 用户传入的接收区指针
 * @return size_t 
 */
static size_t WriteStringCallback(void* contents, size_t size, size_t nmemb, std::string* userStr)
{
    size_t newLength = size * nmemb;
    size_t oldLength = userStr->size();
    try
    {
        userStr->resize(oldLength + newLength);
    }
    catch(std::bad_alloc &e)
    {
        FMTLOG_ERROR("{}", e.what());
        return 0;
    }
    std::copy_n((char*)contents, newLength, userStr->begin() + oldLength);
    return size * nmemb;
}




/* ==================================================================================
 * *********************************** 成员函数 *************************************
 * ================================================================================== */


CurlHttp::CurlHttp()
{
    curl_global_init(CURL_GLOBAL_DEFAULT);
}


CurlHttp::~CurlHttp()
{
    curl_global_cleanup();
}

/**
 * @brief 获取信息
 * 
 * @param url 网址
 * @param response 返回的数据
 * @return true 
 * @return false 
 */
bool CurlHttp::Get(const std::string& url, std::string& response)
{
    CURL *curl;
    CURLcode res;
    curl = curl_easy_init();
    if(curl == nullptr)
    {
        FMTLOG_ERROR("curl_easy_init() failed");
        return false;
    }
    /* 设置为Get请求 */
    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");
    /* 设置url */
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    /* 设置重定向,遇到3xx返回值时自动重定向 */
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
    /* 设置https协议 */
    curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https");
    /* 设置写入回调函数 */
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteStringCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
    /* 设置超时 */
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);

    /* 发送请求 */
    bool result = true;
    res = curl_easy_perform(curl);
    if(res != CURLE_OK)
    {
        FMTLOG_ERROR("Get failed: {}, Url: {}", curl_easy_strerror(res), url);
        result = false;
    }

    curl_easy_cleanup(curl);
    return true;
}

/* 获取信息,带有http头 */
bool CurlHttp::Get(const std::string& url, const std::vector<std::string>& vecHeader, std::string& response)
{
    CURL *curl;
    CURLcode res;
    curl = curl_easy_init();
    if(curl == nullptr)
    {
        FMTLOG_ERROR("curl_easy_init() failed");
        return false;
    }
    /* 设置为Get请求 */
    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");
    /* 设置url */
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    /* 设置http头 */
    struct curl_slist *headers = NULL;
    if(!vecHeader.empty())
    {
        for(auto &header : vecHeader)
        {
            headers = curl_slist_append(headers, header.c_str());
        }
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    }
    /* 设置重定向,遇到3xx返回值时自动重定向 */
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
    /* 设置https协议 */
    curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https");
    /* 设置写入回调函数 */
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteStringCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
    /* 设置超时 */
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);

    /* 发送请求 */
    bool result = true;
    res = curl_easy_perform(curl);
    if(res != CURLE_OK)
    {
        FMTLOG_ERROR("Get failed: {}, Url:{}", curl_easy_strerror(res), url);
        result = false;
    }

    /* 清理内存 */
    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);
    return result;
}

/**
 * @brief 发送信息,不携带http头
 * 
 * @param url 
 * @param postData 
 * @param response 
 * @return true 
 * @return false 
 */
bool CurlHttp::Post(const std::string& url, const std::string& postData, std::string& response)
{
    CURL *curl;
    CURLcode res;
    curl = curl_easy_init();
    if(curl == nullptr)
    {
        FMTLOG_ERROR("curl_easy_init() failed");
        return false;
    }
    /* 设置用户名密码,可能有的话 */

    /* 设置动作功能和网址 */
    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    /* 设置重定向,遇到3xx返回值时自动重定向 */
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
    /* 设置https协议 */
    curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https");

    /* 设置包体 */
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str());
    curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, postData.size());

    /* 设置回调函数 */
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteStringCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);

    /* 设置连接超时和接收数据超时 */
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);

    /* 设置不发送任何信号,对于多线程有用 */
    // curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);

    /* 发送请求 */
    res = curl_easy_perform(curl);

    bool result = true;
    if(res != CURLE_OK)
    {
        FMTLOG_ERROR("Post failed: {}, Url: {}", curl_easy_strerror(res), url);
        result = false;
    }

    /* 清理curl */
    curl_easy_cleanup(curl);

    return result;
}

/**
 * @brief 发送带有Http头和包体的信息
 * 
 * @param url 网址
 * @param vecHeader http头,vector<string>格式,每个元素为一行,
 *                  如 User-Agent: Apifox/1.0.0 (https://apifox.com)
 * @param postData 发送的包体
 * @param response 返回的数据
 * @return true 
 * @return false 
 */
bool CurlHttp::Post(const std::string& url,const std::vector<std::string>& vecHeader, const std::string& postData, std::string& response)
{
    CURL *curl;
    CURLcode res;
    curl = curl_easy_init();
    if(curl == nullptr)
    {
        FMTLOG_ERROR("curl_easy_init() failed");
        return false;
    }
    /* 设置动作功能和网址 */
    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    /* 设置重定向,遇到3xx返回值时自动重定向 */
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
    /* 设置https协议 */
    curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https");

    /* 设置http头 */
    struct curl_slist *headers = NULL;
    if(!vecHeader.empty())
    {
        for(auto &header : vecHeader)
        {
            headers = curl_slist_append(headers, header.c_str());
        }
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    }

    /* 设置包体 */
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str());
    curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, postData.size());

    /* 设置回调函数 */
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteStringCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);

    /* 设置连接超时和接收数据超时 */
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);

    /* 设置不发送任何信号,对于多线程有用 */
    // curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);

    /* 发送请求 */
    res = curl_easy_perform(curl);

    bool result = true;
    if(res != CURLE_OK)
    {
        FMTLOG_ERROR("Post failed: {}, Url: {}", curl_easy_strerror(res), url);
        result = false;
    }

    curl_slist_free_all(headers);
    /* 清理curl */
    curl_easy_cleanup(curl);

    return result;
}