package com.xxfc.platform.universal.service;

import com.alibaba.fastjson.JSON;
import com.xxfc.platform.universal.dto.CaptchaBaseParam;
import com.xxfc.platform.universal.interceptor.CaptchaException;
import com.xxfc.platform.universal.vo.BlockPuzzleCaptchaVO;
import com.xxfc.platform.universal.vo.CaptchaBaseVO;
import org.apache.commons.lang3.StringUtils;
import sun.misc.BASE64Encoder;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.List;
import java.util.Random;

/**
 * 滑块拼图验证码
 * @author stx
 * @date 2018/11/5 11:12
 * @desc
 */
public class BlockPuzzleCaptcha extends AbstractCaptcha {

    @Override
    public CaptchaBaseVO create(CaptchaBaseParam captchaParam) {
        if (captchaParam == null
                || StringUtils.isBlank(captchaParam.getUrlOrPath())) {
            throw new CaptchaException("参数不正确，captchaParam："+JSON.toJSONString(captchaParam));
        }
        if (StringUtils.isBlank(getJigsawUrlOrPath())){
            throw new CaptchaException("参数不正确，请设置jigsawUrlOrPath 参数");
        }

        //原生图片
        BufferedImage originalImage = getBufferedImage(captchaParam.getUrlOrPath());
        //抠图图片
        BufferedImage   jigsawImage = getBufferedImage(getJigsawUrlOrPath());
        return pictureTemplatesCut(originalImage, jigsawImage);
    }

    @Override
    public boolean verification(List<Point> pointList, List<Point> pointList1) {
        return false;
    }

    /**
     * 根据模板切图
     *
     * @throws Exception
     */
    public static CaptchaBaseVO pictureTemplatesCut(BufferedImage originalImage, BufferedImage jigsawImage){
        try {
            BlockPuzzleCaptchaVO dataVO = new BlockPuzzleCaptchaVO();

            int originalWidth = originalImage.getWidth();
            int originalHeight = originalImage.getHeight();
            int jigsawWidth = jigsawImage.getWidth();
            int jigsawHeight = jigsawImage.getHeight();

            //随机生成拼图坐标
            Point point = generateJigsawPoint(originalWidth, originalHeight, jigsawWidth, jigsawHeight);
            int x = (int)point.getX();
            int y = (int)point.getY();

            //生成新的拼图图像
            BufferedImage newJigsawImage = new BufferedImage(jigsawWidth, jigsawHeight, jigsawImage.getType());
            Graphics2D graphics = newJigsawImage.createGraphics();
            graphics.setBackground(Color.white);

            int bold = 5;
            BufferedImage subImage = originalImage.getSubimage(x, 0, jigsawWidth, jigsawHeight);

            // 获取拼图区域
            newJigsawImage = DealCutPictureByTemplate(subImage, jigsawImage, newJigsawImage);

            // 设置“抗锯齿”的属性
            graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            graphics.setStroke(new BasicStroke(bold, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
            graphics.drawImage(newJigsawImage, 0, 0, null);
            graphics.dispose();

            ByteArrayOutputStream os = new ByteArrayOutputStream();//新建流。
            ImageIO.write(newJigsawImage, IMAGE_TYPE_PNG, os);//利用ImageIO类提供的write方法，将bi以png图片的数据模式写入流。
            byte[] jigsawImages = os.toByteArray();

            // 源图生成遮罩
            byte[] oriCopyImages = DealOriPictureByTemplate(originalImage, jigsawImage, x, 0);

            BASE64Encoder encoder = new BASE64Encoder();
            dataVO.setOriginalImageBase64(encoder.encode(oriCopyImages));
            dataVO.setPoint(point);
            dataVO.setJigsawImageBase64(encoder.encode(jigsawImages));
            return dataVO;
        } catch (Exception e){
            throw new CaptchaException(e);
        }
    }

    /**
     * 抠图后原图生成
     *
     * @param oriImage
     * @param templateImage
     * @param x
     * @param y
     * @return
     * @throws Exception
     */
    private static byte[] DealOriPictureByTemplate(BufferedImage oriImage, BufferedImage templateImage, int x,
                                                   int y) throws Exception {
        // 源文件备份图像矩阵 支持alpha通道的rgb图像
        BufferedImage ori_copy_image = new BufferedImage(oriImage.getWidth(), oriImage.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
        // 源文件图像矩阵
        int[][] oriImageData = getData(oriImage);
        // 模板图像矩阵
        int[][] templateImageData = getData(templateImage);

        //copy 源图做不透明处理
        for (int i = 0; i < oriImageData.length; i++) {
            for (int j = 0; j < oriImageData[0].length; j++) {
                int rgb = oriImage.getRGB(i, j);
                int r = (0xff & rgb);
                int g = (0xff & (rgb >> 8));
                int b = (0xff & (rgb >> 16));
                //无透明处理
                rgb = r + (g << 8) + (b << 16) + (255 << 24);
                ori_copy_image.setRGB(i, j, rgb);
            }
        }

        for (int i = 0; i < templateImageData.length; i++) {
            for (int j = 0; j < templateImageData[0].length - 5; j++) {
                int rgb = templateImage.getRGB(i, j);
                //对源文件备份图像(x+i,y+j)坐标点进行透明处理
                if (rgb != 16777215 && rgb <= 0) {
                    int rgb_ori = ori_copy_image.getRGB(x + i, y + j);
                    int r = (0xff & rgb_ori);
                    int g = (0xff & (rgb_ori >> 8));
                    int b = (0xff & (rgb_ori >> 16));
                    rgb_ori = r + (g << 8) + (b << 16) + (150 << 24);
                    ori_copy_image.setRGB(x + i, y + j, rgb_ori);
                } else {
                    //do nothing
                }
            }
        }
        ByteArrayOutputStream os = new ByteArrayOutputStream();//新建流。
        ImageIO.write(ori_copy_image, "png", os);//利用ImageIO类提供的write方法，将bi以png图片的数据模式写入流。
        byte b[] = os.toByteArray();//从流中获取数据数组。
        return b;
    }


    /**
     * 根据模板图片抠图
     * @param oriImage
     * @param templateImage
     * @param targetImage
     * @return
     * @throws Exception
     */
    private static BufferedImage DealCutPictureByTemplate(BufferedImage oriImage, BufferedImage templateImage,
                                                          BufferedImage targetImage) throws Exception {
        // 源文件图像矩阵
        int[][] oriImageData = getData(oriImage);
        // 模板图像矩阵
        int[][] templateImageData = getData(templateImage);
        // 模板图像宽度

        for (int i = 0; i < templateImageData.length; i++) {
            // 模板图片高度
            for (int j = 0; j < templateImageData[0].length; j++) {
                // 如果模板图像当前像素点不是白色 copy源文件信息到目标图片中
                int rgb = templateImageData[i][j];
                if (rgb != 16777215 && rgb <= 0) {
                    targetImage.setRGB(i, j, oriImageData[i][j]);
                }
            }
        }
        return targetImage;
    }

    /**
     * 生成图像矩阵
     * @param
     * @return
     * @throws Exception
     */
    private static int[][] getData(BufferedImage bimg){
        int[][] data = new int[bimg.getWidth()][bimg.getHeight()];
        for (int i = 0; i < bimg.getWidth(); i++) {
            for (int j = 0; j < bimg.getHeight(); j++) {
                data[i][j] = bimg.getRGB(i, j);
            }
        }
        return data;
    }

    /**
     * 随机生成拼图坐标
     * @param originalWidth
     * @param originalHeight
     * @param jigsawWidth
     * @param jigsawHeight
     * @return
     */
    private static Point generateJigsawPoint(int originalWidth, int originalHeight, int jigsawWidth, int jigsawHeight) {
        Random random = new Random();
        int widthDifference = originalWidth - jigsawWidth;
        int heightDifference = originalHeight - jigsawHeight;
        int x, y = 0;
        if (widthDifference <= 0) {
            x = 5;
        } else {
            x = random.nextInt(originalWidth - jigsawWidth) + 5;
        }
        if (heightDifference <= 0) {
            y = 5;
        } else {
            y = random.nextInt(originalHeight - jigsawHeight) + 5;
        }
        return new Point(x, y);
    }
}
