package org.jeecg.common.face.service.impl;

import com.arcsoft.face.*;
import com.arcsoft.face.enums.DetectMode;
import com.arcsoft.face.enums.DetectOrient;
import com.arcsoft.face.enums.ErrorInfo;
import com.arcsoft.face.toolkit.ImageFactory;
import com.arcsoft.face.toolkit.ImageInfo;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.face.config.ArcFaceConfig;
import org.jeecg.common.face.service.IArcFaceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

//@Service
@Slf4j
public class ArcFaceService implements IArcFaceService {

//    @Autowired
    private ArcFaceConfig arcFaceConfig;
    private FaceEngine faceEngine;

//    @PostConstruct
    private void InitEngine(){
        faceEngine = new FaceEngine(arcFaceConfig.getLibPath());

        int errorCode = faceEngine.activeOnline(arcFaceConfig.getAppId(), arcFaceConfig.getSdkKey());
        if (errorCode != ErrorInfo.MOK.getValue() && errorCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue()) {
            log.error("人脸识别引擎激活失败");
            return;
        }
        ActiveFileInfo activeFileInfo=new ActiveFileInfo();
        errorCode = faceEngine.getActiveFileInfo(activeFileInfo);
        if (errorCode != ErrorInfo.MOK.getValue() && errorCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue()) {
            log.error("获取激活文件信息失败");
            return;
        }
        //引擎配置
        EngineConfiguration engineConfiguration = new EngineConfiguration();
        engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);
        engineConfiguration.setDetectFaceOrientPriority(DetectOrient.ASF_OP_ALL_OUT);
        engineConfiguration.setDetectFaceMaxNum(10);
        //功能配置
        FunctionConfiguration functionConfiguration = new FunctionConfiguration();
        functionConfiguration.setSupportAge(true);
        functionConfiguration.setSupportFaceDetect(true);
        functionConfiguration.setSupportFaceRecognition(true);
        functionConfiguration.setSupportGender(true);
        functionConfiguration.setSupportLiveness(true);
        functionConfiguration.setSupportIRLiveness(true);
        //3.0 不支持  替换增强版后启用
//        functionConfiguration.setSupportImageQuality(true);
//        functionConfiguration.setSupportMaskDetect(true);
//        functionConfiguration.setSupportUpdateFaceData(true);
        engineConfiguration.setFunctionConfiguration(functionConfiguration);

        //初始化引擎
        errorCode = faceEngine.init(engineConfiguration);

        if (errorCode != ErrorInfo.MOK.getValue()) {
            faceEngine = null;
            log.error("初始化引擎失败");
        }
    }

    private List<FaceInfo> detectFace(File img){
        List<FaceInfo> faceInfoList = new ArrayList<>();
        ImageInfo imageInfo = ImageFactory.getRGBData(img);
        if(imageInfo == null){
            return faceInfoList;
        }
        int errcode = faceEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList);
        if(errcode != 0){
            return null;
        }
        return faceInfoList;
    }

    private FaceFeature computeFeature(ImageInfo imageInfo,FaceInfo face){
        FaceFeature faceFeature = new FaceFeature();
        int errorCode = faceEngine.extractFaceFeature(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), face, faceFeature);
        if(errorCode != 0){
            return null;
        }
        return faceFeature;
    }

    private FaceSimilar featureCompare(FaceFeature src, FaceFeature target){
        FaceFeature targetFaceFeature = new FaceFeature();
        targetFaceFeature.setFeatureData(target.getFeatureData());
        FaceFeature sourceFaceFeature = new FaceFeature();
        sourceFaceFeature.setFeatureData(src.getFeatureData());
        FaceSimilar faceSimilar = new FaceSimilar();
        int errorCode = faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar);
        if(errorCode != 0){
            return null;
        }
        return faceSimilar;
    }
    @Override
    public File[] loadFaceData() {
        File folder = new File(arcFaceConfig.getFacePath());
        return folder.listFiles();
    }

    @Override
    public boolean hasFace(File file) {
        List<FaceInfo> faceInfos = detectFace(file);
        if(faceInfos!=null){
            return faceInfos.size() > 0;
        }
        return false;
    }

    @Override
    public boolean isSamePerson(File src, File dst, double threshold) {
        ImageInfo imageInfoSrc = ImageFactory.getRGBData(src);
        ImageInfo imageInfoDst = ImageFactory.getRGBData(dst);
        List<FaceInfo> faceSrc = detectFace(src);
        List<FaceInfo> faceDst = detectFace(dst);
        for(FaceInfo _src: faceSrc){
            FaceFeature faceFeatureSrc = computeFeature(imageInfoSrc, _src);
            for(FaceInfo _dst: faceDst){
                FaceFeature faceFeatureDst = computeFeature(imageInfoDst, _dst);
                FaceSimilar similar = featureCompare(faceFeatureSrc, faceFeatureDst);
                if(similar != null && similar.getScore() >= threshold){
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public FaceFeature getFeature(File file) {
        ImageInfo imageInfo = ImageFactory.getRGBData(file);
        List<FaceInfo> faceInfoList = detectFace(file);
        return computeFeature(imageInfo, faceInfoList.get(0));
    }

    @Override
    public String getActiveDeviceInfo() {
//        //替换增强版后启用
//        if(faceEngine!=null){
//            ActiveDeviceInfo activeDeviceInfo = new ActiveDeviceInfo();
//            int errorCode = faceEngine.getActiveDeviceInfo(activeDeviceInfo);
//            if(errorCode == 0){
//                return activeDeviceInfo.getDeviceInfo();
//            }else{
//                return String.valueOf(errorCode);
//            }
//        }
        return null;
    }
}
