#include "SelectBestContainerEngine.h"
#include "myutils.h"

#define CONTAINER_LEN  11

using namespace ai_matrix;


SelectBestContainerEngine::SelectBestContainerEngine() {}

SelectBestContainerEngine::~SelectBestContainerEngine() {}

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

    //获取几个摄像头识别集装箱
    std::map<int, ai_matrix::DataSourceConfig> mapUseDataSouceCfg = MyYaml::GetIns()->GetUseDataSourceConfig();
    for (auto iter = mapUseDataSouceCfg.begin(); iter != mapUseDataSouceCfg.end(); iter++)
    {
        if (iter->second.strTarget.find("CONTAINER") != std::string::npos)
        {
            LogDebug << "DataSource:" << iter->first << " deal CONTAINER";
            mapDataSourceIsEnd_[iter->first] = false;

            if (iter->second.strTarget.find("CONTAINER_T") != std::string::npos)
            {
                setTopContainerSid_.insert(iter->first);
            }
        }
    }

    InitParam();
    LogInfo << "SelectBestContainerEngine Init ok";
    return APP_ERR_OK;
}

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

/**
* 初始化参数信息
* inParam : N/A
* outParam: N/A
* return  : N/A
*/
void SelectBestContainerEngine::InitParam()
{
    for (auto iter = mapDataSourceIsEnd_.begin(); iter != mapDataSourceIsEnd_.end(); iter++)
    {
        iter->second = false;
    }
    mapTCFail0_.clear();
    mapTCSucc0_.clear();
    mapStep1Pre0_.clear();
    mapSecondFlag0_.clear();

    mapTCFail1_.clear();
    mapTCSucc1_.clear();
    mapStep1Pre1_.clear();
    mapSecondFlag1_.clear();
}

/**
* 验证集装箱号是否满足规则(前10位依次与2^0 ~2^9相乘）/11 所得余数，即是校验位)
* inParam : std::string &strContainerNo   集装箱号信息 
* outParam: N/A
* return  : true(校验通过)/false(校验失败)
*/
bool SelectBestContainerEngine::VerifyContainerNo(std::string &strContainerNo)
{
    bool bChkFlag = false;

    if(strContainerNo.length() != CONTAINER_LEN)
    {
        return bChkFlag;
    }

    int iSum = 0;
    for (int i = 0; i < strContainerNo.length()-1; ++i)
    {
        iSum += mapExchange_[strContainerNo.substr(i, 1)] * int(pow(2.0, i));
    }

    //当校验位等于10时要继续模运算,iSum % 11 % 10,保证最终结果为0~9之间的数
    int iChkValue = iSum % 11 % 10;
    if (iChkValue == atoi(strContainerNo.substr(strContainerNo.length()-1, 1).c_str()))
    {
        bChkFlag = true;
    }
    return bChkFlag;
}

/**
* 构造最优集装箱信息
* inParam : N/A
* outParam: N/A
* return  : N/A
*/
void SelectBestContainerEngine::MakeBestContainer(Container &containerBest,
                                                  TransSubData *pTransSubData,
                                                  std::shared_ptr<ProcessData> pProcessData)
{
    // std::string strContainerInfo;
    // for (size_t i = 0; i < pTransSubData->vecTransInfo.size(); i++)
    // {
    //     TransInfo transInfo = pTransSubData->vecTransInfo[i];
    //     for (size_t j = 0; j < transInfo.vecValue.size(); j++)
    //     {
    //         strContainerInfo += transInfo.vecValue[j];
    //     }
    // }
    containerBest.iDataSource = pProcessData->iDataSource;
    containerBest.i64TimeStamp = pProcessData->i64TimeStamp;
    containerBest.bIsEnd = pProcessData->bIsEnd;
    containerBest.strTrainDate = pProcessData->strTrainDate;
    containerBest.strTrainName = pProcessData->strTrainName;
    containerBest.step1Location = pTransSubData->step1Location;
    containerBest.strBestImg = std::to_string(pProcessData->iFrameId) + ".jpg";
    containerBest.fScoreSum = pTransSubData->fScoreSum;
    containerBest.strContainerNo = pTransSubData->strAllValue;
}

/**
* 处理失败的集装箱信息
* inParam : TransSubData &transSubData                  单个转换结果
          : std::shared_ptr<ProcessData> pProcessData   帧处理信息
          : int iIndex                                  第几个箱号
* outParam: N/A
* return  : N/A
*/
void SelectBestContainerEngine::DealFailContianer(TransSubData &transSubData, std::shared_ptr<ProcessData> pProcessData,
                                                  int iIndex, std::map<int, TrainContainer> &mapTCFail)
{
    if (mapTCFail.find(pProcessData->iDataSource) == mapTCFail.end())
    {
        mapTCFail[pProcessData->iDataSource] = TrainContainer();
    }

    Container containerTemp;
    if (iIndex == 1)
    {
        containerTemp = mapTCFail[pProcessData->iDataSource].container1;
    }
    else if (iIndex == 2)
    {
        containerTemp = mapTCFail[pProcessData->iDataSource].container2;
    }

    if (transSubData.strAllValue.length() > containerTemp.strContainerNo.length())
    {
        Container containerBest;
        MakeBestContainer(containerBest, &transSubData, pProcessData);

        if (iIndex == 1)
        {
            mapTCFail[pProcessData->iDataSource].container1 = containerBest;
        }
        else if (iIndex == 2)
        {
            mapTCFail[pProcessData->iDataSource].container2 = containerBest;
        }
    }
}

/**
* 处理成功的集装箱信息
* inParam : TransSubData &transSubData                  单个转换结果
          : std::shared_ptr<ProcessData> pProcessData   帧处理信息
* outParam: N/A
* return  : N/A
*/
void SelectBestContainerEngine::DealSuccContianer(TransSubData &transSubData, std::shared_ptr<ProcessData> pProcessData,
                                                  int iIndex, std::map<int, TrainContainer> &mapTCSucc)
{
    if (mapTCSucc.find(pProcessData->iDataSource) == mapTCSucc.end())
    {
        mapTCSucc[pProcessData->iDataSource] = TrainContainer();
    }

    //iIndex == 1表示第一个集装箱，只有第一个时才设置到第一个集装箱中。防止第一个未校验通过，第二个校验通过的设置为1.
    if (iIndex == 1)
    {
        Container containerTemp1 = mapTCSucc[pProcessData->iDataSource].container1;
        if (containerTemp1.strContainerNo.empty() || transSubData.strAllValue == containerTemp1.strContainerNo)
        {
            if (transSubData.fScoreSum > containerTemp1.fScoreSum)
            {
                Container containerBest;
                MakeBestContainer(containerBest, &transSubData, pProcessData);
                mapTCSucc[pProcessData->iDataSource].container1 = containerBest;
            }
            return;
        }
    }

    Container containerTemp2 = mapTCSucc[pProcessData->iDataSource].container2;
    if (containerTemp2.strContainerNo.empty() || transSubData.strAllValue == containerTemp2.strContainerNo)
    {
        if (transSubData.fScoreSum > containerTemp2.fScoreSum)
        {
            Container containerBest;
            MakeBestContainer(containerBest, &transSubData, pProcessData);
            mapTCSucc[pProcessData->iDataSource].container2 = containerBest;
        }
    }
}

/**
* 识别成功和失败中选取最优结果
* inParam : std::shared_ptr<TrainContainer> pTrainContainer     选优结果
* outParam: N/A
* return  : N/A
*/
void SelectBestContainerEngine::SelectBestTrainContainer(std::shared_ptr<TrainContainer> pTrainContainer)
{
    Container container1;
    Container container2;
    /*
    数据源之间选择时，正确的选得分高的，错误的选长度长的。
    优先从正向数据中获取，正向未通过校验或正向无数据再取反向数据
    */

    //1.获取正向校验通过数据
    for (auto iterSucee = mapTCSucc0_.begin(); iterSucee != mapTCSucc0_.end(); iterSucee++)
    {
        LogDebug << "sourceid:" << iterSucee->first << " frameid:" << iterSucee->second.container1.strBestImg
                 << " containerNo1:" << iterSucee->second.container1.strContainerNo << " frameid:" << iterSucee->second.container2.strBestImg
                 << " containerNo2:" << iterSucee->second.container2.strContainerNo;
        if (!iterSucee->second.container1.strContainerNo.empty() && iterSucee->second.container1.fScoreSum > container1.fScoreSum)
        {
            container1 = iterSucee->second.container1;
        }

        if (!iterSucee->second.container2.strContainerNo.empty() && iterSucee->second.container2.fScoreSum > container2.fScoreSum)
        {
            if (iterSucee->second.container2.strContainerNo == container1.strContainerNo)
            {
                LogError << "sourceid:" << iterSucee->first << " frameid:" << iterSucee->second.container2.strBestImg
                         << " containerNo2:" << iterSucee->second.container2.strContainerNo << " equal containerNo1";
                continue;
            }
            container2 = iterSucee->second.container2;
        }
    }
    pTrainContainer->container1 = container1;
    pTrainContainer->container2 = container2;
    if (!pTrainContainer->container1.strContainerNo.empty() && !pTrainContainer->container2.strContainerNo.empty())
    {
        //正向全部识别成功直接返回。
        return;
    }

    //2. 正向没全部识别成功，获取反向校验通过数据
    Container containerRev1;
    Container containerRev2;
    for (auto iterSucee = mapTCSucc1_.begin(); iterSucee != mapTCSucc1_.end(); iterSucee++)
    {
        LogDebug << "reverse sourceid:" << iterSucee->first << " frameid:"<< iterSucee->second.container1.strBestImg 
                 << " containerNo1:" << iterSucee->second.container1.strContainerNo << " frameid:"<< iterSucee->second.container2.strBestImg 
                 << " containerNo2:" << iterSucee->second.container2.strContainerNo;
        if (!iterSucee->second.container1.strContainerNo.empty() && iterSucee->second.container1.fScoreSum > containerRev1.fScoreSum)
        {
            containerRev1 = iterSucee->second.container1;
        }

        if (!iterSucee->second.container2.strContainerNo.empty() && iterSucee->second.container2.fScoreSum > containerRev2.fScoreSum)
        {
            containerRev2 = iterSucee->second.container2;
        }
    }

    //反向识别且和正向不相等则，按时间顺序设置。
    if (!containerRev1.strContainerNo.empty() && containerRev1.strContainerNo != pTrainContainer->container1.strContainerNo)
    {
        //正向没识别第一个时，反向识别，则反向作为第一个。
        if (pTrainContainer->container1.strContainerNo.empty())
        {
            pTrainContainer->container1 = containerRev1;
        }
        else
        {
            //正向识别第一个，则肯定没识别到第二个，因此按时间处理下。如果反向也只识别到第一个，则作为第二个集装箱号。
            Container container1Temp = pTrainContainer->container1;
            if(container1Temp.i64TimeStamp >= containerRev1.i64TimeStamp)
            {
                pTrainContainer->container2 = container1Temp;
                pTrainContainer->container1 = containerRev1;
            }
            else if (containerRev2.strContainerNo.empty())
            {
                pTrainContainer->container2 = containerRev1;
            }
        }
    }
    if (!containerRev2.strContainerNo.empty() && containerRev2.strContainerNo != pTrainContainer->container2.strContainerNo)
    {
        if (pTrainContainer->container2.strContainerNo.empty() )
        {
            pTrainContainer->container2 = containerRev2;
        }
    }

    if (!pTrainContainer->container1.strContainerNo.empty() && !pTrainContainer->container2.strContainerNo.empty())
    {
        return;
    }

    //3. 获取正向未校验通过数据
    for (auto iterFail = mapTCFail0_.begin(); iterFail != mapTCFail0_.end(); iterFail++)
    {
        LogDebug << "sourceid:" << iterFail->first << " frameid:"<< iterFail->second.container1.strBestImg 
                 << " containerNo1:" << iterFail->second.container1.strContainerNo << " frameid:"<< iterFail->second.container2.strBestImg 
                 << " containerNo2:" << iterFail->second.container2.strContainerNo;
        
        if (pTrainContainer->container1.strContainerNo.empty())
        {
            if (iterFail->second.container1.strContainerNo.length() > pTrainContainer->container1.strContainerNo.length())
            {
                pTrainContainer->container1 = iterFail->second.container1;
            }
        }
        if (pTrainContainer->container2.strContainerNo.empty())
        {
            if (iterFail->second.container2.strContainerNo.length() > pTrainContainer->container2.strContainerNo.length())
            {
                pTrainContainer->container2 = iterFail->second.container2;
            }
        }
    }

    //4.获取反向未通过校验数据 （正向有内容不再取反向内容）
    if (pTrainContainer->container1.strContainerNo.empty() || pTrainContainer->container2.strContainerNo.empty())
    {
        for (auto iterFail = mapTCFail1_.begin(); iterFail != mapTCFail1_.end(); iterFail++)
        {
            LogDebug << "reverse sourceid:" << iterFail->first << " frameid:" << iterFail->second.container1.strBestImg
                     << " containerNo1:" << iterFail->second.container1.strContainerNo << " frameid:" << iterFail->second.container2.strBestImg
                     << " containerNo2:" << iterFail->second.container2.strContainerNo;
            if (pTrainContainer->container1.strContainerNo.empty())
            {
                if (iterFail->second.container1.strContainerNo.length() > pTrainContainer->container1.strContainerNo.length())
                {
                    pTrainContainer->container1 = iterFail->second.container1;
                }
            }
            if (pTrainContainer->container2.strContainerNo.empty())
            {
                if (iterFail->second.container2.strContainerNo.length() > pTrainContainer->container2.strContainerNo.length())
                {
                    pTrainContainer->container2 = iterFail->second.container2;
                }
            }
        }
    }
}

/**
* 识别成功和失败中选取最优结果
* inParam : std::shared_ptr<TrainContainer> pTrainContainer     选优结果
* outParam: N/A
* return  : N/A
*/
void SelectBestContainerEngine::DealContainer(std::shared_ptr<ProcessData> pProcessData, TransSubData &transSubData, 
                                              std::map<int, TrainContainer> &mapTCSucc, std::map<int, TrainContainer> &mapTCFail, 
                                              std::map<int, Step1Location> &mapStep1Pre, std::map<int, bool> &mapSecondFlag)
{
    /*
    mapStep1Pre先保存第1个箱号上一帧识别结果的大框信息。当识别大框信息和保存上一个识别信息间隔大于画面一半时，
    则说明识别到了第2个箱号，此时mapStep1Pre保存第2个箱号的上一帧的大框信息。

    mapStep1Pre保存的第1个箱号时
        和前帧识别大框间隔小于画面一半时，则属于第1个箱号，否则属于第2个箱号
    mapStep1Pre保存的第2个箱号时
        和前帧识别大框间隔小于画面一半时，则属于第2个箱号，否则属于第1个箱号
    */
    if (mapStep1Pre.find(pProcessData->iDataSource) == mapStep1Pre.end())
    {
        mapStep1Pre[pProcessData->iDataSource] = transSubData.step1Location;
    }
    if (mapSecondFlag.find(pProcessData->iDataSource) == mapSecondFlag.end())
    {
        mapSecondFlag[pProcessData->iDataSource] = false;
    }

    int iCompareValue = 0;
    int iPreCenter = 0;
    int iCurCenter = 0;
    if (setTopContainerSid_.count(pProcessData->iDataSource) != 0)
    {
        //顶部集装箱，使用Y坐标比较
        iCompareValue = pProcessData->iHeight / 2;
        iPreCenter = mapStep1Pre[pProcessData->iDataSource].fLTY;
        iCurCenter = transSubData.step1Location.fLTY;
    }
    else
    {
        //侧部集装箱，使用X坐标比较
        iCompareValue = pProcessData->iWidth / 2;
        iPreCenter = mapStep1Pre[pProcessData->iDataSource].fLTX;
        iCurCenter = transSubData.step1Location.fLTX;
    }

    int iIndex = 0;
    if (!mapSecondFlag[pProcessData->iDataSource])
    {
        iIndex = 1;
        if (abs(iPreCenter - iCurCenter) > iCompareValue)
        {
            iIndex = 2;
            mapSecondFlag[pProcessData->iDataSource] = true;
        }
        mapStep1Pre[pProcessData->iDataSource] = transSubData.step1Location;
    }
    else
    {
        if (abs(iPreCenter - iCurCenter) > iCompareValue)
        {
            iIndex = 1;
        }
        else
        {
            iIndex = 2;
            mapStep1Pre[pProcessData->iDataSource] = transSubData.step1Location;
        }
    }

    //集装箱校验
    bool bChkFlag = true;
    for (auto iter = transSubData.vecTransInfo.begin(); iter != transSubData.vecTransInfo.end(); ++iter)
    {
        bChkFlag &= iter->IsChkFlag;
    }
    if (bChkFlag)
    {
        bChkFlag = VerifyContainerNo(transSubData.strAllValue);
    }
    LogDebug << "datasource:" << pProcessData->iDataSource << " frameid:" << pProcessData->iFrameId
             << " iIndex:" << iIndex << " containerNo:" << transSubData.strAllValue << " chkflag:" << bChkFlag;
    if (!bChkFlag)
    {
        DealFailContianer(transSubData, pProcessData, iIndex, mapTCFail);
    }
    else
    {
        DealSuccContianer(transSubData, pProcessData, iIndex, mapTCSucc);
    }
}


APP_ERROR SelectBestContainerEngine::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<ProcessData> pProcessData = std::static_pointer_cast<ProcessData>(pVoidData0);
        std::shared_ptr<TransData> pTransData = std::static_pointer_cast<TransData>(pProcessData->pVoidData);
        if (pProcessData->bIsEnd)
        {
            mapDataSourceIsEnd_[pProcessData->iDataSource] = pProcessData->bIsEnd;
        }

        for (size_t i = 0; i < pTransData->vecTransSubData.size(); i++)
        {
            TransSubData transSubData = pTransData->vecTransSubData[i];
            //正方向信息
            if (transSubData.iBigClassId == 0)
            {
                DealContainer(pProcessData, transSubData, mapTCSucc0_, mapTCFail0_, mapStep1Pre0_, mapSecondFlag0_);
            }
            //反方向信息
            else if (transSubData.iBigClassId == 1)
            {
                LogDebug << "sourceid:" << pProcessData->iDataSource << " frameid:" << pProcessData->iFrameId << " reverse info";
                DealContainer(pProcessData, transSubData, mapTCSucc1_, mapTCFail1_, mapStep1Pre1_, mapSecondFlag1_);
            }
        }

        bool bAllEnd = true;
        for (auto iter = mapDataSourceIsEnd_.begin(); iter != mapDataSourceIsEnd_.end(); iter++)
        {
            bAllEnd = bAllEnd && iter->second;
        }

        //4. 结束帧时，全部处理掉
        if (bAllEnd)
        {
            std::shared_ptr<TrainContainer> pTrainContainer = std::make_shared<TrainContainer>();
            SelectBestTrainContainer(pTrainContainer);

            outputQueMap_[strPort0_]->push(std::static_pointer_cast<void>(pTrainContainer));
            //push端口1，记录csv
            outputQueMap_[strPort1_]->push(std::static_pointer_cast<void>(pTrainContainer));

            //初始化参数变量
            InitParam();
        }
    }
    return APP_ERR_OK;
}
