package com.bonus.canteen.core.user.utils;

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.ImageFormat;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.imageio.ImageIO;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.awt.image.DataBufferByte;
import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

/**
 * 人脸识别工具类
 * @author zys
 */
@Component
@Slf4j
public class ArcFaceHelper {

    @Value("${face.arc-face.app-id}")
    private String APP_ID;

    /**
     * linux_64
     */
    @Value("${face.arc-face.linux64-sdk-key}")
    private String LINUX64_SDK_KEY;

    @Value("${face.arc-face.face-engine-path}")
    private String FACE_ENGINE_PATH;

    private static String appId;
    private static String linuxKey;

    private static String faceEnginePath;

    @PostConstruct
    public void init() {
        appId = this.APP_ID;
        linuxKey = this.LINUX64_SDK_KEY;
        faceEnginePath = this.FACE_ENGINE_PATH;
    }

    /**
     * x64
     */
    private static final String WIN64_SDK_KEY = "3TMzo8nmWDka6NJDrbX7e2y1ew4QgSia4iGBniyWU4qq";

	
	private static FaceEngine faceEngine = null;
	
	public static final float STANDARD_SCORE = 0.85F;

//	static {
//        int errorCode = 0;
//		try{
//            String os = System.getProperty("os.name");
//            if(StringUtils.startsWith(os.toLowerCase(), "win")){
//                faceEngine = new FaceEngine("D:\\arcsoft_lib_64");
//                //激活引擎
//                int code = faceEngine.activeOnline(APP_ID, WIN64_SDK_KEY);
//                log.info("引擎激活code:" + code);
//            } else {
//                faceEngine = new FaceEngine(faceEnginePath);
//                //激活引擎
//                errorCode = faceEngine.activeOnline(APP_ID, LINUX64_SDK_KEY);
//            }
//            log.info("引擎激活code:" + errorCode);
//        }catch (Throwable e){
//            log.info("加载人脸识别引擎异常：" + e.getMessage());
//        }
//        if (errorCode != ErrorInfo.MOK.getValue() && errorCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue()) {
//            log.info("引擎激活失败");
//        }
//	}
	
	public static FaceEngine getFaceEngine() {
		return faceEngine;
	}
	
	public FaceResult getFaceFeatures(String imgSrc) {
        log.info("-----getFaceFeatures");
        if(imgSrc.contains("http")){
            log.info("-----imgSrc.contains(http)");
            return getFaceFeatures(getNetUrlHttp(imgSrc));
        }else{
            return getFaceFeatures(new File(imgSrc));
        }
	}
    public FaceResult getFaceFeatures2(String imgSrc) {
        if(imgSrc.contains("http")){
            return getFaceFeatures(getNetUrlHttp(imgSrc));
        }else{
            return getFaceFeatures(new File(imgSrc));
        }
    }
	public static FaceResult getFaceFeatures(File file) {
        log.info("-----开始识别");

        int errorCode = 0;
        try{
            String os = System.getProperty("os.name");
            if(StringUtils.startsWith(os.toLowerCase(), "win")){
                faceEngine = new FaceEngine("D:\\arcsoft_lib_64");
                //激活引擎
                faceEngine.activeOnline(appId, WIN64_SDK_KEY);
            } else {
                log.info("配置文件加载结果: faceEnginePath=" + faceEnginePath + ",appId=" + appId + ",linuxKey=" + linuxKey);
                faceEngine = new FaceEngine(faceEnginePath);
                //激活引擎
                errorCode = faceEngine.activeOnline(appId, linuxKey);
            }
            log.info("引擎激活code:" + errorCode);
        }catch (Throwable e){
            log.info("加载人脸识别引擎异常：" + e.getMessage());
        }
        if (errorCode != ErrorInfo.MOK.getValue() && errorCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue()) {
            log.info("引擎激活失败");
        }


        ImageInfoTwo imageInfo = getRGBDataTwo(file);
		if (imageInfo == null){
            faceEngine.unInit();
            return null;
        }
		EngineConfiguration engineConfiguration = EngineConfiguration.builder().functionConfiguration(
                FunctionConfiguration.builder()
                		.supportFaceDetect(true)
                        .supportAge(true)
                        .supportFace3dAngle(true)
                        .supportFaceDetect(true)
                        .supportFaceRecognition(true)
                        .supportGender(true)
                        .build()).build();
        //初始化引擎
        int faceEngineInitCode = faceEngine.init(engineConfiguration);
        if (faceEngineInitCode != ErrorInfo.MOK.getValue()){
            log.info("初始化引擎失败" + faceEngineInitCode);
        }
        //人脸检测
        List<FaceInfo> lstFaceInfo = new ArrayList<FaceInfo>();
        faceEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(),
        		ImageFormat.CP_PAF_BGR24, lstFaceInfo);
        if (lstFaceInfo.size() == 0) {
            faceEngine.unInit();
            return null;
//            throw new NullPointerException("未检测到人脸");
		}
        FaceResult result = new FaceResult();
        //提取人脸特征
        FaceFeature faceFeature = new FaceFeature();
        int code = faceEngine.extractFaceFeature(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(),
        		ImageFormat.CP_PAF_BGR24, lstFaceInfo.get(0), faceFeature);
        result.setFeatures(faceFeature.getFeatureData());
        //人脸信息检测
        int processCode = faceEngine.process(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(),
        		ImageFormat.CP_PAF_BGR24, lstFaceInfo,
        		FunctionConfiguration.builder().supportAge(true).supportFace3dAngle(true).supportGender(true).build());
        if(code != 0 || processCode != 0){
            faceEngine.unInit();
            return null;
//            throw new IllegalArgumentException("提取特征失败");
        }
        //性别提取
        List<GenderInfo> genderInfoList = new ArrayList<GenderInfo>();
        int genderCode = faceEngine.getGender(genderInfoList);
        //0:MALE;1:FEMALE;-1:UNKNOWN
        result.setSex(genderInfoList.get(0).getGender());
        //年龄提取
        List<AgeInfo> ageInfoList = new ArrayList<AgeInfo>();
        int ageCode = faceEngine.getAge(ageInfoList);
        result.setAge(ageInfoList.get(0).getAge());
        //3D信息提取
        List<Face3DAngle> face3DAngleList = new ArrayList<Face3DAngle>();
        int face3dCode = faceEngine.getFace3DAngle(face3DAngleList);
        faceEngine.unInit();
        return result;
	}

	public static void unInintEngine() {
		int code = faceEngine.unInit();
		log.info("销毁引擎：" + code);
	}
	
	public static ImageInfo getRGBData(File file) {
        if (file == null){
            return null;
        }
        ImageInfo imageInfo;
        try {
            //将图片文件加载到内存缓冲区
            BufferedImage image = ImageIO.read(file);
            imageInfo = bufferedImage2ImageInfo(image);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        return imageInfo;
    }

    private static ImageInfo bufferedImage2ImageInfo(BufferedImage image) {
        ImageInfo imageInfo = new ImageInfo();
        int width = image.getWidth();
        int height = image.getHeight();
        // 使图片居中
        width = width & (~3);
        height = height & (~3);
        imageInfo.setWidth(width);
        imageInfo.setHeight(height);
        //根据原图片信息新建一个图片缓冲区
        BufferedImage resultImage = new BufferedImage(width, height, image.getType());
        //得到原图的rgb像素矩阵
        int[] rgb = image.getRGB(0, 0, width, height, null, 0, width);
        //将像素矩阵 绘制到新的图片缓冲区中
        resultImage.setRGB(0, 0, width, height, rgb, 0, width);
        //进行数据格式化为可用数据
        BufferedImage dstImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
        try {
        	 if (resultImage.getType() != BufferedImage.TYPE_3BYTE_BGR) {
                 ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
                 ColorConvertOp colorConvertOp = new ColorConvertOp(cs, dstImage.createGraphics().getRenderingHints());
                 colorConvertOp.filter(resultImage, dstImage);
             } else {
                 dstImage = resultImage;
             }
		} catch (Exception e) {
			// TODO: handle exception
			System.err.println(e.toString());
		}
       
        //获取rgb数据
        imageInfo.setRgbData(((DataBufferByte) (dstImage.getRaster().getDataBuffer())).getData());
        return imageInfo;
    }

    private static File getNetUrlHttp(String path){
        //对本地文件命名，path是http的完整路径，主要得到资源的名字
        String newUrl = path;
        newUrl = newUrl.split("[?]")[0];
        String[] bb = newUrl.split("/");
        //得到最后一个分隔符后的名字
        String fileName = bb[bb.length - 1];

        //保存到本地的路径
        String filePath;
        String os = System.getProperty("os.name");
        if(StringUtils.startsWith(os.toLowerCase(), "win")){
            filePath = "D:\\images\\"+fileName;
        } else {
            filePath = "/data/userFace/faceDetection/"+fileName;
        }

        File file = null;

        URL urlfile;
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try{
            //判断文件的父级目录是否存在，不存在则创建
            file = new File(filePath);
            if(!file.getParentFile().exists()){
                file.getParentFile().mkdirs();
            }
            try{
                //创建文件
                file.createNewFile();
            }catch (Exception e){
                e.printStackTrace();
            }
            //下载
            urlfile = new URL(newUrl);
            inputStream = urlfile.openStream();
            outputStream = new FileOutputStream(file);

            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead=inputStream.read(buffer,0,8192))!=-1) {
                outputStream.write(buffer, 0, bytesRead);
            }
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if (null != outputStream) {
                    outputStream.close();
                }
                if (null != inputStream) {
                    inputStream.close();
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return file;
    }


public static ImageInfoTwo getRGBDataTwo(File file) {
        if (file == null){
            return null;
        }
        ImageInfoTwo imageInfo = new ImageInfoTwo();
        try {
            //将图片文件加载到内存缓冲区
            BufferedImage image = ImageIO.read(file);
            imageInfo = bufferedImage2ImageInfoTwo(image);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        return imageInfo;
    }

    public static ImageInfoTwo bufferedImage2ImageInfoTwo(BufferedImage image) {
        ImageInfoTwo imageInfo = new ImageInfoTwo();
        int width = image.getWidth();
        int height = image.getHeight();
        width &= -4;
        height &= -4;
        imageInfo.setWidth(width);
        imageInfo.setHeight(height);
        BufferedImage resultImage = new BufferedImage(width, height, image.getType());
        int[] rgb = image.getRGB(0, 0, width, height, (int[])null, 0, width);
        resultImage.setRGB(0, 0, width, height, rgb, 0, width);
        BufferedImage dstImage = new BufferedImage(width, height, 5);
        if (resultImage.getType() != 5) {
            ColorSpace cs = ColorSpace.getInstance(1004);
            ColorConvertOp colorConvertOp = new ColorConvertOp(cs, dstImage.createGraphics().getRenderingHints());
            colorConvertOp.filter(resultImage, dstImage);
        } else {
            dstImage = resultImage;
        }

        imageInfo.setImageFormat(ImageFormat.CP_PAF_BGR24);
        imageInfo.setImageData(((DataBufferByte)((DataBufferByte)dstImage.getRaster().getDataBuffer())).getData());
        return imageInfo;
    }
}
