#include "smart.h"

#include <spdlog/spdlog.h>
extern "C"
{
#include <maque_type.h>
#include <maque_smart.h>
#include <maque_osd.h>
}

#include "mqtt_helper.hpp"
#include <jsoncons/json.hpp>
#include <vector>
#include <chrono>

#include <motiondetect.hpp>

using namespace std;
using namespace jsoncons;

#define DIV_UP(x, a)    ( ((x) + ((a) - 1) ) / a )
#define SMART_CAP_MIN_SECS  1

long long smartTsLast = 0;
XM_S32 MaQue_JpegEnc_getFrame_callback (XM_VOID *pUserArg, MaQueSmartJpegFrame_s *pstJpegFrame)
{
    ev_module_config_t * mod =  (ev_module_config_t *)pUserArg;
    auto now = chrono::duration_cast<chrono::seconds>(chrono::system_clock::now().time_since_epoch()).count();
    // no two capture within SMART_CAP_MIN_SECS seconds
    if(now - smartTsLast < SMART_CAP_MIN_SECS) {
        return 0;
    }

    smartTsLast = now;
    json js;
    string str = fmt::format("new ai capture: {}, idx = {}, toltal = {}", pstJpegFrame->aClassName, pstJpegFrame->nIndex, pstJpegFrame->nToltalJpeg);
    spdlog::info(str);

    return XM_SUCCESS;
}



void maq_smart_task_entry(ev_module_config_t *pArg)
{
    if(!pArg->module.ai.enabled) {
        spdlog::warn("ai detection is disabled");
        return;
    }
    spdlog::info("starting ai detection task");
    XM_S32 res;
    MaQueStreamChannel_e eStreamChn;
    MaQueSmartParams_s stMaQueSmartParams;
    MaQueSmartTarget_s stMaQueSmartTarget;
    MaQueSmartLowBitRate_s stMaQueSmartLowBitRate;
    XM_S32 bAdvanceIspEnable = 1;
    XM_FLOAT pdThreshold = 0.75;
    XM_FLOAT fdThreshold = 0.63;
    XM_U32 mbSize = 4;
    XM_S32 mbRangeX, mbRangeY;
    XM_S32 i, j, idx;
    MaQueSmartJpegCallbackParam_s stCallback;
    XM_S32 thick = 4;
    XM_S32 x, y;
    XM_S32 width, height;
    XM_U32 cifWidth = 352, tarWidth = 1920;
    XM_U32 cifHeight = 288, tarHeight = 1080;
    XM_U32 osdFlags[MAQUE_STREAM_CHN_NR] = {0, 0};

    // 目前业务都是并行的，此处的delay 仅是确保图像业务已经开启完成。
    sleep(5);

    res = LibXmMaQue_SmartCreate(0);
    if (XM_SUCCESS != res) {
        spdlog::error("LibXmMaQue_SmartCreate() Failed!");
        return;
    }

    stMaQueSmartParams.bEnabled = 1;
    stMaQueSmartParams.pdThreshold = pdThreshold;
    stMaQueSmartParams.fdThreshold = fdThreshold;

    stMaQueSmartParams.bRoiEnabled = 0;
    stMaQueSmartParams.roiThreshold = 256;

    stMaQueSmartParams.roiMbMode = MAQUE_SMART_ROI_MB_MODE_16X16;
    stMaQueSmartParams.imgWidth = 640;
    stMaQueSmartParams.imgHeight = 360;
    stMaQueSmartParams.pMbMap = NULL;

    switch (stMaQueSmartParams.roiMbMode) {
    case MAQUE_SMART_ROI_MB_MODE_4X4:
        mbSize = 4;
        break;
    case MAQUE_SMART_ROI_MB_MODE_8X8:
        mbSize = 8;
        break;
    case MAQUE_SMART_ROI_MB_MODE_16X16:
        mbSize = 16;
        break;
    default:
        return;
    }

    mbRangeX = DIV_UP(stMaQueSmartParams.imgWidth, mbSize);
    mbRangeY = DIV_UP(stMaQueSmartParams.imgHeight, mbSize);
    stMaQueSmartParams.pMbMap = (XM_U8*)malloc(mbRangeX * mbRangeY);
    if (stMaQueSmartParams.pMbMap == NULL) {
        return;
    }

    for (i = 0; i < mbRangeY; i++) {
        idx = i * mbRangeX;
        for (j = 0; j < mbRangeX; j++) {
            if ((j > mbRangeX * i / mbRangeY - mbRangeX / 5) &&
                    (j < mbRangeX * i / mbRangeY + mbRangeX / 5)) {
                stMaQueSmartParams.pMbMap[idx] = 1;
            }
            else {
                stMaQueSmartParams.pMbMap[idx] = 0;
            }
            idx++;
        }
    }

    res = LibXmMaQue_SmartSetParameter(0, &stMaQueSmartParams);
    if (XM_SUCCESS != res) {
        spdlog::error("LibXmMaQue_SmartSetParameter() Failed!");
        free(stMaQueSmartParams.pMbMap);
        stMaQueSmartParams.pMbMap = NULL;
        return;
    }

    if (stMaQueSmartParams.pMbMap) {
        free(stMaQueSmartParams.pMbMap);
        stMaQueSmartParams.pMbMap = NULL;
    }

    eStreamChn = MAQUE_STREAM_CHN_MAIN;
    stMaQueSmartLowBitRate.bLowBitrate = 1;
    stMaQueSmartLowBitRate.bIsoAdaptive = 1;
    res = LibXmMaQue_SmartLowBitRate(0, eStreamChn, &stMaQueSmartLowBitRate);
    if (XM_SUCCESS != res) {
        spdlog::error("LibXmMaQue_SmartLowBitRate() Failed!");
        return;
    }

    res = LibXmMaQue_SmartAdvanceIsp(0, bAdvanceIspEnable);
    if (XM_SUCCESS != res) {
        spdlog::error("LibXmMaQue_SmartAdvanceIsp() Failed!");
        return;
    }

    res = LibXmMaQue_SmartPDThreshold(0, pArg->module.ai.human_thresh);
    if (XM_SUCCESS != res) {
        spdlog::error("LibXmMaQue_SmartPDThreshold() Failed!");
        return;
    }

    res = LibXmMaQue_SmartFDThreshold(0, pArg->module.ai.face_thresh);
    if (XM_SUCCESS != res) {
        spdlog::error("LibXmMaQue_SmartFDThreshold() Failed!");
        return;
    }

    stCallback.eType = MAQUE_SMART_JPEG_ENCODE_LARGE;//MAQUE_SMART_JPEG_ENCODE_CUTOUT;
    stCallback.eClass = MAQUE_SMART_JPEG_CLASS_PD;//MAQUE_SMART_JPEG_CLASS_FD;
    stCallback.stCallback.pCallbackArg = nullptr; //pArg;
    stCallback.stCallback.pCallbackFuncPtr = nullptr; //MaQue_JpegEnc_getFrame_callback;
    LibXmMaQue_SmartRegisterCallback(0, &stCallback);

    extern std::atomic<uint64_t> gMotionCounter;
    extern atomic<uint64_t> frameCntTotal;
    uint64_t frameCntLast = frameCntTotal.load(memory_order_relaxed);
    uint64_t motionCntLast = gMotionCounter.load(memory_order_relaxed);
    const float kMotionPerSecondThresh = 2.5;
    const int kMotionDetectWindowSeconds = 4;
    const float kMsPerMotion = 1000.0/kMotionPerSecondThresh;
    int state = 0;
    int windowSecs = kMotionDetectWindowSeconds;
    ev_region_t last_region = {0};
    sml::sm<md::fsm> fsm{md::fsm{}};
    bool hasHuman = false;
    bool hasMotion = false;
    while(1) {
        // assuming it's 1s
        this_thread::sleep_for(1s);
        // check AI stat
        res = LibXmMaQue_SmartGetTarget(0, &stMaQueSmartTarget);
        if (XM_SUCCESS != res) {
            spdlog::error("failed LibXmMaQue_SmartGetTarget");
        }

        if (stMaQueSmartTarget.targetPDNum > 0) {
            hasHuman = true;
            last_region = {stMaQueSmartTarget.aPDRect[0].s16X1, stMaQueSmartTarget.aPDRect[0].s16Y1, stMaQueSmartTarget.aPDRect[0].s16X2, stMaQueSmartTarget.aPDRect[0].s16Y2};
        }
        if (stMaQueSmartTarget.targetFDNum > 0) {
            hasHuman = true;
            //last_region = {stMaQueSmartTarget.aFDRect[0].s16X1, stMaQueSmartTarget.aFDRect[0].s16Y1, stMaQueSmartTarget.aFDRect[0].s16X2, stMaQueSmartTarget.aFDRect[0].s16Y2};
        }
        if(windowSecs > 0) {
            windowSecs--;
        }
        else {
            auto frameCntTotal_ = frameCntTotal.load(memory_order_relaxed);
            auto motionCnt_ = gMotionCounter.load(memory_order_relaxed);

            float deltaTimeMs = (frameCntTotal_ - frameCntLast) * 1000/pArg->module.sys.fps;
            spdlog::info("deltaTimeMs {}", deltaTimeMs);
            frameCntLast = frameCntTotal_;
            int motionCntThresh = deltaTimeMs / kMsPerMotion;
            int deltaMotionCnt = motionCnt_ - motionCntLast;
            motionCntLast = motionCnt_;
            if(deltaMotionCnt >= motionCntThresh) {
                hasMotion = true;
            }
            if(hasMotion && hasHuman) {
                fsm.process_event(md::mpboth{});
            }
            else if(hasMotion) {
                fsm.process_event(md::motion{});
            }
            else if(hasHuman) {
                fsm.process_event(md::people{});
            }
            else {
                fsm.process_event(md::none{});
            }
            // reset
            windowSecs = kMotionDetectWindowSeconds;
            hasHuman = false;
            hasMotion = false;
            last_region = {0,0,0,0};
        }
    }

    res = LibXmMaQue_SmartDestory(0);
    if (XM_SUCCESS != res) {
        spdlog::error("LibXmMaQue_SmartDestory() Failed!\n");
        return;
    }
}
