#include "faceLiveness.h"
#include "ui_faceLiveness.h"

#include "CAppInfo.h"

#include <QDesktopWidget>
#include <QFile>
#include <QFileInfo>
#include "awMsgBox.h"
#include "logproc.h"
#include "CCommonTools.h"
#include "CFaceRecProc.h"

faceLiveness::faceLiveness(FACE_LIVENESS_TYPE livenessType, QWidget *parent) :
    QWidget(parent),
    ui(new Ui::faceLiveness), m_livenessType(livenessType)
{
    ui->setupUi(this);

    setStyleSheet(g_appInfoPtr->m_sQssStr);

    initUI();

    qRegisterMetaType<CBaseResponsePackage>("CBaseResponsePackage");
    qRegisterMetaType<CClientExamProcessUploadSign>("CClientExamProcessUploadSign");
    qRegisterMetaType<CUploadFileToAliyun>("CUploadFileToAliyun");
    connect(g_httpBllPtr.get(), &CHttpBll::sgnClientExamProcessUploadSign, this, &faceLiveness::onClientExamProcessUploadSign);
    connect(g_httpBllPtr.get(), &CHttpBll::sgnSaveFaceLiveVerifyResult, this, &faceLiveness::onSaveFaceLiveVerifyResult);
    connect(g_httpBllPtr.get(), &CHttpBll::sgnSaveFaceCompareResult, this, &faceLiveness::onSaveFaceCompareResult);
    connect(g_httpBllPtr.get(), &CHttpBll::sgnUploadFileToAliyun, this, &faceLiveness::onUploadFileToAliyun);

    if(m_livenessType == FACE_LIVENESS_TYPE::flt_inprogress)
    {
        ui->label_fl_time->setVisible(true);
        ui->btn_fl_close->setVisible(false);
    }
    else
    {
        ui->label_fl_time->setVisible(false);
        ui->btn_fl_close->setVisible(true);
    }

    m_nMaxSeconds = g_appInfoPtr->m_oExamInfo.nAllActionDuration;

    QString sFileName = g_appInfoPtr->m_sStudentPhotoPath.right(g_appInfoPtr->m_sStudentPhotoPath.length() - g_appInfoPtr->m_sStudentPhotoPath.lastIndexOf("/") - 1);
    sFileName = g_appInfoPtr->m_sCacheFileDir + sFileName;
    if(!QFile::exists(sFileName))
    {
        CHttpRequestPackage hrp;
        hrp.sUri = g_appInfoPtr->m_sStudentPhotoPath;
        hrp.sCommonStr = sFileName;
        hrp.nRequestType = RequestType::rtDownLoadFile;
        hrp.nRetryCount = 3;
        g_httpBllPtr->downLoad(hrp);
    }

    m_pVideoTimer = std::make_shared<QTimer>();
    m_pVideoTimer->setInterval(200);
    connect(m_pVideoTimer.get(), &QTimer::timeout, this, [&](){
        cv::Mat frame;
        {
            std::scoped_lock sl(m_imageMutex);
            frame = m_nCurImage;
        }

        QImage img = CCommonTools::Mat2QImage(frame);

        ui->widget_fc_camera->setAutoFillBackground(true);
        QPalette palette;
        palette.setBrush(QPalette::Window, QBrush(img.scaled(ui->widget_fc_camera->width(), ui->widget_fc_camera->height())));
        ui->widget_fc_camera->setPalette(palette);

        if(m_bStartCompare)
        {
            std::scoped_lock sl(m_imgMutex);
            m_imgList.push_back(frame);
        }
    });

    g_clientVideoProcPtr->startTest(this);
    m_pVideoTimer->start();

    //生成动作列表
    QStringList sActionList = g_appInfoPtr->m_oExamInfo.sActionOptions.split(",");
    if(sActionList.count() != g_appInfoPtr->m_oExamInfo.nActionNum)
    {
        ShowMsg(QString::fromLocal8Bit("初始化活体动作失败"), this, MSG_ICON_TYPE::mit_error);
        emit faceLivenessFaild();
        return;
    }

    LivenessVerifyInfo lvif;
    lvif.sActionType = ACTION_TYPE::AT_FACE_DETECT;
    lvif.nActionLeftSceonds = m_nFaceDetectDuration;
    m_livenessList.push_back(lvif);
	m_currentVerifyInfo = lvif;
    if(g_appInfoPtr->m_oExamInfo.sActionOrder == "FIXED")
    {		
        for(QString sAction : sActionList)
        {
            LivenessVerifyInfo lvi;
            lvi.sActionType = sAction;
            lvi.nActionLeftSceonds = g_appInfoPtr->m_oExamInfo.nActionDuration;
            m_livenessList.push_back(lvi);
        }
    }
    else
    {
        QList<int> list;
        CCommonTools::genRandomNumber(list, g_appInfoPtr->m_oExamInfo.nActionNum);
        for(int i = 0; i < g_appInfoPtr->m_oExamInfo.nActionNum; i++)
        {
            LivenessVerifyInfo lvi;
            lvi.sActionType = sActionList[list[i]-1];
            lvi.nActionLeftSceonds = g_appInfoPtr->m_oExamInfo.nActionDuration;
            m_livenessList.push_back(lvi);
        }
    }
    m_nCurIndex = 0;

    m_ActionTimer = std::make_shared<QTimer>();
    m_ActionTimer->setInterval(1000);
    connect(m_ActionTimer.get(), &QTimer::timeout, this, &faceLiveness::actionTimer);


    m_countdownTimer = std::make_shared<QTimer>();
    m_countdownTimer->setInterval(1000);
    connect(m_countdownTimer.get(), &QTimer::timeout, this, &faceLiveness::countdownTimer);
    m_countdownTimer->start();

    m_bIsRun = true;
    m_thread = std::thread(std::bind(&faceLiveness::threadProc, this));

    m_initTimer = std::make_shared<QTimer>();
    m_initTimer->setInterval(100);
    connect(m_initTimer.get(), &QTimer::timeout, this, [&](){
		m_initTimer->stop();
        if(g_faceRecProcPtr == nullptr)
        {
            g_faceRecProcPtr = std::make_shared<CFaceRecProc>();            
        }

        if (!g_appInfoPtr->m_sStudentPhotoPath.isEmpty())
        {
//                if(!g_faceRecProcPtr->hasBaseImage())
            {
                if(!setBaseImage())
                {
                    return;
                }
            }
        }
        else
        {
            ShowMsg(QString::fromLocal8Bit("当前考试未底照"), this, MSG_ICON_TYPE::mit_error);
            return;
        }

//        if(!g_faceRecProcPtr->hasBaseImage())
//        {
//            if(!setBaseImage())
//            {
//                return;
//            }
//        }

        m_bStartCompare = true;
        m_fMaxYaw = 0;
        m_fMinYaw = 0;
        m_sLivenessStatus = STATUS_TYPE::ST_SUCCESS;

        {
            std::scoped_lock sl(m_livenessListMutex);
            m_currentVerifyInfo = m_livenessList[m_nCurIndex];
        }
        initAcionIcon();
        m_ActionTimer->start();
    });
    m_initTimer->start();
}

faceLiveness::~faceLiveness()
{
    m_pVideoTimer->stop();
    m_ActionTimer->stop();

    g_clientVideoProcPtr->stopTest();

    m_bIsRun = false;
    m_thread.join();
    awMsgBox::clear(this);
    delete ui;
}

void faceLiveness::onRenderVideoFrame(const char* userId, TRTCVideoStreamType streamType, TRTCVideoFrame* frame)
{
    if(g_clientVideoProcPtr->isCameraTest())
    {
        __int64 nServerTime = g_appInfoPtr->serverMTime();
        if(nServerTime - m_lastFaceTime >= 100)
        {
            m_lastFaceTime = nServerTime;
            cv::Mat matImg;
            cv::cvtColor(cv::Mat(frame->height, frame->width, CV_8UC4, frame->data), matImg, CV_RGBA2RGB);
            if(matImg.empty())
            {
                qDebug()<<"widgetCameraTest frame is empty";
                return;
            }

            std::scoped_lock sl(m_imageMutex);
            m_nCurImage = matImg;
        }
    }
}

void faceLiveness::initUI()
{
    QDesktopWidget *dekwiget = QApplication::desktop();
    setGeometry(0, 0, dekwiget->width(), dekwiget->height());
    ui->widget_mask->setGeometry(0, 0, dekwiget->width(), dekwiget->height());
    ui->widget_fc_BG->setGeometry((width() - g_appInfoPtr->m_fRate*800)/2, (height() - g_appInfoPtr->m_fRate*536)/2,
                                   g_appInfoPtr->m_fRate*800, g_appInfoPtr->m_fRate*536);
    ui->label_fl_title->adjustSize();
    ui->label_fl_title->setGeometry(g_appInfoPtr->m_fRate*20, g_appInfoPtr->m_fRate*20,
                                    ui->label_fl_title->width(), ui->label_fl_title->height());
    ui->label_fl_time->setGeometry(ui->label_fl_title->x() + ui->label_fl_title->width(), ui->label_fl_title->y(),
                                   g_appInfoPtr->m_fRate*200, ui->label_fl_title->height());
    ui->btn_fl_close->setGeometry(ui->widget_fc_BG->width() - g_appInfoPtr->m_fRate*(20 + 100),
                                  g_appInfoPtr->m_fRate*16, g_appInfoPtr->m_fRate*100, g_appInfoPtr->m_fRate*36);

    ui->widget_fc_camera->setGeometry(g_appInfoPtr->m_fRate*30, g_appInfoPtr->m_fRate*75,
                                     g_appInfoPtr->m_fRate*740, g_appInfoPtr->m_fRate*320);
    ui->btn_fl_startVerify->setGeometry((ui->widget_fc_camera->width() - g_appInfoPtr->m_fRate*160)/2,
		ui->widget_fc_camera->y() + ui->widget_fc_camera->height() + g_appInfoPtr->m_fRate*30,
                                    g_appInfoPtr->m_fRate*160, g_appInfoPtr->m_fRate*40);
    ui->label_fl_tips->adjustSize();
    ui->label_fl_tips->setGeometry((ui->widget_fc_BG->width() - ui->label_fl_tips->width())/2, ui->btn_fl_startVerify->y() + ui->btn_fl_startVerify->height() + g_appInfoPtr->m_fRate*20,
                                   ui->label_fl_tips->width(), ui->label_fl_tips ->height());

    ui->label_fl_actionMoive->setGeometry(0, 0, g_appInfoPtr->m_fRate*64, g_appInfoPtr->m_fRate*64);
    ui->label_fl_actionTips->adjustSize();
    int nActionWidth = ui->label_fl_actionMoive->width() + g_appInfoPtr->m_fRate*20 + ui->label_fl_actionTips->width() +
            g_appInfoPtr->m_fRate*20 + g_appInfoPtr->m_fRate*44;
    ui->widget_fl_action->setGeometry((ui->widget_fc_camera->width() - nActionWidth)/2, g_appInfoPtr->m_fRate*10,
                                      nActionWidth, g_appInfoPtr->m_fRate*64) ;
    ui->label_fl_actionTips->setGeometry(ui->label_fl_actionMoive->x() + ui->label_fl_actionMoive->width() + g_appInfoPtr->m_fRate*20,
                                         (ui->widget_fl_action->height() - ui->label_fl_actionTips->height())/2,
                                         ui->label_fl_actionTips->width(), ui->label_fl_actionTips->height());
    ui->btn_fl_time->setGeometry(ui->label_fl_actionTips->x() + ui->label_fl_actionTips->width() + g_appInfoPtr->m_fRate*20,
                                 (ui->widget_fl_action->height() - g_appInfoPtr->m_fRate*40)/2, g_appInfoPtr->m_fRate*40, g_appInfoPtr->m_fRate*40);

    ui->widget_fl_hint->setVisible(false);
    ui->widget_fl_hint->setGeometry((width() - g_appInfoPtr->m_fRate*400)/2, (height() - g_appInfoPtr->m_fRate*180)/2,
                                    g_appInfoPtr->m_fRate*400, g_appInfoPtr->m_fRate*180);
    ui->label_fl_icon->setGeometry(g_appInfoPtr->m_fRate*75, g_appInfoPtr->m_fRate*70, g_appInfoPtr->m_fRate*40, g_appInfoPtr->m_fRate*40);
    ui->label_fl_hint->adjustSize();
    ui->label_fl_hint->setGeometry(ui->label_fl_icon->x() + ui->label_fl_icon->width() + g_appInfoPtr->m_fRate*20,
                                   ui->label_fl_icon->y() + (ui->label_fl_icon->height() - ui->label_fl_hint->height())/2,
                                   ui->label_fl_hint->width(), ui->label_fl_hint->height());

    ui->widget_fl_action->setVisible(false);

    ui->btn_fl_startVerify->setVisible(false);
    ui->label_fl_tips->setVisible(false);
    ui->widget_fc_camera->setFixedHeight(g_appInfoPtr->m_fRate*431);
    ui->widget_fl_action->setVisible(true);
}

void faceLiveness::threadProc()
{
	try
	{
		while (m_bIsRun)
		{
			if (m_bStartCompare)
			{
				if (m_imgList.begin() != m_imgList.end())
				{
					myDebug() << "m_imgList:" << m_imgList.size();

					cv::Mat matImage;
					{
						std::scoped_lock lock(m_imgMutex);
						if (m_imgList.begin() != m_imgList.end())
						{
							matImage = (*m_imgList.begin()).clone();
							m_imgList.erase(m_imgList.begin());
						}
						else
						{
							continue;
						}
					}

					if (matImage.empty())
					{
						myDebug() << "matImage.empty";
						continue;
					}

					if (!m_currentVerifyInfo.bIsVerify)
					{
						verifyAction(matImage);
					}
				}
			}
			else
			{
				Sleep(100);
			}
		}
	}
	catch (const std::exception &e)
	{
		myDebug()<<QString::fromLocal8Bit("人脸比对失败,%1").arg(e.what());		
	}
}

void faceLiveness::verifyAction(cv::Mat matImage)
{
	try
	{
		bool bHasStatus = false;
		int nFaceCount = 0;
		float fScore = 0;

		if (m_currentVerifyInfo.bIsVerify)
		{
			return;
		}

		m_currentVerifyInfo.matImage = matImage;

		if (m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_FACE_DETECT)
		{
			SeetaRect rt;
			if (!g_faceRecProcPtr->compareWithBase(matImage, nFaceCount, fScore, rt))
			{
				m_currentVerifyInfo.sErrorMsg = g_faceRecProcPtr->errorMsg();
				myServerLog() << g_faceRecProcPtr->errorMsg();
				return;
			}

			m_currentVerifyInfo.nFaceCount = nFaceCount;
			m_currentVerifyInfo.fSimilarity = fScore;

			if (nFaceCount != 1)
			{
				myServerLog() << QString::fromLocal8Bit("活体检测人脸数量异常,") << nFaceCount;
				m_currentVerifyInfo.sErrorMsg = QString::fromLocal8Bit("活体检测人脸数量异常,").arg(nFaceCount);
				return;
			}

            if (fScore * 100 > g_appInfoPtr->m_oExamInfo.nFaceThreshold)
			{
				bool bRealness = false;
				if (!g_faceRecProcPtr->faceRealness(matImage, bRealness))
				{
					myServerLog() << g_faceRecProcPtr->errorMsg();
					m_currentVerifyInfo.sErrorMsg = g_faceRecProcPtr->errorMsg();
					return;
				}
				//验证成功
				if (bRealness)
				{
					m_currentVerifyInfo.nEndTime = g_appInfoPtr->serverMTime();
					m_currentVerifyInfo.nRealness = 1;
					m_currentVerifyInfo.bPass = true;
					m_currentVerifyInfo.bIsVerify = true;

					myServerLog() << m_currentVerifyInfo.sActionType << ":" << fScore;

					return;
				}
				else
				{
					m_currentVerifyInfo.nRealness = 0;
				}
			}
		}
		if (m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_BLINK ||
			m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_NOD)
		{
			int nFaceStatus = 0;
			if (m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_BLINK)
			{
				nFaceStatus = SL_EYE_CLOSE;
			}
			else
			{
				nFaceStatus = SL_HEAD_DOWN;
			}

			if (!g_faceRecProcPtr->getFaceAttribute(matImage, nFaceStatus, bHasStatus, nFaceCount))
			{
				myServerLog() << g_faceRecProcPtr->errorMsg();
				m_currentVerifyInfo.sErrorMsg = g_faceRecProcPtr->errorMsg();
				return;
			}

			m_currentVerifyInfo.nFaceCount = nFaceCount;

			if (nFaceCount != 1)
			{
				myServerLog() << QString::fromLocal8Bit("活体检测人脸数量异常,") << nFaceCount;
				m_currentVerifyInfo.sErrorMsg = QString::fromLocal8Bit("活体检测人脸数量异常,").arg(nFaceCount);
				return;
			}

			if (bHasStatus)
			{
				//验证成功
				m_currentVerifyInfo.nEndTime = g_appInfoPtr->serverMTime();
				m_currentVerifyInfo.nRealness = 1;
				m_currentVerifyInfo.bPass = true;
				m_currentVerifyInfo.bIsVerify = true;
			}
		}
		else if (m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_SHAKE)
		{
			float fYaw = 0;
			float fPitch = 0;
			float fRoll = 0;
			int nFaceCount = 0;
			if (!g_faceRecProcPtr->getFaceAttribute(matImage, fYaw, fPitch, fRoll, nFaceCount))
			{
				myServerLog() << g_faceRecProcPtr->errorMsg();
				m_currentVerifyInfo.sErrorMsg = g_faceRecProcPtr->errorMsg();
				return;
			}
			m_currentVerifyInfo.nFaceCount = nFaceCount;
			if (m_fMinYaw > fYaw)
			{
				m_fMinYaw = fYaw;
			}

			if (m_fMaxYaw < fYaw)
			{
				m_fMaxYaw = fYaw;
			}

			if (m_fMaxYaw - m_fMinYaw > 30)
			{
				//验证成功
				m_currentVerifyInfo.nEndTime = g_appInfoPtr->serverMTime();
				m_currentVerifyInfo.nRealness = 1;
				m_currentVerifyInfo.bPass = true;
				m_currentVerifyInfo.bIsVerify = true;
			}
		}
	}
	catch (const std::exception &e)
	{
		myDebug() << QString::fromLocal8Bit("人脸比对失败,%1").arg(e.what());
	}
}

bool faceLiveness::setBaseImage()
{
    QString sFileName = g_appInfoPtr->m_sStudentPhotoPath.right(g_appInfoPtr->m_sStudentPhotoPath.length() - g_appInfoPtr->m_sStudentPhotoPath.lastIndexOf("/") - 1);
    sFileName = g_appInfoPtr->m_sCacheFileDir + sFileName;
    if(!QFile::exists(sFileName))
    {
        ShowMsg(QString::fromLocal8Bit("底照下载失败，请检查网络"), this, MSG_ICON_TYPE::mit_error);
        return false;
    }
    QFileInfo filePath(sFileName);

    if(!g_faceRecProcPtr->setBaseImage(filePath.absoluteFilePath()))
    {
        //g_faceRecProcPtr->errorMsg()
        ShowMsg(QString::fromLocal8Bit("底照不符合要求，请更换照片"), this, MSG_ICON_TYPE::mit_error);
        return false;
    }
    return true;
}

void faceLiveness::on_btn_fl_startVerify_clicked()
{
    if(g_faceRecProcPtr == nullptr)
    {
        g_faceRecProcPtr = std::make_shared<CFaceRecProc>();       
    }

    if (!g_appInfoPtr->m_sStudentPhotoPath.isEmpty())
    {
//        if(!g_faceRecProcPtr->hasBaseImage())
        {
            if(!setBaseImage())
            {
                return;
            }
        }

    }
    else
    {
        ShowMsg(QString::fromLocal8Bit("当前考试未底照"), this, MSG_ICON_TYPE::mit_error);
        return;
    }


//    if(!g_faceRecProcPtr->hasBaseImage())
//    {
//        if(!setBaseImage())
//        {
//            return;
//        }
//    }

    m_bStartCompare = true;
    m_fMaxYaw = 0;
    m_fMinYaw = 0;
    m_sLivenessStatus = STATUS_TYPE::ST_SUCCESS;
    ui->btn_fl_startVerify->setVisible(false);
    ui->label_fl_tips->setVisible(false);
    ui->widget_fc_camera->setFixedHeight(g_appInfoPtr->m_fRate*431);
    ui->widget_fl_action->setVisible(true);
    {
        std::scoped_lock sl(m_livenessListMutex);
        m_currentVerifyInfo = m_livenessList[m_nCurIndex];
    }
    initAcionIcon();
    m_ActionTimer->start();
}

void faceLiveness::initAcionIcon()
{
    QMovie *movie;
	ui->btn_fl_time->setVisible(true);
    ui->btn_fl_time->setText(QString("%1s").arg(m_currentVerifyInfo.nActionLeftSceonds));
    if(m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_FACE_DETECT)
    {
        movie = new QMovie(":/images/img-fl-face.png");
        ui->label_fl_actionTips->setText(QString::fromLocal8Bit("请让我看到您的正脸"));
        //ui->btn_fl_time->setVisible(false);
    }
    else if(m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_BLINK)
    {
        movie = new QMovie(":/images/gif-close-eyes.gif");
        ui->label_fl_actionTips->setText(QString::fromLocal8Bit("请闭眼"));
    }
    else if(m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_SHAKE)
    {
        movie = new QMovie(":/images/gif-turn-head.gif");
        ui->label_fl_actionTips->setText(QString::fromLocal8Bit("请摇头"));
    }
    else if(m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_NOD)
    {
        movie = new QMovie(":/images/gif-head-down.gif");
        ui->label_fl_actionTips->setText(QString::fromLocal8Bit("请点头"));
    }
    ui->label_fl_actionMoive->setMovie(movie);
    movie->start();
}

void faceLiveness:: saveLivenessResult()
{
	try
	{
		bool bSubmit = true;
		for (int i = 0; i < m_livenessList.count(); ++i)
		{
			if (!m_livenessList[i].matImage.empty() &&
				m_livenessList[i].sUrl.isEmpty())
			{
				bSubmit = false;
				m_nCurIndex = i;
				m_currentVerifyInfo = m_livenessList[i];
				QString sImageFile = QString("temp/photo/%1.png").arg(CCommonTools::getUuid());
				QImage img = CCommonTools::Mat2QImage(m_currentVerifyInfo.matImage);
				img.save(sImageFile, "PNG");
				//上传图片
				CHttpRequestPackage hrp;
                hrp.sUri = "/api/ecs_oe_student/client/exam/process/upload/sign";
                hrp.nRequestType = RequestType::rtClientExamProcessUploadSign;
                hrp.sCommonStr = __FILE__;			
                hrp.sCommonStr1 = sImageFile;
                hrp.sParamList.push_back(QString("fileSuffix,%1").arg(".png"));
                hrp.sParamList.push_back(QString("fileMd5,%1").arg(CCommonTools::fileMd5(sImageFile)));

                hrp.eParamType = HttpParamType::hptBody;
				g_httpBllPtr->post(hrp);
				break;
			}
		}

		if (bSubmit)
		{
			bool bSucceed = true;
			Json::Value jLiveness = Json::Value::null;
			jLiveness["status"] = m_sLivenessStatus.toStdString();
			jLiveness["faceLiveVerifyId"] = g_appInfoPtr->m_oExamInfo.nFaceLiveVerifyId;
			jLiveness["processTime"] = m_nEndTime - m_nStartTime;
			for (int i = 0; i < m_livenessList.count(); ++i)
			{
				if (m_livenessList[i].sActionType == ACTION_TYPE::AT_FACE_DETECT)
				{
					jLiveness["examRecordDataId"] = g_appInfoPtr->m_oExamInfo.nExamRecordDataId;
					jLiveness["faceCount"] = m_livenessList[i].nFaceCount;
					jLiveness["realness"] = m_livenessList[i].nRealness;
					jLiveness["similarity"] = m_livenessList[i].fSimilarity;
                    jLiveness["fileUrl"] = m_livenessList[i].sUrl.toStdString();
					if (m_livenessList[i].nFaceCount == 0)
					{
						jLiveness["status"] = STATUS_TYPE::ST_NOT_ONESELF.toStdString();
					}

				}
				else
				{
					Json::Value jAction = Json::Value::null;
					if (!m_livenessList[i].bPass)
					{
						bSucceed = false;
						jAction["errorMsg"] = m_livenessList[i].sErrorMsg.toStdString();
					}
					jAction["fileUrl"] = m_livenessList[i].sUrl.toStdString();
					jAction["pass"] = m_livenessList[i].bPass;
					jAction["processTime"] = m_livenessList[i].nEndTime - m_livenessList[i].nStartTime;
					jAction["retry"] = 0;
					jAction["type"] = m_livenessList[i].sActionType.toStdString();
					jLiveness["actions"].append(jAction);
				}

			}
            myServerLog() << jLiveness.toStyledString().c_str();
			CHttpRequestPackage hrp;
			hrp.sUri = QString("/api/ecs_oe_student/client/exam/process/saveFaceLiveVerifyResult");
			hrp.nRequestType = RequestType::rtSaveFaceLiveVerifyResult;
			hrp.eParamType = HttpParamType::hptCustomBody;
			hrp.sParamList.push_back(QString("CustomBody,%1").arg(jLiveness.toStyledString().c_str()));

            g_httpBllPtr->post(hrp);

		}
	}
	catch (const std::exception &e)
	{
		myDebug() << QString::fromLocal8Bit("人脸比对失败,%1").arg(e.what());
	}
}

void faceLiveness::countdownTimer()
{
	try
	{
		--m_nMaxSeconds;
		ui->label_fl_time->setText(QString("(%1)").arg(m_nMaxSeconds));
        if(m_livenessType == FACE_LIVENESS_TYPE::flt_inprogress && m_nMaxSeconds <= 0)
		{           
            //整体超时
            m_bStartCompare = false;
            m_ActionTimer->stop();
            m_countdownTimer->stop();
            {
                std::scoped_lock sl(m_livenessListMutex);
                m_livenessList[m_nCurIndex] = m_currentVerifyInfo;
            }
            m_sLivenessStatus = STATUS_TYPE::ST_TIME_OUT;
            saveLivenessResult();

		}
	}
	catch (const std::exception &e)
	{
		myDebug() << QString::fromLocal8Bit("人脸比对失败,%1").arg(e.what());
	}
}

void faceLiveness::actionTimer()
{        
	try
	{
		if (m_currentVerifyInfo.bIsVerify)
		{
			//当前比对完成
			{
				std::scoped_lock sl(m_livenessListMutex);
				m_livenessList[m_nCurIndex] = m_currentVerifyInfo;
			}

			//下一个动作
			++m_nCurIndex;
			if (m_nCurIndex < m_livenessList.count())
			{
				std::scoped_lock sl(m_livenessListMutex);
				m_currentVerifyInfo = m_livenessList[m_nCurIndex];
                m_currentVerifyInfo.reset(m_livenessList[m_nCurIndex].sActionType == ACTION_TYPE ::AT_FACE_DETECT ? m_nFaceDetectDuration : g_appInfoPtr->m_oExamInfo.nActionDuration);
				initAcionIcon();
				{
					std::scoped_lock sl(m_imgMutex);
					m_imgList.clear();
				}
			}
			else
			{
				//整体比对完成
				m_sLivenessStatus = STATUS_TYPE::ST_SUCCESS;
				m_bStartCompare = false;
				m_ActionTimer->stop();
				m_countdownTimer->stop();
				saveLivenessResult();
				return;
			}

		}
		else
		{
			if (m_currentVerifyInfo.nActionLeftSceonds <= 0)
			{
                ++m_nFaceRetryCount;
                {
                    std::scoped_lock sl(m_imgMutex);
                    m_imgList.clear();
                }

                if(m_nFaceRetryCount >= m_nMaxFaceRetryCount)
                {
                    if(m_nFaceWholeRetryCount >= m_nMaxFaceWholeRetryCount)
                    {
                        //整体失败
                        m_bStartCompare = false;
                        m_currentVerifyInfo.sErrorMsg = QString::fromLocal8Bit("action timeout");
                        m_ActionTimer->stop();
                        m_countdownTimer->stop();
                        {
                            std::scoped_lock sl(m_livenessListMutex);
                            m_livenessList[m_nCurIndex] = m_currentVerifyInfo;
                        }
                        m_sLivenessStatus = STATUS_TYPE::ST_TIME_OUT;
                        if(m_livenessType == FACE_LIVENESS_TYPE::flt_inprogress)
                        {
                            saveLivenessResult();
                        }
                        else
                        {							
                            showVerifyResultUI();
							QTimer::singleShot(2000, this, [&]() {
								emit faceLivenessFaild();
							});
                            
                        }
                        return;
                    }

                    ++m_nFaceWholeRetryCount;
                    {
                        std::scoped_lock sl(m_imgMutex);
                        m_imgList.clear();
                    }

					m_nFaceRetryCount = 0;
                    m_nCurIndex = 0;
                    if (m_nCurIndex < m_livenessList.count())
                    {
                        std::scoped_lock sl(m_livenessListMutex);
                        m_currentVerifyInfo = m_livenessList[m_nCurIndex];
                        m_currentVerifyInfo.reset(m_livenessList[m_nCurIndex].sActionType == ACTION_TYPE::AT_FACE_DETECT ? m_nFaceDetectDuration : g_appInfoPtr->m_oExamInfo.nActionDuration);
                        initAcionIcon();
                    }
                    return;
                }

                ++m_nFaceRetryCount;
                m_currentVerifyInfo.reset(m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_FACE_DETECT ? m_nFaceDetectDuration : g_appInfoPtr->m_oExamInfo.nActionDuration);
                initAcionIcon();
			}
			else
			{
				--m_currentVerifyInfo.nActionLeftSceonds;
				ui->btn_fl_time->setText(QString("%1s").arg(m_currentVerifyInfo.nActionLeftSceonds));

                if(m_currentVerifyInfo.sActionType == ACTION_TYPE::AT_FACE_DETECT)
                {
                    if(m_currentVerifyInfo.nActionLeftSceonds == 50)
                    {
                        ShowMsg(QString::fromLocal8Bit("人脸检测不通过(请调整角度和光线进行尝试，不要戴眼镜)"), (QWidget*)this->parent());
                    }
                    else if(m_currentVerifyInfo.nActionLeftSceonds == 30)
                    {
                         ShowMsg(QString::fromLocal8Bit("人脸检测不通过(请调整角度和光线进行尝试，不要戴眼镜)"), (QWidget*)this->parent());
                    }
                    else if(m_currentVerifyInfo.nActionLeftSceonds == 1)
                    {
                         ShowMsg(QString::fromLocal8Bit("若多次尝试不通过，请联系老师更换照片后重试"), (QWidget*)this->parent());
                    }
                }
			}
		}
	}
	catch (const std::exception &e)
	{
		myDebug() << QString::fromLocal8Bit("人脸比对失败,%1").arg(e.what());
	}
}

//文件上传
void faceLiveness::onClientExamProcessUploadSign(CClientExamProcessUploadSign processUpload)
{
    try
    {
        if(processUpload.sCommonStr == __FILE__)
        {
            if(processUpload.nCode == 200)
            {
                m_nRetryCount = 0;
                CHttpRequestPackage hrp;
                hrp.sUri = processUpload.sFormUrl;
                hrp.nRequestType = RequestType::rtUploadFileToAliyun;
                hrp.sCommonStr = __FILE__;
                hrp.sCommonStr1 = processUpload.sAccessUrl;
                hrp.sParamList.push_back(QString("OSSAccessKeyId,%1").arg(processUpload.sOssAcessKeyId));
                hrp.sParamList.push_back(QString("Signature,%1").arg(processUpload.sSignature));
                hrp.sParamList.push_back(QString("key,%1").arg(processUpload.sKey));
                hrp.sParamList.push_back(QString("policy,%1").arg(processUpload.sPolicy));
                hrp.sParamList.push_back(QString("success_action_status,%1").arg(200));
                hrp.sParamList.push_back(QString("formdataFileType,file,%1").arg(processUpload.sFilePath));
                hrp.bNoHostPrefix = true;
                hrp.eParamType = HttpParamType::hptFormdata;
                g_httpBllPtr->post(hrp);

            }
            else
            {
                if(processUpload.sMessage.isEmpty())
                {
                    ShowMsg(QString::fromLocal8Bit("上传照片失败"), this, MSG_ICON_TYPE::mit_error);
                }
                else
                {
                    ShowMsg(processUpload.sMessage, this, MSG_ICON_TYPE::mit_error);
                }

                if(m_nRetryCount < 4)
                {
                    saveLivenessResult();
                    m_nRetryCount++;
                }
                else
                {
                    emit faceLivenessFaild();
                }
            }
        }
    }
    catch (const std::exception &e)
    {
        ShowMsg(QString::fromLocal8Bit("上传照片失败"), this, MSG_ICON_TYPE::mit_error);
        myServerLog()<<"exception error"<<e.what();
    }
}

void faceLiveness::onUploadFileToAliyun(CUploadFileToAliyun uploadFileToAliyun)
{
    try
    {
        if(uploadFileToAliyun.sCommonStr == __FILE__)
        {
            if (uploadFileToAliyun.nCode == 200)
            {
                m_nRetryCount = 0;
                m_livenessList[m_nCurIndex].sUrl = uploadFileToAliyun.sFileUrl;
                if(m_livenessType == FACE_LIVENESS_TYPE::flt_entry_exam &&
                        m_livenessList[m_nCurIndex].sActionType == ACTION_TYPE::AT_FACE_DETECT)
                {
                    saveFaceCampareResult();
                }
                else
                {
                    saveLivenessResult();
                }

            }
            else
            {
                if (uploadFileToAliyun.sMessage.isEmpty())
                {
                    ShowMsg(QString::fromLocal8Bit("上传照片失败"), this, MSG_ICON_TYPE::mit_error);
                }
                else
                {
                    ShowMsg(uploadFileToAliyun.sMessage, this, MSG_ICON_TYPE::mit_error);
                }

                if(m_nRetryCount < 4)
                {
                    saveLivenessResult();
                    m_nRetryCount++;
                }
                else
                {
                    emit faceLivenessFaild();
                }
            }
        }
    }
    catch (const std::exception &e)
    {
        myDebug() << QString::fromLocal8Bit("人脸比对失败,%1").arg(e.what());
    }
}

void faceLiveness::saveFaceCampareResult()
{
    CHttpRequestPackage hrp;
    hrp.sUri = "/api/ecs_oe_student/client/exam/process/saveFaceCompareResult";
    hrp.nRequestType = RequestType::rtSaveFaceCompareResult;
    Json::Value jBody = Json::Value::null;
    jBody["faceCompareResult"] = QString::number(m_livenessList[m_nCurIndex].fSimilarity).toStdString();
    jBody["fileUrl"] = m_livenessList[m_nCurIndex].sUrl.toStdString();
    jBody["pass"] = true;
    jBody["processTime"] = m_livenessList[m_nCurIndex].nEndTime - m_livenessList[m_nCurIndex].nStartTime;
    jBody["stranger"] = m_livenessList[m_nCurIndex].nFaceCount > 1;
    hrp.sParamList.push_back(QString("CustomBody,%1").arg(jBody.toStyledString().c_str()));
    hrp.eParamType = HttpParamType::hptCustomBody;

    g_httpBllPtr->post(hrp);
}

void faceLiveness::onSaveFaceCompareResult(CBaseResponsePackage res)
{
    try
    {
        if(res.nCode == 200)
        {
            m_nRetryCount = 0;
            saveLivenessResult();
        }
        else
        {
            if(m_nRetryCount < 4)
            {
                saveFaceCampareResult();
                m_nRetryCount++;
            }
            else
            {
                emit faceLivenessFaild();
            }
        }
    }
    catch (const std::exception &e)
    {
        ShowMsg(QString::fromLocal8Bit("保存人脸识别信息失败"), this, MSG_ICON_TYPE::mit_error);
        myServerLog()<<"exception error"<<e.what();
    }
}

void faceLiveness::showVerifyResultUI()
{
	if (m_sLivenessStatus == STATUS_TYPE::ST_SUCCESS)
	{
		ui->label_fl_hint->setText(QString::fromLocal8Bit("恭喜您完成检测！"));
		ui->label_fl_icon->setPixmap(QPixmap(":/images/icon-welcom.png"));
	}
    else if(m_sLivenessStatus == STATUS_TYPE::ST_TIME_OUT)
	{
        ui->label_fl_hint->setText(QString::fromLocal8Bit("检测失败：活体检测超时"));
		ui->label_fl_icon->setPixmap(QPixmap(":/images/icon-liveness-faild.png"));
	}
    else
    {
        ui->label_fl_hint->setText(QString::fromLocal8Bit("检测失败：单个动作超时"));
        ui->label_fl_icon->setPixmap(QPixmap(":/images/icon-liveness-faild.png"));
    }

	ui->label_fl_hint->adjustSize();
	ui->widget_fc_BG->setVisible(false);
	ui->widget_fl_hint->setVisible(true);
	ui->widget_fl_hint->setGeometry((width() - ui->label_fl_hint->width() - g_appInfoPtr->m_fRate * 180) / 2, (height() - g_appInfoPtr->m_fRate * 180) / 2,
		ui->label_fl_hint->width() + g_appInfoPtr->m_fRate * 180, g_appInfoPtr->m_fRate * 180);
	ui->label_fl_icon->setGeometry(g_appInfoPtr->m_fRate * 75, g_appInfoPtr->m_fRate * 70, g_appInfoPtr->m_fRate * 40, g_appInfoPtr->m_fRate * 40);

	ui->label_fl_hint->setGeometry(ui->label_fl_icon->x() + ui->label_fl_icon->width() + g_appInfoPtr->m_fRate * 20,
		ui->label_fl_icon->y() + (ui->label_fl_icon->height() - ui->label_fl_hint->height()) / 2,
		ui->label_fl_hint->width(), ui->label_fl_hint->height());
}

//保存人脸活体验证结果
void faceLiveness::onSaveFaceLiveVerifyResult(CBaseResponsePackage res)
{
	try
	{
		if (res.nCode == 200)
		{
            m_nRetryCount = 0;
			if (m_pCloseTimer == nullptr)
			{
				m_pCloseTimer = std::make_shared<QTimer>();
				m_pCloseTimer->setInterval(2000);
				connect(m_pCloseTimer.get(), &QTimer::timeout, this, [&]() {
					if (m_sLivenessStatus == STATUS_TYPE::ST_SUCCESS)
					{
						emit faceLivenessSucceed();
					}
					else
					{
						emit faceLivenessFaild();
					}
				});
			}

			showVerifyResultUI();

			m_pCloseTimer->start();
			

		}
		else
		{
			if (res.sMessage.isEmpty())
			{
                ShowMsg(QString::fromLocal8Bit("活体验证结果失败"), this, MSG_ICON_TYPE::mit_error);
			}
			else
			{
                ShowMsg(res.sMessage, this, MSG_ICON_TYPE::mit_error);
			}

            if(m_nRetryCount < 4)
            {
                saveLivenessResult();
                m_nRetryCount++;
            }
            else
            {
                emit faceLivenessFaild();
            }
		}
	}
	catch (const std::exception &e)
	{
		myDebug() << QString::fromLocal8Bit("人脸比对失败,%1").arg(e.what());
	}
}

void faceLiveness::on_btn_fl_close_clicked()
{
    emit faceLivenessFaild();
}
