package com.ktg.common.utils.face;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
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.enums.ExtractType;
import com.arcsoft.face.toolkit.ImageInfo;
import com.google.common.collect.Lists;
import com.ktg.common.config.RuoYiConfig;
import com.ktg.common.utils.FileDriveLetterUtils;
import com.ktg.common.utils.StringUtils;
import com.ktg.common.vo.FaceCutVO;
import com.ktg.common.vo.FaceMatchVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static com.arcsoft.face.toolkit.ImageFactory.getRGBData;

/**
 * 面部裁剪工具
 *
 * @author cgj
 */
@Slf4j
public class ArcSoftMothodUtil {


    static FaceEngine faceEngine;

    static {
        //从官网获取
        String appId;
        String sdkKey;
        String activeKey = null;
        String libarcsoftFaceDllPath;
        String prodApi = RuoYiConfig.getProd();
        if (StringUtils.isNotBlank(prodApi)) {
            // 在linux
            // appId = "FTN3G4pk8n2RKwjD955sRapRjbYQFefwhHd4sBZMYEz6";
            // sdkKey = "BjJomNU2bQc2SYhT7NNqwvFd1t6RRNv5DTZNYd8pMH19";
            appId = "5fiiFa6fTSRhDbiarBSMGVS1hEkDyMQLYyBgn97EzZyb";
            sdkKey = "3QndXfNCsoUM5hDyzSw2a3j7BXUXJJbEB6bLK4qzAaJb";
            activeKey = "82G1-11YW-G2ER-C3AW";
            libarcsoftFaceDllPath = "/usr/lib/ArcSoft_ArcFacePro_linux_java_V4.1/libs/LINUX64";
        } else {
            // 在win
            appId = "FTN3G4pk8n2RKwjD955sRapRjbYQFefwhHd4sBZMYEz6";
            sdkKey = "BjJomNU2bQc2SYhT7NNqwvFd9zfc72Q7nneh75r3NT3x";
            libarcsoftFaceDllPath = "D:\\work\\app\\appinstall\\ArcSoft_ArcFace_Java_Windows_x64_V3.0\\libs\\WIN64";
            // libarcsoftFaceDllPath = "D:\\work\\app\\appinstall\\ArcSoft_ArcFacePro_windows_x64_java_V4.2\\libs\\WIN64";
        }
        faceEngine = new FaceEngine(libarcsoftFaceDllPath);
        //激活引擎
        // 联网激活
        int errorCode = faceEngine.activeOnline(appId, sdkKey, activeKey);
        // 离线激活
        // int errorCode = faceEngine.activeOffline("D:\\work\\app\\appinstall\\ArcSoft_ArcFacePro_windows_x64_java_V4.2\\86M111YWX11JB4BZ.dat");

        if (errorCode != ErrorInfo.MOK.getValue() && errorCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue()) {
            System.out.println("引擎激活失败");
        }


        ActiveFileInfo activeFileInfo = new ActiveFileInfo();
        errorCode = faceEngine.getActiveFileInfo(activeFileInfo);
        if (errorCode != ErrorInfo.MOK.getValue() && errorCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue()) {
            System.out.println("获取激活文件信息失败");
        }

        //引擎配置
        EngineConfiguration engineConfiguration = new EngineConfiguration();
        engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);
        engineConfiguration.setDetectFaceOrientPriority(DetectOrient.ASF_OP_ALL_OUT);
        engineConfiguration.setDetectFaceMaxNum(1);
        // 联网激活
        // engineConfiguration.setDetectFaceScaleVal(16);
        //功能配置
        FunctionConfiguration functionConfiguration = new FunctionConfiguration();
        functionConfiguration.setSupportAge(false);
        // 联网激活
        // functionConfiguration.setSupportFace3dAngle(true);
        functionConfiguration.setSupportFaceDetect(true);
        functionConfiguration.setSupportFaceRecognition(true);
        functionConfiguration.setSupportGender(false);
        functionConfiguration.setSupportLiveness(true);
        functionConfiguration.setSupportIRLiveness(false);
        engineConfiguration.setFunctionConfiguration(functionConfiguration);

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

        if (errorCode != ErrorInfo.MOK.getValue()) {
            System.out.println("初始化引擎失败");
        }
    }


    /**
     * 图片人脸检测
     */
    public static FaceCutVO saveArcData(MultipartFile file, Long userId, String url) throws IOException {
        int errorCode;
        String prodApi = RuoYiConfig.getProd();
        // 1.判断这个文件是否有效
        Assert.isFalse(file.isEmpty(), "请上传人脸文件！");
        // 2.-----------------开始存储上传的照片--------------------
        // 人脸存储基础路径
        String profile = RuoYiConfig.getProfile();
        String basePath = FileDriveLetterUtils.getDriveLetter() + profile + "/face/" + userId + "/";
        /*if (StringUtils.isBlank(prodApi)) {
            basePath = "C:" + basePath;
        }*/
        // 原文件转存后的路径
        String imagePath;
        // 原文件转存后的前端请求路径
        String imageUrl = null;

        // 时间戳
        long currentTimestamp = System.currentTimeMillis();
        // 设置文件路径
        String filePath = basePath;
        // 确保目录存在，不存在则创建一个
        Path uploadPath = Paths.get(filePath);
        if (!Files.exists(uploadPath)) {
            Files.createDirectories(uploadPath);
        }
        // 获取文件名并构建目标路径
        String fileName = userId + "_" + currentTimestamp + "_" + file.getOriginalFilename();
        Path targetLocation = uploadPath.resolve(fileName);
        imagePath = filePath + fileName;
        // 获取前端的请求路径地址
        /*if (imagePath.contains("C:" + profile)) {
            imageUrl = imagePath.replace("C:" + profile, url + "/profile");
        } else if (imagePath.contains(profile)) {
            imageUrl = imagePath.replace(profile, url + "/prod-api" + "/profile");
        }*/
        if (StringUtils.isBlank(prodApi)) {
            imageUrl = imagePath.replace(FileDriveLetterUtils.getDriveLetter() + profile, url + "/profile");
        } else {
            imageUrl = imagePath.replace(profile, url + "/prod-api" + "/profile");
        }
        // 将文件写入目标路径
        file.transferTo(targetLocation.toFile());

        // 3--------------------------开始读取照片的特征值-------------------------------
        ImageInfo imageInfo = null;
        try {
            imageInfo = getRGBData(new File(imagePath));
        } catch (Exception e) {
            // 如果解析失败 直接删除人脸文件
            new File(imagePath).delete();
            Assert.isTrue(false, "人脸文件无法解析！");
        }
        List<FaceInfo> faceInfoList = new ArrayList<>();
        errorCode = faceEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList);
        Assert.isFalse(errorCode != ErrorInfo.MOK.getValue(), "获取人脸失败！");

        //特征提取
        FaceFeature faceFeature = new FaceFeature();
        // 联网激活 3.0
        // errorCode = faceEngine.extractFaceFeature(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList.get(0), faceFeature);
        // 离线激活4.1 / 4.2
        errorCode = faceEngine.extractFaceFeature(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList.get(0), ExtractType.RECOGNIZE, 0, faceFeature);
        Assert.isFalse(errorCode != ErrorInfo.MOK.getValue(), "获取人脸特征值失败！");
        byte[] featureData = faceFeature.getFeatureData();
        // 4------------------------返回解析的数据---------------------
        FaceCutVO faceCutVO = new FaceCutVO();
        faceCutVO.setType("2");
        faceCutVO.setContent(Arrays.toString(featureData));
        faceCutVO.setImageUrl(imageUrl);
        faceCutVO.setImagePath(imagePath);
        return faceCutVO;
    }


    // 相似度门槛
    private static final double THRESHOLD = 0.8; // THRESHOLD越高，错误率越低，阈值[0,1]
    private static final ExecutorService THREAD_POOL_EXECUTOR = Executors.newFixedThreadPool(4); // 线程池

    public static FaceMatchVO completableFutureComparison(final MultipartFile file, final Set<String> matcher) throws IOException {
        int errorCode;
        String prodApi = RuoYiConfig.getProd();
        // 提取当前人脸的特征值,比对文件零时存储,人脸存储基础路径
        String profile = RuoYiConfig.getProfile();
        String basePath = FileDriveLetterUtils.getDriveLetter() + profile + "/face/" + 0 + "/";
        /*if (StringUtils.isBlank(prodApi)) {
            basePath = "C:" + basePath;
        }*/
        // 时间戳
        long currentTimestamp = System.currentTimeMillis();
        String filePath = basePath;
        // 确保目录存在，不存在则创建一个
        Path uploadPath = Paths.get(filePath);
        if (!Files.exists(uploadPath)) {
            Files.createDirectories(uploadPath);
        }
        // 获取文件名并构建目标路径
        String fileName = filePath + currentTimestamp + "_" + file.getOriginalFilename();
        Path targetLocation = uploadPath.resolve(fileName);
        // 将文件写入目标路径
        file.transferTo(targetLocation.toFile());
        try {
            //人脸检测
            ImageInfo imageInfo = getRGBData(new File(fileName));
            List<FaceInfo> faceInfoList = new ArrayList<>();
            errorCode = faceEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList);
            Assert.isFalse(errorCode != ErrorInfo.MOK.getValue(), "未识别到人脸！");
            System.out.println("人脸检测完成---------");
            //特征提取
            FaceFeature faceFeature = new FaceFeature();
            // 联网激活
            // errorCode = faceEngine.extractFaceFeature(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList.get(0), faceFeature);
            // 离线激活
            errorCode = faceEngine.extractFaceFeature(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList.get(0), ExtractType.RECOGNIZE, 0, faceFeature);
            Assert.isFalse(errorCode != ErrorInfo.MOK.getValue(), "获取人脸特征值失败！");
            byte[] featureData = faceFeature.getFeatureData();
            System.out.println("特征提取完成---------");
            // 转成list
            List<String> matcherFeatureDataList = new ArrayList<>(matcher);
            /*FaceFeature targetFaceFeature = new FaceFeature();
            targetFaceFeature.setFeatureData(featureData);
            FaceFeature sourceFaceFeature = new FaceFeature();
            FaceSimilar faceSimilar = new FaceSimilar();
            for (String matcherData : matcherFeatureDataList) {
                // 输入的人脸和人脸库中的人脸进行对比，找到当前线程中的分数最高的人脸
                byte[] matchData = strToBytes(matcherData);
                //特征比对
                sourceFaceFeature.setFeatureData(matchData);
                int errorCode1 = faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar);
                Assert.isFalse(errorCode1 != ErrorInfo.MOK.getValue(), "人脸对比异常" + errorCode1 + "！");
                // 相似度
                float score = faceSimilar.getScore();
                if (score > THRESHOLD) {
                    return new FaceMatchVO().setContent(matcherData).setScore((double) score);
                }
            }*/

            // 切分四等份
            int denominator = 1;
            if (matcherFeatureDataList.size() >= 4) {
                denominator = 4;
            }
            List<List<String>> averageMatcherFeatureDataList = Lists.partition(matcherFeatureDataList, matcherFeatureDataList.size() / denominator);

            // 构建四个线程进行处理，防止人员过多对比速度太慢
            CompletableFuture<FaceMatchVO>[] completableFutureArray = averageMatcherFeatureDataList.stream().map(partitionFeatureData -> CompletableFuture.supplyAsync(() -> comparison(featureData, new ArrayList<>(partitionFeatureData)), THREAD_POOL_EXECUTOR)).toArray(CompletableFuture[]::new);
            System.out.println("四个线程进行处理完成---------");
            // 等待所有任务执行完
            CompletableFuture.allOf(completableFutureArray).join();
            List<FaceMatchVO> verificationList = new ArrayList<>();
            for (CompletableFuture<FaceMatchVO> completableFuture : completableFutureArray) {
                try {
                    FaceMatchVO verification = completableFuture.get();
                    if (ObjectUtil.isNotEmpty(verification)) {
                        verificationList.add(verification);
                    }
                } catch (InterruptedException | ExecutionException e) {
                    log.error(e.getMessage(), e);
                }
            }
            // 找出最匹配的人脸（匹配度最高）
            if (ObjectUtil.isNotEmpty(verificationList)) {
                FaceMatchVO max = verificationList.stream().max(Comparator.comparing(FaceMatchVO::getScore)).get();
                log.info("相似值最佳的的人脸：{}，分数{}", max.getContent(), max.getScore());
                return max;
            }
        } catch (Exception e) {
            log.error("人脸比对异常1：{}", e.getMessage());
        } finally {
            //特征值提取完毕后清除数据
            removeFile(fileName);
        }
        return null;
    }

    public static FaceMatchVO comparison(final byte[] featureData, final List<String> matcherFeatureDataList) {
        try {
            if (!matcherFeatureDataList.isEmpty()) {
                // 最匹配的人脸
                String match = null;
                // 峰值
                double high = 0;
                FaceFeature targetFaceFeature = new FaceFeature();
                targetFaceFeature.setFeatureData(featureData);
                FaceFeature sourceFaceFeature = new FaceFeature();
                FaceSimilar faceSimilar = new FaceSimilar();
                for (String matcherData : matcherFeatureDataList) {
                    // 输入的人脸和人脸库中的人脸进行对比，找到当前线程中的分数最高的人脸
                    byte[] matchData = strToBytes(matcherData);
                    //特征比对
                    sourceFaceFeature.setFeatureData(matchData);
                    int errorCode = faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar);
                    Assert.isFalse(errorCode != ErrorInfo.MOK.getValue(), "人脸对比异常" + errorCode + "！");
                    // 相似度
                    float score = faceSimilar.getScore();
                    if (score > high) {
                        high = score;
                        match = matcherData;
                    }
                }
                return high >= THRESHOLD ? new FaceMatchVO().setContent(match).setScore(high) : null;
            }
        } catch (Exception e) {
            log.error("人脸比对异常2：{}", e.getMessage());
        }
        return null;
    }

    private static byte[] strToBytes(String string) {
        // Remove the brackets and spaces from the string
        string = string.substring(1, string.length() - 1).replaceAll("\\s", "");
        // Split the string by commas to get each number as a string
        String[] numbers = string.split(",");
        // Create a new byte array with the same length as the original
        byte[] newBytes = new byte[numbers.length];
        // Convert each string number back to a byte and store it in the new array
        for (int i = 0; i < numbers.length; i++) {
            newBytes[i] = Byte.parseByte(numbers[i]);
        }
        // Print the new byte array to verify it matches the original
        // System.out.println("New byte array: " + Arrays.toString(newBytes));
        return newBytes;
    }

    private static void removeFile(String path) {
        try {
            File file = new File(path);
            if (!file.delete()) {
                log.error("DAT文件删除失败: " + path);
            }
        } catch (Exception e) {
            log.error("存在异常文件：{}", e);
        }
    }

}
