#include "MergerAllEngine.h"

using namespace ai_matrix;

namespace
{
    std::map<std::string, std::string> mapLoad = {
            {"C62", "60"},
            {"C62B", "61"},
            {"C63", "61"},
            {"C64", "61"},
            {"C65", "60"},
            {"C70", "70"},
            {"C80", "80"},
            {"KM70", "70"},
            {"KM81", "81"},
            {"KM98", "98"}
    };
}

MergerAllEngine::MergerAllEngine() {}

MergerAllEngine::~MergerAllEngine() {}

APP_ERROR MergerAllEngine::Init()
{
    strPort0_ = engineName_ + "_" + std::to_string(engineId_) + "_0";   //接受车号属性
    strPort1_ = engineName_ + "_" + std::to_string(engineId_) + "_1";   //接受定检期
    strPort2_ = engineName_ + "_" + std::to_string(engineId_) + "_2";   //接受集装箱
    strPort3_ = engineName_ + "_" + std::to_string(engineId_) + "_3";   //接受车厢范围

    iPushDirection_ = MyYaml::GetIns()->GetIntValue("gc_push_direction");

    mapPopPort_[strPort0_] = false;
    mapPopPort_[strPort1_] = false;
    mapPopPort_[strPort2_] = false;
    mapPopPort_[strPort3_] = true;

    //获取在用摄像头识别的目标(目标分为3大类，车号，定检期，集装箱  哪个识别监听哪个端口的数据)
    std::map<int, ai_matrix::DataSourceConfig> mapUseDataSouceCfg = MyYaml::GetIns()->GetUseDataSourceConfig();
    std::string delimiter(",");
    for (auto iter = mapUseDataSouceCfg.begin(); iter!=mapUseDataSouceCfg.end(); iter++)
    {
        std::vector<std::string> vecSplit = MyUtils::getins()->split(iter->second.strTarget, delimiter);
        for (auto iter = vecSplit.begin(); iter != vecSplit.end(); iter++)
        {
            if (*iter == "NUM" && !mapPopPort_[strPort0_])
            {
                mapPopPort_[strPort0_] = true;
            }
            else if (*iter == "CHKDATE" && !mapPopPort_[strPort1_])
            {
                mapPopPort_[strPort1_] = true;
            }
            else if ((*iter == "CONTAINER" || *iter == "CONTAINER_T") && !mapPopPort_[strPort2_])
            {
                mapPopPort_[strPort2_] = true;
            }
        }
    }

    for (auto iter = mapPopPort_.begin(); iter != mapPopPort_.end(); iter++)
    {
        if (iter->second)
        {
            iPopPortCnt_++;
        }
    }
    LogDebug << "iPopPortCnt_:" << iPopPortCnt_;

    InitParam();

    bUploadFlag_ = (MyYaml::GetIns()->GetIntValue("gc_http_open") == 1 || MyYaml::GetIns()->GetIntValue("gc_mysql_open") == 1);

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

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

/**
* 初始化参数信息
* inParam : N/A
* outParam: N/A
* return  : N/A
*/
void MergerAllEngine::InitParam()
{
    iTrainIndex_ = 1;
    bHeadFrontFlag_ = false;
}

/**
 * push结果
 * inParam : std::shared_ptr<Train> pTrain  合并后车厢信息
 * outParam: N/A
 * return  : N/A
 */
void MergerAllEngine::PushData(std::shared_ptr<Train> pTrain)
{
    LogInfo << "\n ---所有信息合并结果--- \n"
            << "日期: " << pTrain->strTrainDate << "\n"
            << "车次: " << pTrain->strTrainName << "\n"
            << "车厢序号: " << pTrain->iCarXH << "\n"
            << "车型Id: " << pTrain->trainNum.iTrainTypeId << "\n"
            << "车型: " << pTrain->trainNum.strTrainType << "\n"
            << "车号: " << pTrain->trainNum.strTrainNum << "\n"
            << "载重: " << pTrain->trainPro.strLoad << "\n"
            << "自重: " << pTrain->trainPro.strSelf << "\n"
            << "容积: " << pTrain->trainPro.strVolume << "\n"
            << "换长: " << pTrain->trainPro.strChange << "\n"
            << "容量计表: " << pTrain->trainPro.strVolumeSurface << "\n"
            << "编号图片: " << pTrain->trainNum.strBestImg << "\n"
            << "属性图片: " << pTrain->trainPro.strBestImg << "\n"
            << "开始帧号: " << pTrain->iStartFrameId << "\n"
            << "结束帧号: " << pTrain->iEndFrameId << "\n"
            << "时间戳: " << pTrain->i64TimeStamp << "\n"
            << "定检期_段修: " << pTrain->chkDate.strChkDate1 << "\n"
            << "定检期_厂修: " << pTrain->chkDate.strChkDate2 << "\n"
            << "定检期图片: " << pTrain->chkDate.strBestImg << "\n"
            << "定检期时间戳: " << pTrain->chkDate.i64TimeStamp << "\n"
            << "集装箱1: " << pTrain->container1.strContainerNo << "\n"
            << "集装箱1图片: " << pTrain->container1.strBestImg << "\n"
            << "集装箱1时间戳: " << pTrain->container1.i64TimeStamp << "\n"
            << "集装箱2: " << pTrain->container2.strContainerNo << "\n"
            << "集装箱2图片: " << pTrain->container2.strBestImg << "\n"
            << "集装箱2时间戳: " << pTrain->container2.i64TimeStamp << "\n"
            << "车厢开始时间: " << MyUtils::getins()->Stamp2Time(pTrain->i64StartTimeStamp, true) << "\n"
            << "车厢结束时间: " << MyUtils::getins()->Stamp2Time(pTrain->i64EndTimeStamp, true) << "\n"
            << " ---所有信息合并结果 END--- ";
    if (pTrain->bIsEnd)
    {
        LogInfo << " ===============所有信息合并结束=============== ";
    }

    //3:敞车,6:平车 有集装箱; 其他车型无集装箱，有误识别时剔除
    if (pTrain->trainNum.iTrainTypeId != 3 && pTrain->trainNum.iTrainTypeId != 6)
    {
        pTrain->container1.strContainerNo = "";
        pTrain->container1.strBestImg = "";
        pTrain->container2.strContainerNo = "";
        pTrain->container2.strBestImg = "";
    }

    pTrain->bMergerFlag = true;
    std::shared_ptr<Train> pTrainToCsv = std::make_shared<Train>();
    *pTrainToCsv = *pTrain;

    if (bUploadFlag_)
    {
        queTrain_.push(pTrain);
        if (pTrain->iDirection == iPushDirection_ || iPushDirection_ == DIRECTION_UNKNOWN)
        {
            while (!queTrain_.empty())
            {
                std::shared_ptr<Train> pTrainToHttp = queTrain_.front();
                queTrain_.pop();
                outputQueMap_[strPort0_]->push(std::static_pointer_cast<void>(pTrainToHttp));
            }
        }

        if (pTrain->bIsEnd)
        {
            while (!queTrain_.empty())
            {
                std::shared_ptr<Train> pTrainTemp = queTrain_.front();
                queTrain_.pop();
                LogDebug << "carNO:" << pTrainTemp->iCarXH << " direction:" << pTrainTemp->iDirection << " not push to httpSrv";
            }
        }
    }

    //push端口1，csv保存合并结果
    outputQueMap_[strPort1_]->push(std::static_pointer_cast<void>(pTrainToCsv));
}

/**
 * 有数据队列的个数
 * inParam : N/A
 * outParam: N/A
 * return  : N/A
 */
int MergerAllEngine::QueueHaveDataCount()
{
    int iHaveDataCnt = 0;
    iHaveDataCnt = queuePort0_.empty() ? iHaveDataCnt : iHaveDataCnt + 1;
    iHaveDataCnt = queuePort1_.empty() ? iHaveDataCnt : iHaveDataCnt + 1;
    iHaveDataCnt = queuePort2_.empty() ? iHaveDataCnt : iHaveDataCnt + 1;
    iHaveDataCnt = queuePort3_.empty() ? iHaveDataCnt : iHaveDataCnt + 1;
    LogDebug << "queuePort0_size:" << queuePort0_.size() << " queuePort1_size:" << queuePort1_.size()
             << " queuePort2_size:" << queuePort2_.size() << " queuePort3_size:" << queuePort3_.size();
    return iHaveDataCnt;
}

/*
注：以下异常场景也要保证 (IsEnd-结束标识) push到该Engine中。否则数据无法推送到web端。
    1. 摄像头异常
    2. 未识别到内容    
*/
APP_ERROR MergerAllEngine::Process()
{
    int iRet = APP_ERR_OK;

    while (!isStop_)
    {
        bool bPopFlag = false;
        // pop端口0，车号
        if (mapPopPort_[strPort0_])
        {
            std::shared_ptr<void> pVoidData0 = nullptr;
            inputQueMap_[strPort0_]->pop(pVoidData0);
            if (nullptr != pVoidData0)
            {
                LogInfo<<"pop0 data";
                queuePort0_.push(pVoidData0);
                bPopFlag = true;
            }
        }

        // pop端口1，定检期
        if (mapPopPort_[strPort1_])
        {
            std::shared_ptr<void> pVoidData1 = nullptr;
            inputQueMap_[strPort1_]->pop(pVoidData1);
            if (nullptr != pVoidData1)
            {
                LogDebug<<"pop1 data";
                queuePort1_.push(pVoidData1);
                bPopFlag = true;
            }
        }

        // pop端口2，集装箱
        if (mapPopPort_[strPort2_])
        {
            std::shared_ptr<void> pVoidData2 = nullptr;
            inputQueMap_[strPort2_]->pop(pVoidData2);
            if(nullptr != pVoidData2)
            {
                LogDebug<<"pop2 data";
                queuePort2_.push(pVoidData2);
                bPopFlag = true;
            }
        }

        //pop端口3，车厢范围
        if (mapPopPort_[strPort3_])
        {
            std::shared_ptr<void> pVoidData3 = nullptr;
            inputQueMap_[strPort3_]->pop(pVoidData3);
            if (nullptr != pVoidData3)
            {
                LogDebug << "pop3 data";
                queuePort3_.push(pVoidData3);
                bPopFlag = true;
            }
        }

        if (!bPopFlag)
        {
            usleep(1000); // 1ms
            continue;
        }

        int iHaveDataCnt = QueueHaveDataCount();
        //识别目标信息个数和获取信息个数相等，则合并信息推送web
        if (iHaveDataCnt == iPopPortCnt_)
        {
            std::shared_ptr<Train> pTrain = nullptr;
            std::shared_ptr<ChkDate> pChkDate = nullptr;
            std::shared_ptr<TrainContainer> pTrainContainer = nullptr;
            std::shared_ptr<TrainRange> pTrainRange = nullptr;
            if (!queuePort0_.empty())
            {
                pTrain = std::static_pointer_cast<Train>(queuePort0_.front());
                queuePort0_.pop();
            }
            if (!queuePort1_.empty())
            {
                pChkDate = std::static_pointer_cast<ChkDate>(queuePort1_.front());
                queuePort1_.pop();
            }
            if (!queuePort2_.empty())
            {
                pTrainContainer = std::static_pointer_cast<TrainContainer>(queuePort2_.front());
                queuePort2_.pop();
            }
            if (!queuePort3_.empty())
            {
                pTrainRange = std::static_pointer_cast<TrainRange>(queuePort3_.front());
                queuePort3_.pop();
            }

            //信息合并处理
            if (nullptr != pTrain)
            {
                if (nullptr != pTrainRange)
                {
                    pTrain->iStartFrameId = pTrainRange->iStartFrameId;
                    pTrain->i64StartTimeStamp = pTrainRange->i64StartTimeStamp;
                    pTrain->iEndFrameId = pTrainRange->iEndFrameId;
                    pTrain->i64EndTimeStamp = pTrainRange->i64EndTimeStamp;
                    pTrain->bIsEnd = pTrainRange->bIsEnd;
                }
                if (nullptr != pChkDate)
                {
                    pTrain->chkDate = *pChkDate;
                }
                if (nullptr != pTrainContainer)
                {
                    pTrain->container1 = pTrainContainer->container1;
                    pTrain->container2 = pTrainContainer->container2;
                }

                //车头在前为第0节; 车头在后为最后一节+1
                if(pTrain->trainNum.iTrainTypeId == 0 && iTrainIndex_ == 1)
                {
                    iTrainIndex_ = 0;
                    bHeadFrontFlag_ = true;
                }
                pTrain->iCarXH = iTrainIndex_++;

                /*
                特殊处理：针对车头在后的且当前为最后一节，且未识别到车型，则默认设置为车头。
                防止车尾间隔误识别，多切分车型，且web端无法过滤的问题。
                */
                if (!bHeadFrontFlag_
                    && pTrain->bIsEnd
                    && pTrain->trainNum.iTrainTypeId == -1)
                {
                    LogDebug << "cometime:" << pTrain->strTrainDate << " " << pTrain->strTrainName << " iCarXH:" << pTrain->iCarXH
                             << " num:" << pTrain->trainNum.strTrainNum;
                    pTrain->trainNum.iTrainTypeId = 0;
                }

                // 修正载重
                for (std::map<std::string, std::string>::iterator it =
                        mapLoad.begin();
                     it != mapLoad.end(); ++it) {

                    if (pTrain->trainNum.strTrainType.find(it->first) !=
                        std::string::npos) {
                        pTrain->trainPro.strLoad = it->second;
                    }
                }

                // if (pTrain->trainPro.strLoad.size() > 2 && pTrain->trainPro.strLoad[0] != '1') pTrain->trainPro.strLoad = "";

                PushData(pTrain);

                //最后一节处理后，初始化参数
                if (pTrain->bIsEnd)
                {
                    InitParam();
                }
            }
        }
    }
    return APP_ERR_OK;
}
