#include "ChkDateStepTwoEngine.h"
#include <opencv2/opencv.hpp>
#include "myutils.h"
#include "myqueue.h"

using namespace ai_matrix;

ChkDateStepTwoEngine::ChkDateStepTwoEngine() {}

ChkDateStepTwoEngine::~ChkDateStepTwoEngine() {}

APP_ERROR ChkDateStepTwoEngine::Init()
{
    bUseEngine_ = MyUtils::getins()->ChkIsHaveTarget("CHKDATE");
    if (!bUseEngine_)
    {
        LogWarn << "engineId_:" << engineId_ << " not use engine";
        return APP_ERR_OK;
    }

    strPort0_ = engineName_ + "_" + std::to_string(engineId_) + "_0";
    modelConfig_ = MyYaml::GetIns()->GetModelConfig("ChkDateStepTwoEngine");

    //读取模型信息
    APP_ERROR ret = ReadModelInfo();
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to read model info, ret = " << ret;
        return ret;
    }
    ret = InitModel();
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to read model info, ret = " << ret;
        return ret;
    }

    LogInfo << "AclChkDateStepTwoEngine Init ok";
    return APP_ERR_OK;
}

APP_ERROR ChkDateStepTwoEngine::InitModel()
{
    modelinfo.yolov5ClearityModelParam.uiClassNum = class_num;
    modelinfo.yolov5ClearityModelParam.uiClearNum = clear_num;
    modelinfo.yolov5ClearityModelParam.uiDetSize = det_size;
    modelinfo.yolov5ClearityModelParam.fScoreThreshold = score_threshold;
    modelinfo.yolov5ClearityModelParam.fNmsThreshold = nms_threshold;

    modelinfo.modelCommonInfo.uiModelWidth = model_width;
    modelinfo.modelCommonInfo.uiModelHeight = model_height;
    modelinfo.modelCommonInfo.uiInputSize = input_size;
    modelinfo.modelCommonInfo.uiOutputSize = output_size;
    modelinfo.modelCommonInfo.uiChannel = INPUT_CHANNEL;
    modelinfo.modelCommonInfo.uiBatchSize = batch_size;
    modelinfo.modelCommonInfo.strInputBlobName = INPUT_BLOB_NAME;
    modelinfo.modelCommonInfo.strOutputBlobName = OUTPUT_BLOB_NAME;

    string strModelName = "";

    int nRet = yolov5model.YoloV5ClearityInferenceInit(&modelinfo, strModelName, modelConfig_.strOmPath);
    if (nRet != 0)
    {
        LogInfo << "YoloV5ClassifyInferenceInit nRet:" << nRet;
        return APP_ERR_COMM_READ_FAIL;
    }
    return APP_ERR_OK;
}

APP_ERROR ChkDateStepTwoEngine::ReadModelInfo()
{
    char szAbsPath[PATH_MAX];
    // Get the absolute path of model file
    if (realpath(modelConfig_.strOmPath.c_str(), szAbsPath) == nullptr)
    {
        LogError << "Failed to get the real path of " << modelConfig_.strOmPath.c_str();
        return APP_ERR_COMM_NO_EXIST;
    }

    // Check the validity of model path
    int iFolderExist = access(szAbsPath, R_OK);
    if (iFolderExist == -1)
    {
        LogError << "ModelPath " << szAbsPath << " doesn't exist or read failed!";
        return APP_ERR_COMM_NO_EXIST;
    }

    //读取模型参数信息文件
    Json::Value jvModelInfo;
    if (!MyUtils::getins()->ReadJsonInfo(jvModelInfo, modelConfig_.strModelInfoPath))
    {
        LogError << "ModelInfoPath:" << modelConfig_.strModelInfoPath << " doesn't exist or read failed!";
        return APP_ERR_COMM_NO_EXIST;
    }

    model_width = jvModelInfo["model_width"].asInt();
    model_height = jvModelInfo["model_height"].asInt();
    clear_num = jvModelInfo["clear"].isArray() ? jvModelInfo["clear"].size() : 0;
    class_num = jvModelInfo["class"].isArray() ? jvModelInfo["class"].size() : 0;
    input_size = GET_INPUT_SIZE(model_width, model_height);
    output_size = GET_OUTPUT_SIZE(model_width, model_height, clear_num, class_num);
    det_size = clear_num + class_num + 5;
    score_threshold = modelConfig_.fScoreThreshold;
    nms_threshold = modelConfig_.fNMSTreshold;

    return APP_ERR_OK;
}

APP_ERROR ChkDateStepTwoEngine::DeInit()
{
    if (!bUseEngine_)
    {
        LogWarn << "engineId_:" << engineId_ << " not use engine";
        return APP_ERR_OK;
    }

    yolov5model.YoloV5ClearityInferenceDeinit();
    LogInfo << "ChkDateStepTwoEngine DeInit ok";
    return APP_ERR_OK;
}

/**
* push数据到队列，队列满时则休眠一段时间再push。
* inParam : const std::string strPort                           push的端口
          : const std::shared_ptr<ProcessData> &pProcessData    push的数据
* outParam: N/A
* return  : N/A
*/
void ChkDateStepTwoEngine::PushData(const std::string &strPort, const std::shared_ptr<ProcessData> &pProcessData)
{
    while (true)
    {
        int iRet = outputQueMap_[strPort]->push(std::static_pointer_cast<void>(pProcessData));
        if (iRet != 0)
        {
            LogDebug << "sourceid:" << pProcessData->iDataSource << " frameid:" << pProcessData->iFrameId << " push fail iRet:" << iRet;
            if (iRet == 2)
            {
                usleep(10000); // 10ms
                continue;
            }
        }
        break;
    }
}

APP_ERROR ChkDateStepTwoEngine::Process()
{
    if (!bUseEngine_)
    {
        LogWarn << "engineId_:" << engineId_ << " not use engine";
        return APP_ERR_OK;
    }
    int iRet = APP_ERR_OK;

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

        std::shared_ptr<ProcessData> pProcessData = std::static_pointer_cast<ProcessData>(pVoidData0);
        //组织输出数据
        std::shared_ptr<PostData> pPostData = std::make_shared<PostData>();
        pPostData->iModelType = MODELTYPE_CHKDATE;

        //获取图片
        if (pProcessData->iStatus == TRAINSTATUS_RUN || pProcessData->bIsEnd)
        {
            if (pProcessData->pData != nullptr && pProcessData->iSize != 0)
            {
                std::shared_ptr<PostData> ppostbuff = std::static_pointer_cast<PostData>(pProcessData->pVoidData);
                cv::Mat img(pProcessData->iHeight, pProcessData->iWidth, CV_8UC3, static_cast<uint8_t *>(pProcessData->pData.get()));   //RGB
                    
                for(int i = 0; i< ppostbuff->vecPostSubData.size(); i++)
                {
                    PostSubData postsubdata = ppostbuff->vecPostSubData[i];
                    if (postsubdata.iTargetType != CHKDATE)
                    {
                        continue;
                    }

                    cv::Rect step2_rect(cv::Point(postsubdata.step1Location.fLTX, postsubdata.step1Location.fLTY), cv::Point(postsubdata.step1Location.fRBX, postsubdata.step1Location.fRBY));
                    cv::Mat step2_image = img(step2_rect).clone();

                    //进行推理
                    std::vector<stDetection> res;
                    auto start = std::chrono::system_clock::now(); // 计时开始
                    yolov5model.YoloV5ClearityInferenceModel(step2_image, res);
                    auto end = std::chrono::system_clock::now();
                    LogInfo << "date2 inference time: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms";

                    PostSubData postSubDataNew;
                    postSubDataNew.iTargetType = postsubdata.iTargetType;
                    postSubDataNew.iBigClassId = postsubdata.iBigClassId;
                    postSubDataNew.iCarXH = postsubdata.iCarXH;
                    postSubDataNew.step1Location = postsubdata.step1Location;
                    //整理推理结果
                    //根据非极大值抑制的结果标注相关信息(画框,文字信息等)
                    //res.size()为每张图片上的识别到的对象数目
                    for (size_t j = 0; j < res.size(); j++)
                    {
                        SingleData singledata;
                        singledata.iLine = res[j].clear_id;
                        // singledata.iLine = -1;
                        singledata.iClassId = res[j].class_id;
                        singledata.fScore = res[j].class_conf;
                        // singledata.iAnchorId = -1;
                        singledata.fLTX = res[j].bbox[0];
                        singledata.fLTY = res[j].bbox[1];
                        singledata.fRBX = res[j].bbox[2];
                        singledata.fRBY = res[j].bbox[3];
                        singledata.fClear = res[j].clear_id;

                        MyUtils::getins()->Step2ResetLocation(singledata, 1.0, pProcessData, postSubDataNew.step1Location);

                        postSubDataNew.vecSingleData.emplace_back(singledata);

                        LogDebug << "sourceid:" << pProcessData->iDataSource << "  step2 after frameId:" << pProcessData->iFrameId
                                << " --iClassId:" << singledata.iClassId << " iLine:" << singledata.iLine << " confidence=" << singledata.fScore
                                << " lx=" << singledata.fLTX << " ly=" << singledata.fLTY << " rx=" << singledata.fRBX << " ry=" << singledata.fRBY;
                    }
                    pPostData->vecPostSubData.emplace_back(postSubDataNew);
                }
            }
        }

        //及时释放内存
        if (pProcessData->pData != nullptr)
        {
            pProcessData->pData = nullptr;
            pProcessData->iSize = 0;
        }

        // push端口0，第1步推理
        pProcessData->pVoidData = std::static_pointer_cast<void>(pPostData);
        PushData(strPort0_, pProcessData);
    }
    return APP_ERR_OK;
}

