#include "FaceReconitionHandle.h"

#include <QImage>
#include <QThread>
#include <iostream>

#define cimg_display 0
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION

#include "CImg.h"
#include "stb_image.h"
#include "stb_image_write.h"

using namespace cimg_library;


FaceReconitionHandle::FaceReconitionHandle() {
    
}

FaceReconitionHandle::~FaceReconitionHandle(){
    static int i=0;

    if (ctxHandle != nullptr) {
        HF_ReleaseFaceContext(ctxHandle);
        qInfo()<<"人脸析构"<<++i;

        ctxHandle = nullptr;
    }
}
bool FaceReconitionHandle::getImageChanged()const{
    return isImageChanged.load(std::memory_order_acquire);
}

void FaceReconitionHandle::setImageChanged(bool imageChanged){
    this->isImageChanged.store(imageChanged, std::memory_order_release);
}

cv::Mat FaceReconitionHandle::loadImage(const QString &path) {
    // 尝试使用OpenCV直接加载图像
    std::string stdPath = path.toStdString();
    cv::Mat image = cv::imread(stdPath, cv::IMREAD_COLOR);
    if (!image.empty()) {
        qInfo() << "图像以OpenCV成功加载。";
        return image;
    }

    image= loadImageFromByteStream(path);
    if (!image.empty()) {
        qInfo() << "图像以CImg成功加载。";
        return image;
    }
    image=loadImageWithStb(path);
    if (!image.empty()) {
        qInfo() << "图像以stb_image成功加载。";
        return image;
    }
    image=loadQImageFromByteStream(path);
    if (!image.empty()) {
        qInfo() << "图像以QImage成功加载。";
        return image;
    }
    qInfo() << "四种加载方式都不行";
    return cv::Mat();

}

void FaceReconitionHandle::initSourceImageMap(std::map<QString,QString>&maps,int numberFaces,float confidence){
    ScopeSemaphoreExit streamGuard([this]() {
        isImageChanged.store(false, std::memory_order_release);
    });
    //featureRemove();
    featureRemoveMap(maps);
    HResult ret;
    // 初始化context
#if defined(__arm__) || defined(__ARM_ARCH)
    // ARM 平台相关的代码
    QString bPath = QCoreApplication::applicationDirPath() + "/model_zip/N1su_5";
#elif defined(__i386__) || defined(__x86_64__)
    QString bPath = QCoreApplication::applicationDirPath() + "/model_zip/T1_5";
#else
#error "不支持的架构"
    
#endif
    QByteArray && bypath = bPath.toUtf8();
    char* spath = bypath.data();
    HString path = spath;
    HInt32 option = HF_ENABLE_QUALITY | HF_ENABLE_FACE_RECOGNITION | HF_ENABLE_MASK_DETECT;
    HF_DetectMode detMode = HF_DETECT_MODE_IMAGE;   // 选择图像模式 即总是检测
    if(ctxHandle==nullptr){
        // 创建ctx
        ret = HF_CreateFaceContextFromResourceFileOptional(path, option, detMode, numberFaces, &ctxHandle);
        if (ret != HSUCCEED) {
            qInfo() << QString("Create ctx error: %1").arg(ret);
            return;
        }
    }
    //customIds.clear();
    int i = 0;
    qInfo()<< "加载图像size: "<<maps.size();
    
    for (auto it = maps.begin(); it != maps.end(); ++it,++i) {
        const QString& key = it->first;
        QString& value = it->second;
        cv::Mat image = loadImage(value);
        if (image.empty()) {
            qInfo() << "错误：使用QImage预处理无法加载图像 " << value;
            continue ;
        }
        HF_ImageData imageData = {0};
        imageData.data = image.data;
        imageData.height = image.rows;
        imageData.width = image.cols;
        imageData.rotation = VIEW_ROTATION_0;
        imageData.format = FORMAT_BGR;
        
        HImageHandle imageSteamHandle;
        ret = HF_CreateImageStream(&imageData, &imageSteamHandle);
        this->configConfidence=confidence;
        if (ret != HSUCCEED) {
            qInfo() << QString("image handle error: %1").arg((long)imageSteamHandle);
            HF_ReleaseImageStream(imageSteamHandle); // 释放资源
            return;
        }
        
        HF_MultipleFaceData multipleFaceData = {0};
        HF_FaceContextRunFaceTrack(ctxHandle, imageSteamHandle, &multipleFaceData);
        
        if (multipleFaceData.detectedNum <= 0) {
            qInfo() << QString("initSourceImageMap:未检测到人脸: %1").arg(key);
            HF_ReleaseImageStream(imageSteamHandle); // 释放资源
            return;
        }
        
        HF_FaceFeature feature = {0};
        ret = HF_FaceFeatureExtract(ctxHandle, imageSteamHandle, multipleFaceData.tokens[0], &feature);
        if (ret != HSUCCEED) {
            qInfo() << QString("特征提取出错: %1").arg(ret);
            HF_ReleaseImageStream(imageSteamHandle); // 释放资源
            
            return;
        }
        
        char* tagName = new char[key.size() + 1];
        std::strcpy(tagName, key.toStdString().c_str());
        HF_FaceFeatureIdentity identity = {0};
        identity.feature = &feature;
        identity.customId = i;
        //customIds.push_back( identity.customId);
        mapCustomId[key]=i;
        identity.tag = tagName;
        
        ret = HF_FeaturesGroupInsertFeature(ctxHandle, identity);
        if (ret != HSUCCEED) {
            qInfo() << QString("插入失败: %1").arg(ret);
            HF_ReleaseImageStream(imageSteamHandle); // 释放资源
            return;
        }
        
        delete[] tagName;
        
        ret = HF_ReleaseImageStream(imageSteamHandle);
        if (ret == HSUCCEED) {
            imageSteamHandle = nullptr;
            qInfo() << QString("mage released");
        } else {
            qInfo() << QString("image release error: %l").arg(ret);
        }
    }
}


void FaceReconitionHandle::featureRemove(){
    if(customIds.size()>0){
        for(auto customId:customIds){
            HResult ret= HF_FeaturesGroupFeatureRemove(ctxHandle,customId);
            qInfo()<<"ret:featureRemove "<<ret;
        }
        setImageChanged(false);
    }
}
void FaceReconitionHandle::featureRemoveMap(std::map<QString, QString> &maps){
    if(mapCustomId.size()>0){
        for (auto it = mapCustomId.begin(); it != mapCustomId.end(); ) {
            // 检查当前键是否存在于 maps 中
            if (maps.find(it->first) == maps.end()) {
                int32_t & customId = it->second;

                // 如果不存在，则从 mapCustomId 中删除该键
                it = mapCustomId.erase(it);
                HResult ret= HF_FeaturesGroupFeatureRemove(ctxHandle,customId);
                if (ret != HSUCCEED) {
                    qInfo() << QString("从特征组中移除一个面部特征失败: %1").arg(ret);
                    continue;
                }
            } else {
                // 如果存在，则继续下一个元素
                ++it;
            }
        }
        setImageChanged(false);
    }

}

cv::Mat FaceReconitionHandle::loadImageWithStb(const QString& filename) {
    int width, height, channels;
    QByteArray && b_filename = filename.toUtf8();
    char* c_filename = b_filename.data();
    // 使用 stb_image 加载图像，stbi_load 会根据图像格式返回数据
    unsigned char* data = stbi_load(c_filename, &width, &height, &channels, 0);
    if (!data) {
        qInfo() << "tb_image无法加载图像 " << filename;
        return cv::Mat();
    }

    // 检查图像通道数，创建对应的 cv::Mat
    cv::Mat image;
    if (channels == 1) {
        // 灰度图像
        image = cv::Mat(height, width, CV_8UC1, data);
    } else if (channels == 3) {
        // 彩色图像
        image = cv::Mat(height, width, CV_8UC3, data);
        // OpenCV 默认使用 BGR，因此需要将 RGB 转换为 BGR
        cv::cvtColor(image, image, cv::COLOR_RGB2BGR);
    } else if (channels == 4) {
        // 带 Alpha 通道的图像
        image = cv::Mat(height, width, CV_8UC4, data);
        // RGB -> BGR（包括 Alpha 通道）
        cv::cvtColor(image, image, cv::COLOR_RGBA2BGRA);
    }

    // 释放 stb_image 加载的内存
    stbi_image_free(data);

    return image;
}

cv::Mat FaceReconitionHandle::loadQImageFromByteStream(const QString& filePath) {
    // 使用 QImage 加载图像
    QImage qimage(filePath);
    if (qimage.isNull()) {
        qInfo() << "QImage Error: 无法加载图像 " << filePath;
        return cv::Mat();
    }

    // 将 QImage 转换为 cv::Mat
    cv::Mat mat(qimage.height(), qimage.width(), CV_8UC4, const_cast<uchar*>(qimage.bits()), qimage.bytesPerLine());
    // 如果需要 BGR 格式
    cv::cvtColor(mat, mat, cv::COLOR_RGBA2BGR);

    return mat;
}
cv::Mat FaceReconitionHandle::loadImageFromByteStream(const QString& filePath) {
    
    try {
        // 使用 CImg 读取 JPEG 图像
        QByteArray bPath  =filePath.toUtf8();
        const char* ctr=bPath.data();
        CImg<unsigned char> cimg_image(ctr);
        
        // 将 CImg 对象转换为 OpenCV 的 Mat 格式
        int width = cimg_image.width();
        int height = cimg_image.height();
        cv::Mat mat(height, width, CV_8UC3);
        
        cimg_forXY(cimg_image, x, y) {
            // 注意OpenCV默认是BGR顺序
            // CImg中像素的存取方式是 (x, y, z, c) 其中c是颜色通道
            mat.at<cv::Vec3b>(y, x)[2] = cimg_image(x, y, 0, 0); // R
            mat.at<cv::Vec3b>(y, x)[1] = cimg_image(x, y, 0, 1); // G
            mat.at<cv::Vec3b>(y, x)[0] = cimg_image(x, y, 0, 2); // B
        }
        return mat;
    } catch (const CImgException& e) {
        qInfo() << "CImg Error: " << e.what();
        return cv::Mat();;
    } catch (const cv::Exception& e) {
        qInfo() << "OpenCV Error: " << e.what();
        return cv::Mat(); ;
    }
    
    return cv::Mat();
}



void FaceReconitionHandle::doesItExistEmployee(const QString &sn,const cv::Mat &source,std::list<vides_data::faceRecognitionResult>&faces){
    thread_time.store(QDateTime::currentMSecsSinceEpoch(), std::memory_order_release);
    ScopeSemaphoreExit streamGuard([this]() {
        isRunning.store(false, std::memory_order_release);
    });
    HResult ret;
    HF_ContextCustomParameter parameter = {0};
    HF_ImageData imageData = {0};
    imageData.data = source .data;
    imageData.height = source.rows;
    imageData.width = source.cols;
    imageData.rotation = VIEW_ROTATION_0;
    imageData.format = FORMAT_BGR;
    
    HImageHandle imageSteamHandle;
    
    
    ret = HF_CreateImageStream(&imageData, &imageSteamHandle);
    if (ret != HSUCCEED) {
        qInfo()<<QString("SN(%1): image handle error:%2").arg(sn).arg((long)imageSteamHandle,0,10);
        return ;
    }
    HF_MultipleFaceData multipleFaceData = {0};
    HF_FaceContextRunFaceTrack(ctxHandle, imageSteamHandle, &multipleFaceData);
    
    if (multipleFaceData.detectedNum <= 0) {
        qInfo()<<QString("SN(%1): 未检测到人脸").arg(sn);
        return ;
    }
    
    std::vector<std::vector<float>> features;
    // 被搜索的目标这边推荐使用拷贝式的接口来获取特征向量
    HInt32 featureNum;
    HF_GetFeatureLength(ctxHandle, &featureNum);
    for(int j=0;j< multipleFaceData.detectedNum; ++j){
        //qInfo()<<QString("doesItExistEmployee==>面部索引: %1").arg(j);
        qDebug() << QString("SN(%1): 面部索引:%2").arg(sn).arg(j);
        std::vector<float> newfeature(featureNum,0.0f);
        ret = HF_FaceFeatureExtractCpy(ctxHandle, imageSteamHandle, multipleFaceData.tokens[j], newfeature.data());
        if(ret != HSUCCEED) {
            qInfo() << QString("SN(%1): 特征提取出错:%2").arg(sn).arg(ret);
            HF_ReleaseImageStream(imageSteamHandle);
            return ;
        }
        features.push_back(newfeature);
    }
    int rect=0;
    for(auto feat:features){
        HF_FaceFeature feature;
        feature.size = feat.size();
        feature.data = feat.data();
        HF_FaceFeatureIdentity searchIdentity = {0};
        //    HF_FaceFeature featureSearched = {0};
        //    searchIdentity.feature = &featureSearched;
        HFloat confidence;
        ret = HF_FeaturesGroupFeatureSearch(ctxHandle, feature, &confidence, &searchIdentity);
        if (ret != HSUCCEED) {
            qInfo() << QString("SN(%1): 搜索失败:%2").arg(sn).arg(ret);
            return ;
        }
        qInfo() << QString("SN(%1): 搜索置信度:%2").arg(sn).arg(confidence);
        qInfo() << QString("SN(%1): 匹配到的tag:%2").arg(sn).arg(searchIdentity.tag);
        qInfo() << QString("SN(%1): 匹配到的customId:%2").arg(sn).arg(searchIdentity.customId);
        // Face Pipeline
        //printf("人脸特征数量: %d", faceNum);
        if (confidence > configConfidence) {
            vides_data::faceRecognitionResult newface;
            newface.id=searchIdentity.tag;
            newface.x=multipleFaceData.rects[rect].x;
            newface.y=multipleFaceData.rects[rect].y;
            newface.width=multipleFaceData.rects[rect].width;
            newface.height=multipleFaceData.rects[rect].height;
            faces.push_back(newface);
        }
        
        rect++;
    }
    ret = HF_ReleaseImageStream(imageSteamHandle);
    if (ret == HSUCCEED) {
        imageSteamHandle = nullptr;
        // printf("image released");
    } else {
        //printf("image release error: %ld", ret);
        qInfo() << QString("SN(%1): image release error:%2").arg(sn).arg(ret);
    }
}

