/*
 * @Description: 
 * @Version: 1.0
 * @Autor: lishengyin
 * @Date: 2022-03-09 10:58:34
 * @LastEditors: lishengyin
 * @LastEditTime: 2022-07-18 14:01:19
 */
#include "InfineFilter.h"


/**
 * @description: 获取ptr
 * @param {*}
 * @return {*}
 */
std::shared_ptr<InfineFilter> InfineFilter::getPtr(){
    static std::shared_ptr<InfineFilter> m_infineFilter = nullptr;
    if(m_infineFilter == nullptr) m_infineFilter = std::shared_ptr<InfineFilter>(new InfineFilter);
    return m_infineFilter;
}

/**
 * @description: 判定结果
 * @param {*}
 * @return {*}
 */
int8_t InfineFilter::judgementResult(std::shared_ptr<CNStreamInferData>& cnStreamInferData){
    int8_t result = ERR;

    // 过滤小目标
    // MinBBoxFilter(cnStreamInferData);
    
    switch (sensitivity)
    {
    case 0:
        result = OK;
        break;
    case 1:
        result = this->LastOneJudgment(cnStreamInferData);
        break;
    case 2:
        result = (this->IntervalJudgment() == OK && this->LastOneJudgment(cnStreamInferData) == OK ? OK : ERR);
        break;
    case 3:
        result = this->LastOneJudgment(cnStreamInferData);
        if(result == ERR) break;
        result = this->HistoricalJudgment(cnStreamInferData);
        break;
    default:
        break;
    }
    return result;
}   

/**
 * @description: 最小BBox过滤器
 * @return {*}
 */
int8_t InfineFilter::MinBBoxFilter(std::shared_ptr<CNStreamInferData>& cnStreamInferData){
    if(this->bboxSize == 0) return OK;
    vector<std::vector<InferInfo>::iterator> ret;
    for(auto iter = cnStreamInferData->Objects.begin(); iter != cnStreamInferData->Objects.end(); iter++){
        double t = (iter->BBox.h * (double)cnStreamInferData->width) * (iter->BBox.w * (double)1920);
        if(t < (double)bboxSize) ret.push_back(iter);
    }
    for(auto iter = ret.begin(); iter != ret.end(); iter++){
        cnStreamInferData->Objects.erase(*iter);
    }
    return OK;
}

/**
 * @description: 获取是否相似
 * @param {InferInfo} infer1
 * @param {InferInfo} infer2
 * @return {*}
 */
int8_t InfineFilter::CalculateSimilarity(InferInfo& infer1, InferInfo& infer2){
    // Label
    if(infer1.Label != infer2.Label) return ERR;
    // Source
    // BBOX
    double difference = fabs(infer1.BBox.x  - infer2.BBox.x);
    if(difference > 10) return ERR;
    difference = fabs(infer1.BBox.y  - infer2.BBox.y );
    if(difference > 10) return ERR;
    difference = fabs(infer1.BBox.w  - infer2.BBox.w );
    if(difference > 10) return ERR;
    difference = fabs(infer1.BBox.h  - infer2.BBox.h );
    if(difference > 10) return ERR;
    return OK;
}

/**
 * @description: 设置灵敏度
 * @param {int} Sensitivity
 * @return {*}
 */
int8_t InfineFilter::setSensitivity(int Sensitivity){
    if(Sensitivity < 0 || Sensitivity > 3) return ERR;
    this->sensitivity = Sensitivity;
    return OK;
}

/**
 * @description: 设置时间间隔
 * @param {int} Interval
 * @return {*}
 */
int8_t InfineFilter::setInterval(int Interval){
    if(Interval < 0 || Interval > 60000) return ERR;
    this->interval = Interval;
    return OK;
}

/**
 * @description: 设置超时时间
 * @param {int} timeOut
 * @return {*}
 */
int8_t InfineFilter::setTimeOut(int timeOut){
    if(timeOut < 0) return ERR;
    this->TimeOut = timeOut;
    return OK;
}

/**
 * @description: 与上一次的结果判断
 * @param {*}
 * @return {*}
 */
int8_t InfineFilter::LastOneJudgment(std::shared_ptr<CNStreamInferData>& cnStreamInferData){
    static std::shared_ptr<CNStreamInferData> old = nullptr;
    if(old == nullptr) {
        old = cnStreamInferData;
        return OK;
    }
    // 判断是否为同一个视频内
    if(old->videoPath == cnStreamInferData->videoPath){
        // 分析结果数量
        int num = 0;
        if(old->Objects.size() == cnStreamInferData->Objects.size()){
            for(auto iter1 = old->Objects.begin(); iter1 != old->Objects.end(); iter1++){
                for(auto iter2 = cnStreamInferData->Objects.begin(); iter2 != cnStreamInferData->Objects.end(); iter2++){
                    if(CalculateSimilarity(*iter1, *iter2) == OK) num++;
                }
            }
        }
        if(num == old->Objects.size()) {
            return ERR;
        }
    }
    old = cnStreamInferData;
    return OK;
}

/**
 * @description: 时间间隔判定
 * @param {*}
 * @return {*}
 */
int8_t InfineFilter::IntervalJudgment(){
    static std::shared_ptr<Ticker> ticker = nullptr;
    if(ticker == nullptr) {
        ticker = std::make_shared<Ticker>();
        return OK;
    }
    if(ticker->elapsedTime() >= config::getPtr()->InferInterval){
        ticker->resetTime();
        return OK;
    }
    return ERR;
}

/**
 * @description: 历史判定
 * @param {*}
 * @return {*}
 */
int8_t InfineFilter::HistoricalJudgment(std::shared_ptr<CNStreamInferData>& cnStreamInferData){
    // 历史检查
    this->HistoricalCheck();
    auto task = [&](){
        HistoryData historyData;
        historyData.ticker = std::make_shared<Ticker>();
        historyData.CnStreamInferData = std::make_shared<CNStreamInferData>();
        historyData.CnStreamInferData->StreamName = cnStreamInferData->StreamName;
        historyData.CnStreamInferData->FrameCount = cnStreamInferData->FrameCount;
        historyData.CnStreamInferData->width = cnStreamInferData->width;
        historyData.CnStreamInferData->height = cnStreamInferData->height;
        historyData.CnStreamInferData->Objects = cnStreamInferData->Objects;
        historyData.CnStreamInferData->videoPath = cnStreamInferData->videoPath;
        this->HistoryDatas.push_back(historyData);
    };
    if(HistoryDatas.empty()){
        task();
        return OK;
    }
    for(auto iter = HistoryDatas.begin(); iter != HistoryDatas.end(); iter++){
        int num = 0;
        if(iter->CnStreamInferData->Objects.size() == cnStreamInferData->Objects.size()){
            for(auto iter1 = iter->CnStreamInferData->Objects.begin(); iter1 != iter->CnStreamInferData->Objects.end(); iter1++){
                for(auto iter2 = cnStreamInferData->Objects.begin(); iter2 != cnStreamInferData->Objects.end(); iter2++){
                    if(CalculateSimilarity(*iter1, *iter2) == OK) num++;
                }
            }
        }
        if(num == iter->CnStreamInferData->Objects.size()) {
            iter->ticker->resetTime();
            return ERR;
        }
    }
    task();
    return OK;
}

/**
 * @description: 历史检查
 * @param {*}
 * @return {*}
 */
void InfineFilter::HistoricalCheck(){
    if(sensitivity != 3) return;
    vector<vector<HistoryData>::iterator> ret;
    for(auto iter = this->HistoryDatas.begin(); iter != this->HistoryDatas.end(); iter++){
        if(iter->ticker != nullptr && iter->ticker->elapsedTime() > TimeOut){
            ret.push_back(iter);
      }
    }
    if(ret.empty()) return;
    for(auto iter = ret.begin(); iter != ret.end(); iter++){
        this->HistoryDatas.erase(*iter);
    }
}

