#include "ResultToHttpSrvEngine.h"
#include "myutils.h"


ResultToHttpSrvEngine::ResultToHttpSrvEngine() {}
ResultToHttpSrvEngine::~ResultToHttpSrvEngine() {}

APP_ERROR ResultToHttpSrvEngine::Init()
{
    strPort0_ = engineName_ + "_" + std::to_string(engineId_) + "_0";
    strUsername_ = MyYaml::GetIns()->GetStringValue("username");
    strPassword_ = MyYaml::GetIns()->GetStringValue("password");
    strURL_ = MyYaml::GetIns()->GetStringValue("gc_http_url");
    strGetTokenURL_ = MyYaml::GetIns()->GetStringValue("gc_gettoken_url");
    strImageSrv_ = MyYaml::GetIns()->GetPathValue("gc_image_srv");
    strPoundNo_ = MyYaml::GetIns()->GetStringValue("atlas_poundno");
    strFailSavePath_ = MyYaml::GetIns()->GetPathValue("gc_result_path") + "httpfailcontent.csv";
    strFailSaveBakPath_ = MyYaml::GetIns()->GetPathValue("gc_result_path") + "httpfailcontent_bak.csv";
    LogInfo << "ResultToHttpSrvEngine Init ok";
    return APP_ERR_OK;
}

APP_ERROR ResultToHttpSrvEngine::DeInit()
{
    LogDebug << "curl_easy_cleanup";
    curl_easy_cleanup(pCurl_);

    /*  这个处理移动到main中， 防止多线程调用
    LogDebug << "内存清理";
    curl_global_cleanup();
    */
    LogInfo << "ResultToHttpSrvEngine DeInit ok";
    return APP_ERR_OK;
}

/**
* libcurl回调函数
* inParam : void *pBuffer   回调内容地址
          : size_t size     回调单个数据大小
          : size_t nmemb    回调数据个数
* outParam: std::string &strResp    返回内容
* return  : 回调数据大小
*/
size_t ResultToHttpSrvEngine::WriteCallBack(void *pBuffer, size_t size, size_t nmemb, std::string &strResp)
{
    size_t sizes = size * nmemb;
    std::string strTemp((char*)pBuffer, sizes);
    strResp += strTemp;
    return sizes;
}

/**
* 调用http接口获取token
* inParam :
* outParam: std::string &strBladeAuth   返回的token信息
* return  : true:成功; false:失败
*/
bool ResultToHttpSrvEngine::GetToken(std::string &strBladeAuth)
{
    //1. 获得curl操作符
    if (nullptr == pCurl_)
    {
        LogDebug<<"pCurl_ is null, invoke curl_easy_init";
        pCurl_ = curl_easy_init();
        if (nullptr == pCurl_)
        {
            LogError << "curl_easy_init failed !";
            return false;
        }
    }

    //2. 设置head, 和表单
    //设置head信息
    struct curl_slist *pHeaderList = nullptr;
    pHeaderList = curl_slist_append(pHeaderList, "Authorization:Basic Y2xpZW50X2VudGVycHJpc2U6Y2xpZW50X2VudGVycHJpc2Vfc2VjcmV0");

    //设置表单信息
    curl_mime *pMultipart = curl_mime_init(pCurl_);
    curl_mimepart *pPart = curl_mime_addpart(pMultipart);
    curl_mime_name(pPart, "username");
    curl_mime_data(pPart, strUsername_.c_str(), CURL_ZERO_TERMINATED);
    pPart = curl_mime_addpart(pMultipart);
    curl_mime_name(pPart, "password");
    curl_mime_data(pPart, strPassword_.c_str(), CURL_ZERO_TERMINATED);
    pPart = curl_mime_addpart(pMultipart);
    curl_mime_name(pPart, "tenantId");
    curl_mime_data(pPart, "000000", CURL_ZERO_TERMINATED);
    pPart = curl_mime_addpart(pMultipart);
    curl_mime_name(pPart, "grant_type");
    curl_mime_data(pPart, "password", CURL_ZERO_TERMINATED);

    curl_easy_setopt(pCurl_, CURLOPT_CONNECTTIMEOUT, 1);              //连接超时(1s连接不上服务器返回超时)
    curl_easy_setopt(pCurl_, CURLOPT_URL, strGetTokenURL_.c_str());   //设置url
    curl_easy_setopt(pCurl_, CURLOPT_HTTPHEADER, pHeaderList);        //设置报文头
    curl_easy_setopt(pCurl_, CURLOPT_MIMEPOST, pMultipart);           //设置表单
    //curl_easy_setopt(pCurl_, CURLOPT_POSTFIELDS, strBody.c_str());    //设置post内容
    //curl_easy_setopt(pCurl_, CURLOPT_POST, 1);                        //设置操作为POST(为非0表示post)
    curl_easy_setopt(pCurl_, CURLOPT_WRITEFUNCTION, WriteCallBack);   //设置回调函数
    std::string strResponse;
    curl_easy_setopt(pCurl_, CURLOPT_WRITEDATA, &strResponse);        //设置回调参数

    //3. 执行http请求
    CURLcode res = curl_easy_perform(pCurl_);
    curl_slist_free_all(pHeaderList); //清除headerlist
    curl_mime_free(pMultipart);       //清除curl_mime
    curl_easy_reset(pCurl_);          //重置curl
    if (res != CURLE_OK)
    {
        LogError << " curl_easy_perform fail:" << curl_easy_strerror(res);
        return false;
    }

    //4. 执行成功解析响应内容
    Json::CharReaderBuilder readerBuilder;
    std::shared_ptr<Json::CharReader> reader(readerBuilder.newCharReader());
    Json::Value jvResponse;
    JSONCPP_STRING errs;
    if (!reader->parse(strResponse.data(), strResponse.data() + strResponse.size(), &jvResponse, &errs))
    {
        LogError << " response content fail " << strResponse;
        return false;
    }
    LogDebug << "GetToken resp:" << strResponse;

    strBladeAuth += jvResponse["token_type"].asString();
    strBladeAuth += " ";
    strBladeAuth += jvResponse["access_token"].asString();
    return true;
}

/**
* 列车信息提交http接口
* inParam : Json::Value &jvRequest 提交内容
* outParam:
* return  : true:提交成功; false:提交失败
*/
bool ResultToHttpSrvEngine::ResultToHttpSrv(Json::Value &jvRequest)
{
    //获取token
    std::string strBladeAuth("blade-auth:");
    if (!GetToken(strBladeAuth))
    {
        LogError << "comeTime:" << jvRequest["comeTime"].asString() << " carriageOrder:" << jvRequest["carriageOrder"].asInt() << " GetToken fail ";
        return false;
    }
    LogDebug << "strBladeAuth:" << strBladeAuth;

    //1. 获得curl操作符
    if (nullptr == pCurl_)
    {
        LogDebug<<"pCurl_ is null, invoke curl_easy_init";
        pCurl_ = curl_easy_init();
        if (nullptr == pCurl_)
        {
            LogError << "curl_easy_init failed !";
            return false;
        }
    }

    //2. 设置http请求信息
    Json::StreamWriterBuilder jswBuilder;
    std::string strRequest = Json::writeString(jswBuilder, jvRequest);
    LogDebug << "to http:" << strRequest;
    std::string strResponse;
    struct curl_slist *pHeaderList = nullptr;
    pHeaderList = curl_slist_append(pHeaderList, "Accept:application/json");
    pHeaderList = curl_slist_append(pHeaderList, "Content-Type:application/json");
    pHeaderList = curl_slist_append(pHeaderList, "charset:utf-8");
    pHeaderList = curl_slist_append(pHeaderList, strBladeAuth.c_str());
    curl_easy_setopt(pCurl_, CURLOPT_CONNECTTIMEOUT, 1);              //连接超时(1s连接不上服务器返回超时)
    curl_easy_setopt(pCurl_, CURLOPT_URL, strURL_.c_str());           //设置url
    curl_easy_setopt(pCurl_, CURLOPT_HTTPHEADER, pHeaderList);        //设置报文头
    curl_easy_setopt(pCurl_, CURLOPT_POSTFIELDS, strRequest.c_str()); //设置post内容
    curl_easy_setopt(pCurl_, CURLOPT_POST, 1);                        //设置操作为POST(为非0表示post)
    curl_easy_setopt(pCurl_, CURLOPT_WRITEFUNCTION, WriteCallBack);   //设置回调函数
    curl_easy_setopt(pCurl_, CURLOPT_WRITEDATA, &strResponse);        //设置回调参数

    //3. 执行http请求
    CURLcode res = curl_easy_perform(pCurl_);
    curl_slist_free_all(pHeaderList); //清除headerlist
    curl_easy_reset(pCurl_);          //重置curl
    if (res != CURLE_OK)
    {
        LogError << "comeTime:" << jvRequest["comeTime"].asString() << " carriageOrder:" << jvRequest["carriageOrder"].asInt()
                 << " curl_easy_perform fail:" << curl_easy_strerror(res);
        return false;
    }

    //4. 执行成功解析响应内容
    LogInfo << "http resp:" << strResponse;
    Json::CharReaderBuilder readerBuilder;
    std::shared_ptr<Json::CharReader> reader(readerBuilder.newCharReader());
    Json::Value jvResponse;
    JSONCPP_STRING errs;
    if (!reader->parse(strResponse.data(), strResponse.data() + strResponse.size(), &jvResponse, &errs))
    {
        LogError << "comeTime:" << jvRequest["comeTime"].asString() << " carriageOrder:" << jvRequest["carriageOrder"].asInt()
                 << " response content fail " << strResponse;
        return false;
    }

    if (!jvResponse["success"].asBool())
    {
        LogError << "comeTime:" << jvRequest["comeTime"].asString() << " carriageOrder:" << jvRequest["carriageOrder"].asInt()
                 << " response fail";
        return false;
    }

    LogInfo << "comeTime:" << jvRequest["comeTime"].asString() << " carriageOrder:" << jvRequest["carriageOrder"].asInt()
            << " post success";
    return true;
}

/**
* 处理上传失败的信息
* inParam : N/A
* outParam: N/A
* return  : N/A
*/
void ResultToHttpSrvEngine::DealHttpFailInfo()
{
    //队列有待处理数据，则先不处理异常数据。
    if (inputQueMap_[strPort0_]->getSize() > 0)
    {
        LogDebug << "have new data to process";
        return;
    }

    //文件不存在不处理
    if (access(strFailSavePath_.c_str(), F_OK) == -1)
    {
        LogDebug << "no exit file:" << strFailSavePath_;
        return;
    }

    bool bAllSucc = true;
    std::ifstream inFile(strFailSavePath_.c_str(), std::ios::in);
    if (!inFile.is_open())
    {
        LogError << strFailSavePath_ << " open fail";
        return;
    }

    int iDealCnt = 0;
    std::string strLine;
    while (getline(inFile, strLine))
    {
        Json::CharReaderBuilder jsrBuilder;
        std::shared_ptr<Json::CharReader> reader(jsrBuilder.newCharReader());
        Json::Value jvRequest;
        JSONCPP_STRING errs;
        if (!reader->parse(strLine.data(), strLine.data() + strLine.size(), &jvRequest, &errs))
        {
            LogError << "json parse fail content:" << strLine;
            return;
        }

        /*
        新数据到达后，异常数据还未开始处理，则直接关闭文件返回。先处理正常数据。
        新数据到达后，异常数据处理中，则把异常未处理的数据全部当处理失败写入新文件中。先处理正常数据。
        */
        if (inputQueMap_[strPort0_]->getSize() > 0)
        {
            LogDebug << "Abnormal data processing, have new data to process";
            if (0 == iDealCnt)
            {
                LogDebug << "Abnormal data processing not start";
                inFile.close();
                return;
            }
            SaveHttpFailInfo(jvRequest, strFailSaveBakPath_);
            bAllSucc = false;
            continue;
        }

        iDealCnt++;

        if (!ResultToHttpSrv(jvRequest))
        {
            LogError << "re http post err:" << strLine;
            //SaveHttpFailInfo(jvRequest, strFailSaveBakPath_);
//            bAllSucc = false;
            continue;
        }
    }
    inFile.close();

    if(bAllSucc)
    {
        //都处理成功，文件删除
        remove(strFailSavePath_.c_str());
    }
    else
    {
        //部分处理成功，重命名后再次被处理
        rename(strFailSaveBakPath_.c_str(), strFailSavePath_.c_str());
    }
}

/**
* 保存http上传失败的信息
* inParam : Json::Value &jvRequest     http失败信息
*         : std::string &strFilePath   保存路径
* outParam: N/A
* return  : true(成功);false(失败)
*/
bool ResultToHttpSrvEngine::SaveHttpFailInfo(Json::Value &jvRequest, std::string &strFilePath)
{
    std::ofstream outFile;
    outFile.open(strFilePath, std::ios::app);
    if (!outFile.is_open())
    {
        LogError << strFilePath << " open fail";
        return false;
    }

    Json::StreamWriterBuilder jswBuilder;
    jswBuilder["indentation"] = "";
    std::string strRequest = Json::writeString(jswBuilder, jvRequest);

    outFile << strRequest << std::endl;
    outFile.close();
    return true;
}


APP_ERROR ResultToHttpSrvEngine::Process()
{
    int iRet = APP_ERR_OK;
    if (0 == MyYaml::GetIns()->GetIntValue("gc_http_open"))
    {
        LogDebug << " gc_http_open value is 0";
        return APP_ERR_OK;
    }

    while (!isStop_)
    {
        std::shared_ptr<void> pVoidData0 = nullptr;
        inputQueMap_[strPort0_]->pop(pVoidData0);
        if (nullptr == pVoidData0)
        {
            usleep(1000); //1ms

            //无数据大于1分钟
            // iNoDataCnt_++;
            // if (iNoDataCnt_ > (60 * 1000))
            // {
            //     DealHttpFailInfo();
            //     iNoDataCnt_ = 0;
            // }
            continue;
        }
        iNoDataCnt_ = 0;

        std::shared_ptr<Train> pTrain = std::static_pointer_cast<Train>(pVoidData0);

        ai_matrix::DataSourceConfig dataSourceConfig = MyYaml::GetIns()->GetDataSourceConfigById(pTrain->trainNum.iDataSource); //获取摄像机参数
        char szCameraNo[4] = {0};
        sprintf(szCameraNo, "%03d", pTrain->trainNum.iDataSource + 1);

        char szNumImgPath[64] = {0};            //车号最优图片路径
        if (!pTrain->trainNum.strBestImg.empty())
        {
            sprintf(szNumImgPath, "/%03d/%s", pTrain->trainNum.iDataSource + 1, pTrain->trainNum.strBestImg.c_str());
        }

        char szProImgPath[64] = {0};            //属性最优图片路径
        if (!pTrain->trainPro.strBestImg.empty())
        {
            sprintf(szProImgPath, "/%03d/%s", pTrain->trainPro.iDataSource + 1, pTrain->trainPro.strBestImg.c_str());
        }

        // char szChkDateImgPath[64] = {0};        //定检期最优图片路径
        // if (!pTrain->chkDate.strBestImg.empty())
        // {
        //     sprintf(szChkDateImgPath, "%03d/%s", pTrain->chkDate.iDataSource + 1, pTrain->chkDate.strBestImg.c_str());
        // }

        // char szContainer1ImgPath[64] = {0};     //集装箱1最优图片路径
        // if (!pTrain->container1.strBestImg.empty())
        // {
        //     sprintf(szContainer1ImgPath, "%03d/%s", pTrain->container1.iDataSource + 1, pTrain->container1.strBestImg.c_str());
        // }

        // char szContainer2ImgPath[64] = {0};     //集装箱2最优图片路径
        // if (!pTrain->container2.strBestImg.empty())
        // {
        //     sprintf(szContainer2ImgPath, "%03d/%s", pTrain->container2.iDataSource + 1, pTrain->container2.strBestImg.c_str());
        // }

        std::string strTime = pTrain->strTrainName;
        strTime = MyUtils::getins()->replace_all_distinct(strTime, std::string("-"), std::string(":"));
        int iCategory = 0;
        if (pTrain->trainNum.iTrainTypeId == 3)
        {
            iCategory = 0;
        }
        else if(pTrain->trainNum.iTrainTypeId == 2)
        {
            iCategory = 1;
        }
        else if (pTrain->trainNum.iTrainTypeId == 6)
        {
            iCategory = 2;
        }
        else if (pTrain->trainNum.iTrainTypeId == 0)
        {
            iCategory = 3;
        }

        //组装post信息
        Json::Value jvRequest;
        Json::Value jvSubObj;
        jvSubObj["poundNo"] = strPoundNo_; // 股道号
        jvRequest["trainParams"] = jvSubObj;
        jvRequest["carriageType"] = pTrain->trainNum.strTrainType;                                                   // 车型
        jvRequest["carriageNumber"] = pTrain->trainNum.strTrainNum;                                                  // 车厢号
        jvRequest["carriageOrder"] = pTrain->iCarXH;                                                                 // 车节号
        jvRequest["cameraNumber"] = szCameraNo;                                                                      // 摄像头编号
        jvRequest["carriageTareweight"] = pTrain->trainPro.strSelf;                                                  // 皮重
        jvRequest["carriageLoad"] = pTrain->trainPro.strLoad;                                                        // 载重
        jvRequest["carriageVolume"] = pTrain->trainPro.strVolume;                                                    // 容积
        jvRequest["carriageChangelength"] = pTrain->trainPro.strChange;                                              // 换长
        jvRequest["proImageName"] = strImageSrv_ + pTrain->strTrainDate + "/" + pTrain->strTrainName + szProImgPath; // 属性图片
        jvRequest["numImageName"] = strImageSrv_ + pTrain->strTrainDate + "/" + pTrain->strTrainName + szNumImgPath; // 车号图片
        jvRequest["comeTime"] = pTrain->strTrainDate + " " + strTime;                                                // 来车时间
        jvRequest["carriageCategory"] = iCategory;                                                                   // 车厢类别:0敞车,1:漏洞矿车,2:平车,3:车头
        jvRequest["isTheLast"] = pTrain->bIsEnd ? 1 : 0;                                                             // 是否最后一节: 0:否,1:是
        jvRequest["startFrame"] = pTrain->iStartFrameId;                                                            //车厢开始帧
        jvRequest["endFrame"] = pTrain->iEndFrameId;                                                                //车厢结束帧
        jvRequest["skipFrame"] = dataSourceConfig.iSkipInterval;
        jvRequest["collectTime"] = MyUtils::getins()->Stamp2Time(pTrain->i64EndTimeStamp, true);//车厢切分的时间                                               //跳帧
        if (!ResultToHttpSrv(jvRequest))
        {
//            SaveHttpFailInfo(jvRequest, strFailSavePath_);
        }

        //列车结束后再次处理失败的信息
        if (pTrain->bIsEnd)
        {
            DealHttpFailInfo();
        }
    }
    return APP_ERR_OK;
}
