#include "ContainerDivideEngine.h"

#include <utility>


using namespace ai_matrix;

ContainerDivideEngine::ContainerDivideEngine() {}

ContainerDivideEngine::~ContainerDivideEngine() {}

APP_ERROR ContainerDivideEngine::Init()
{
    strPort0_ = engineName_ + "_" + std::to_string(engineId_) + "_0";
    strPort1_ = engineName_ + "_" + std::to_string(engineId_) + "_1";

    this->vecDataSourceConfig_ = Config::getins()->getAllDataSourceConfig();
    if (this->vecDataSourceConfig_.size() <= this->engineId_)
    {
        LogWarn << " -- " << engineName_ << "_" << engineId_ << " dataSource no set, Engine DeInit";
        return APP_ERR_OK;
    }
    this->dataSourceConfig_ = this->vecDataSourceConfig_.at(engineId_);
    this->baseConfig_ = Config::getins()->getBaseConfig();
    this->identifyConfig_ = Config::getins()->getIdentifyConfig();

    this->initParam();
    LogInfo << "DetectDivideEngine Init ok";
    return APP_ERR_OK;
}

APP_ERROR ContainerDivideEngine::DeInit()
{
    LogInfo << "DetectDivideEngine DeInit ok";
    return APP_ERR_OK;
}

/**
* 初始化参数信息
* inParam : N/A
* outParam: N/A
* return  : N/A
*/
void ContainerDivideEngine::initParam()
{
    this->bLastIsEnd = true;
    this->iContainerIndex = 0;
    this->pVStep2OutputDataPre_ = nullptr;
    this->vecContainerFail_.clear();
}

void ContainerDivideEngine::sendBestData(const VSelectBestData& selectBestData)
{
    // 直接发送第一个箱子的箱号
    std::shared_ptr<VSelectBestData> pVSelectBestData = std::make_shared<VSelectBestData>();
    *pVSelectBestData = selectBestData;
    outputQueMap_[strPort0_]->push(std::static_pointer_cast<void>(pVSelectBestData), true);
    LogInfo << " 帧:" << selectBestData.iFrameId
            << " 数据源:" << selectBestData.iDataSource
            << " 节:" << selectBestData.iContainerIndex
            << "  发送识别结果  " << selectBestData.strNumResult;
    this->stdContainerResult_ = selectBestData.strNumResult;
    this->iContainerIndex++;
}

void ContainerDivideEngine::makeResult(const std::shared_ptr<VStep2OutputData>& pVStep2OutputData,
                                       VSelectBestData & selectBestData) const
{
    selectBestData.strDetectDate = pVStep2OutputData->strDetectDate;
    selectBestData.strDetectTime = pVStep2OutputData->strDetectTime;
    selectBestData.iDataSource = pVStep2OutputData->iDataSource;
    selectBestData.iFrameId = pVStep2OutputData->iFrameId;
    selectBestData.strNumResult = pVStep2OutputData->step2ResultData.transInfo.strTmpResult;
    selectBestData.bIsChkFlag = pVStep2OutputData->step2ResultData.transInfo.bIsChkFlag;
    selectBestData.fSumScore = pVStep2OutputData->step2ResultData.fSubScoreSum;
    selectBestData.iContainerIndex = this->iContainerIndex;
    selectBestData.bHaveTwoContainer = pVStep2OutputData->vecCornerResultData.size() > 2;
}

/**
 * 依据像素进行集装箱切分
 * @param pVStep2OutputData
 */
void ContainerDivideEngine::divideByPixelInfo(std::shared_ptr<VStep2OutputData> pVStep2OutputData)
{
    auto vCompare = [](std::string a, std::string b) {
        if (a.size() != b.size()) return 999;

        int count = 0;
        for (int i = 0; i < a.size(); ++i)
        {
            if (a[i] != b[i])
            {
                ++count;
            }
        }
        return count;
    };

    if (!this->pVStep2OutputDataPre_)
    {
        if (pVStep2OutputData->step2ResultData.fScore == 0.0f) return;
        this->pVStep2OutputDataPre_ = pVStep2OutputData;
    }

    Step2ResultData step2ResultData = pVStep2OutputData->step2ResultData;
    Step2ResultData step2ResultData_pre = this->pVStep2OutputDataPre_->step2ResultData;

    float fCenterX = (step2ResultData.fRBX + step2ResultData.fLTX)/2;
    float fCenterY = (step2ResultData.fRBY + step2ResultData.fLTY)/2;
    float fCenterX_Pre = (step2ResultData_pre.fRBX + step2ResultData_pre.fLTX)/2;
    float fCenterY_Pre = (step2ResultData_pre.fRBY + step2ResultData_pre.fLTY)/2;

    LogDebug << " 帧:" << pVStep2OutputData->iFrameId
             << " 数据源:" << pVStep2OutputData->iDataSource
             << " Y差值:" << std::abs(fCenterY - fCenterY_Pre) << " X差值:" << std::abs(fCenterX - fCenterX_Pre);
    if ((pVStep2OutputData->iDataSource == 0
         && std::abs(fCenterY - fCenterY_Pre) > this->identifyConfig_.iTop_Y_SplitSpanPx)
        ||
        (pVStep2OutputData->iDataSource != 0
         && std::abs(fCenterX - fCenterX_Pre) > this->identifyConfig_.iSide_X_SplitSpanPx))
    {
        if (step2ResultData.transInfo.bIsChkFlag)
        {
            if (stdContainerResult_ != step2ResultData.transInfo.strTmpResult)
            {
                if (stdContainerResult_.empty())
                {
                    // 筛选不满足校验的部分  取最长的发送
                    int iSize = 0, iIndex = 0;
                    std::string strContainer_tmp;
                    for (int i = 0; i < this->vecContainerFail_.size(); i++)
                    {
                        this->vecContainerFail_[i];
                        if (iSize < this->vecContainerFail_[i].strNumResult.size())
                        {
                            iSize = this->vecContainerFail_[i].strNumResult.size();
                            iIndex = i;
                            strContainer_tmp = this->vecContainerFail_[i].strNumResult;
                        }
                    }

                    sendBestData(this->vecContainerFail_[iIndex]);
                }
                // 记录第二箱子
                this->vecContainerFail_.clear();
                VSelectBestData selectBestData;
                this->makeResult(pVStep2OutputData, selectBestData);
                this->sendBestData(selectBestData);
            }
        }
        else
        {
            // 筛选不满足校验的部分  取最长的发送
            int iSize = 0, iIndex = 0;
            std::string strContainer_tmp;
            for (int i = 0; i < this->vecContainerFail_.size(); i++)
            {
                this->vecContainerFail_[i];
                if (iSize < this->vecContainerFail_[i].strNumResult.size())
                {
                    iSize = this->vecContainerFail_[i].strNumResult.size();
                    iIndex = i;
                    strContainer_tmp = this->vecContainerFail_[i].strNumResult;
                }
            }
            if (this->vecContainerFail_.size() > 0 && this->iContainerIndex < 1)
            {
                sendBestData(this->vecContainerFail_[iIndex]);
            }

            // 把之前的清理掉再存新箱子的 （有风险，有可能切分错）
            this->vecContainerFail_.clear();
            VSelectBestData selectBestData;
            this->makeResult(pVStep2OutputData, selectBestData);
            this->vecContainerFail_.emplace_back(selectBestData);
        }
    }
    else
    {// 不满足像素差
        if (!this->stdContainerResult_.empty())
        {
            if (step2ResultData.transInfo.bIsChkFlag)
            {
                if (this->stdContainerResult_ != step2ResultData.transInfo.strTmpResult)
                {
//                    if (vCompare(this->stdContainerResult_, step2ResultData.transInfo.strTmpResult) > 2)
//                    {
                    this->vecContainerFail_.clear();
                    VSelectBestData selectBestData;
                    this->makeResult(pVStep2OutputData, selectBestData);
                    this->sendBestData(selectBestData);
//                    }
                }
            }
        }
        else
        {
            if (step2ResultData.transInfo.bIsChkFlag)
            {
                this->stdContainerResult_ = step2ResultData.transInfo.strTmpResult;
                this->vecContainerFail_.clear();
                VSelectBestData selectBestData;
                this->makeResult(pVStep2OutputData, selectBestData);
                this->sendBestData(selectBestData);
            }
            else
            {
                VSelectBestData selectBestData;
                this->makeResult(pVStep2OutputData, selectBestData);
                this->vecContainerFail_.emplace_back(selectBestData);
            }
        }
    }
}

bool ContainerDivideEngine::setectBestByCorner()
{
    if (!this->vecContainer_.empty())
    {
        // 汇总下vecContainer中，有几种正确的识别结果，选出识别次数最多的
        std::map<std::string, int> mapContainer_count;
        std::map<std::string, int> mapContainer_index;
        int i = 0;
        for (auto &container: this->vecContainer_)
        {
            mapContainer_count[container.strNumResult]++;
            mapContainer_index[container.strNumResult] = i;
            ++i;
        }
        std::string strBestContainer;
        int count = 0;
        // 取出现次数最高的 且与第一个箱子不一样的
        for (auto &it_max: mapContainer_count) {
            if (it_max.first.empty()) continue;
            if (it_max.second >= count) {
                count = it_max.second;
                strBestContainer = it_max.first;
            }
        }

        this->sendBestData(this->vecContainer_[mapContainer_index[strBestContainer]]);
        this->vecContainer_.clear();
        this->vecContainerFail_.clear();
    }
    else
    {
        if (this->vecContainerFail_.empty()) return false;
        // 汇总下vecContainer中，有几种正确的识别结果，选出识别次数最多的
        int iMaxSize = 0, iIndex = 0;
        std::map<std::string, int> mapContainer_index;
        int i = 0;
        for (auto &container: this->vecContainerFail_)
        {
            if (container.strNumResult.size() > iMaxSize) {
                iMaxSize = container.strNumResult.size();
                iIndex = i;
            }
            ++i;
        }

        this->sendBestData(this->vecContainerFail_[iIndex]);
        this->vecContainer_.clear();
        this->vecContainerFail_.clear();
    }
    return true;
}

/**
 * 依据箱角切分
 * @param pVStep2OutputData
 */
void ContainerDivideEngine::divideByCornerInfo(std::shared_ptr<VStep2OutputData> pVStep2OutputData)
{
    Step2ResultData step2ResultData = pVStep2OutputData->step2ResultData;

    if (pVStep2OutputData->vecCornerResultData.size() > 1 && this->iContainerIndex < 1)
    {
        if (pVStep2OutputData->vecCornerResultData.size() > 2)
        {
            LogWarn << "帧:" << pVStep2OutputData->iFrameId
                    << " 数据源:" << pVStep2OutputData->iDataSource
                    << " ---- >>> 检测到超过2个箱角，疑似画面过于倾斜或模型训练不足！";
            return;
        }
        float fDiffX = std::abs(pVStep2OutputData->vecCornerResultData[0].fLTX - pVStep2OutputData->vecCornerResultData[1].fLTX);
        float fDiffY = std::abs(pVStep2OutputData->vecCornerResultData[0].fLTY - pVStep2OutputData->vecCornerResultData[1].fLTY);

        if (fDiffX > this->identifyConfig_.iMaxContainerSpaceX || fDiffY > this->identifyConfig_.iMaxContainerSpaceY)
        {
            LogWarn << "帧:" << pVStep2OutputData->iFrameId
                    << " 数据源:" << pVStep2OutputData->iDataSource
                    << " ---- >>> 检测到超过2个间隔过远的箱角，疑似画面过于倾斜看到一个箱子的多个角或模型训练不足！";
            return;
        }

        LogInfo << "帧:" << pVStep2OutputData->iFrameId
                << " 数据源:" << pVStep2OutputData->iDataSource
                << " ---- >>> 检测到多个箱角，准备切分";

        if (pVStep2OutputData->iDataSource == 0 || this->dataSourceConfig_.strRunDirection == "right")
        {
            if (this->vecContainerFail_.empty())
            {
                VSelectBestData selectBestData;
                selectBestData.strDetectDate = pVStep2OutputData->strDetectDate;
                selectBestData.strDetectTime = pVStep2OutputData->strDetectTime;
                selectBestData.iDataSource = pVStep2OutputData->iDataSource;
                selectBestData.iFrameId = 1;
                selectBestData.strNumResult = "invalid-?";
                selectBestData.iContainerIndex = this->iContainerIndex;
                selectBestData.bHaveTwoContainer = true;
                this->vecContainerFail_.emplace_back(selectBestData);
            }
            this->setectBestByCorner();
            VSelectBestData selectBestData;
            this->makeResult(pVStep2OutputData, selectBestData);
            if (step2ResultData.transInfo.bIsChkFlag)
            {
                this->vecContainer_.emplace_back(selectBestData);
            }
            else
            {
                this->vecContainerFail_.emplace_back(selectBestData);
            }
            return;
        }

        if (this->dataSourceConfig_.strRunDirection == "left")
        {
            LogWarn << "向左行驶的一侧暂不支持箱角识别，请及时修改参数 divide_mode: \"pixel\"";
            return;
        }
    }
    else
    {
        VSelectBestData selectBestData;
        this->makeResult(pVStep2OutputData, selectBestData);
        if (step2ResultData.transInfo.bIsChkFlag)
        {
            this->vecContainer_.emplace_back(selectBestData);
        }
        else
        {
            this->vecContainerFail_.emplace_back(selectBestData);
        }
    }
}

/**
 * 切分车厢
 * @param pVStep2OutputData
 */
void ContainerDivideEngine::divideInfo(std::shared_ptr<VStep2OutputData> pVStep2OutputData)
{
//    if (!this->pVStep2OutputDataPre_) return;

    if (this->iContainerIndex > 1) return;

    if (this->vecDataSourceConfig_[pVStep2OutputData->iDataSource].strDivideModel == "corner")
    {
        this->divideByCornerInfo(pVStep2OutputData);
    }
    else
    {
        this->divideByPixelInfo(pVStep2OutputData);
    }
}

APP_ERROR ContainerDivideEngine::Process()
{
    int iRet = APP_ERR_OK;
    while (!isStop_)
    {
        //pop端口0
        std::shared_ptr<void> pVoidData0 = nullptr;
        iRet = inputQueMap_[strPort0_]->pop(pVoidData0);
        if (nullptr == pVoidData0)
        {
            usleep(1000);
            continue;
        }
        std::shared_ptr<VStep2OutputData> pVStep2OutputData = std::static_pointer_cast<VStep2OutputData>(pVoidData0);

        if (pVStep2OutputData->bIsEnd)
        {
//            if (this->bLastIsEnd == pVStep2OutputData->bIsEnd) continue;

            std::shared_ptr<VSelectBestData> pVSelectBestData = std::make_shared<VSelectBestData>();
            pVSelectBestData->bIsEnd = true;
            pVSelectBestData->iDataSource = pVStep2OutputData->iDataSource;
            pVSelectBestData->strDetectDate = pVStep2OutputData->strDetectDate;
            pVSelectBestData->strDetectTime = pVStep2OutputData->strDetectTime;

            if (this->iContainerIndex > 1)
            {
                outputQueMap_[strPort0_]->push(std::static_pointer_cast<void>(pVSelectBestData), true);
                this->initParam();
                continue;
            }

            if (this->vecDataSourceConfig_[pVStep2OutputData->iDataSource].strDivideModel == "corner")
            {
                if (!this->vecContainerFail_.empty() || !this->vecContainer_.empty())
                {
                    this->setectBestByCorner();
                }
            }
            else
            {
                if (!this->vecContainerFail_.empty())
                {
                    // 筛选不满足校验的部分  取最长的发送
                    int iSize = 0, iIndex = 0;
                    std::string strContainer_tmp;
                    for (int i = 0; i < this->vecContainerFail_.size(); i++)
                    {
                        this->vecContainerFail_[i];
                        if (iSize < this->vecContainerFail_[i].strNumResult.size())
                        {
                            iSize = this->vecContainerFail_[i].strNumResult.size();
                            iIndex = i;
                            strContainer_tmp = this->vecContainerFail_[i].strNumResult;
                        }
                    }
                    this->vecContainerFail_[iIndex].bIsEnd = true;
                    sendBestData(this->vecContainerFail_[iIndex]);
                    this->initParam();
                    continue;
                }
            }


            outputQueMap_[strPort0_]->push(std::static_pointer_cast<void>(pVSelectBestData), true);
            this->initParam();
            continue;
        }

        this->bLastIsEnd = false;

        std::string strFilePath ;
        strFilePath = this->baseConfig_.strDebugResultPath + "/"
                + pVStep2OutputData->strDetectDate + "/"
                + StringUtil::getins()->replace_all_distinct(pVStep2OutputData->strDetectTime, ":", "-") + "/"
                + std::to_string(pVStep2OutputData->iFrameId) + "_" + std::to_string(pVStep2OutputData->iDataSource) + ".json";

        // 先读取文本内容，追加新的信息后再写入
        Json::Value jvFrameInfo;
        if (!FileUtil::getins()->readJsonInfo(jvFrameInfo, strFilePath))
        {
            LogError << "read fail:" << strFilePath;
        }
//        jvFrameInfo["isEnd"] = (pVStep2OutputData->bIsEnd || jvFrameInfo["isEnd"].asBool());

        // 识别结果存储
        float fCenter = pVStep2OutputData->step2ResultData.fLTX + (pVStep2OutputData->step2ResultData.fRBX - pVStep2OutputData->step2ResultData.fLTX) / 2;

//        this->mapContainerCenterInfo_.insert(std::make_pair(pVStep2OutputData->iFrameId, fCenter));

        Json::Value jvInfo;
        jvInfo["classid"] = pVStep2OutputData->step2ResultData.iClassId;
        jvInfo["score"] = pVStep2OutputData->step2ResultData.fScore;
        jvInfo["ltx"] = pVStep2OutputData->step2ResultData.fLTX;
        jvInfo["lty"] = pVStep2OutputData->step2ResultData.fLTY;
        jvInfo["rbx"] = pVStep2OutputData->step2ResultData.fRBX;
        jvInfo["rby"] = pVStep2OutputData->step2ResultData.fRBY;

        jvFrameInfo["step1"].append(jvInfo);

        for (auto &step2ResultData : pVStep2OutputData->vecCornerResultData)
        {
            float fCenter = step2ResultData.fLTX + (step2ResultData.fRBX - step2ResultData.fLTX) / 2;

//            this->mapContainerCenterInfo_.insert(std::make_pair(pVStep2OutputData->iFrameId, fCenter));

            Json::Value jvInfo;
            jvInfo["classid"] = step2ResultData.iClassId;
            jvInfo["score"] = step2ResultData.fScore;
            jvInfo["ltx"] = step2ResultData.fLTX;
            jvInfo["lty"] = step2ResultData.fLTY;
            jvInfo["rbx"] = step2ResultData.fRBX;
            jvInfo["rby"] = step2ResultData.fRBY;

            jvFrameInfo["step1"].append(jvInfo);
        }

        FileUtil::getins()->writeJsonInfo(jvFrameInfo, strFilePath);

        if (pVStep2OutputData->step2ResultData.fScore != 0.0f)
        {
            LogDebug << " 帧:" << pVStep2OutputData->iFrameId
                     << " 数据源:" << pVStep2OutputData->iDataSource
                     << " - "<< pVStep2OutputData->step2ResultData.transInfo.strTmpResult;
        }

        this->divideInfo(pVStep2OutputData);

    }
    return APP_ERR_OK;
}
