package cn.palmte.work.utils.image;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.BASE64Decoder;
import top.jfunc.common.utils.ArrayUtil;
import top.jfunc.common.utils.RandomUtil;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Random;

/**
 * 滑动验证码相关工具方法
 */
public class VerifyImageUtil {
    private static final Logger logger = LoggerFactory.getLogger(VerifyImageUtil.class);
    /**
     * 模板图宽度
     */
    private static int CUT_WIDTH = 50;
    /**
     * 模板图高度
     */
    private static int CUT_HEIGHT = 50;
    /**
     * 抠图凸起圆心
     */
    private static int CIRCLE_R = 5;
    /**
     * 抠图内部矩形填充大小
     */
    private static int RECTANGLE_PADDING = 8;
    /**
     * 抠图的边框宽度
     */
    private static int SLIDER_IMG_OUT_PADDING = 1;

    private static int CENTER_INDICATOR = 1;
    private static int BORDER_INDICATOR = 2;
    private static int OTHER_INDICATOR  = 0;

    /**
     * 根据传入的file生成验证码图片
     */
    public static VerifyImage getVerifyImage(File file) throws IOException {
        BufferedImage srcImage = ImageIO.read(file);

        return getVerifyImage(srcImage);
    }

    /**
     * 根据转入的流生成验证码图片
     */
    public static VerifyImage getVerifyImage(InputStream inputStream) throws IOException {
        BufferedImage srcImage = ImageIO.read(inputStream);

        return getVerifyImage(srcImage);
    }

    /**
     * 以左上角为计数起点
     */
    public static VerifyImage getVerifyImage(BufferedImage srcImage) throws IOException {
        //1/4=75处，最右减1个方块儿，-5是保守的防止超界
        int locationX = RandomUtil.randomInt(srcImage.getWidth()/4, srcImage.getWidth()-CUT_WIDTH-5);
        //距离最下方一个方块，-5是保守的防止超界
        int locationY = RandomUtil.randomInt(5, srcImage.getHeight()-CUT_HEIGHT-5);

        BufferedImage markImage = new BufferedImage(CUT_WIDTH,CUT_HEIGHT,BufferedImage.TYPE_4BYTE_ABGR);
        cutImgByTemplate(srcImage, markImage, getBlockData(), locationX, locationY);
        return new VerifyImage(getImageBASE64(srcImage),
                getImageBASE64(markImage),
                locationX,
                locationY,
                srcImage.getWidth(),
                srcImage.getHeight());
    }


    /**
     * 生成随机滑块形状
     * <p>
     * 0 透明像素
     * 1 滑块像素
     * 2 阴影像素
     * @return int[][]
     */
    private static int[][] getBlockData() {
        int[][] data = new int[CUT_WIDTH][CUT_HEIGHT];
        SecureRandom random = new SecureRandom();
        //Random random = new Random();
        //(x-a)²+(y-b)²=r²
        //x中心位置左右5像素随机
        double x1 = RECTANGLE_PADDING + (CUT_WIDTH - 2 * RECTANGLE_PADDING) / 2.0/* - 5 + random.nextInt(10)*/;
        //y 矩形上边界半径-1像素移动
        double y1_top = RECTANGLE_PADDING - random.nextInt(3);
        double y1_bottom = CUT_HEIGHT - RECTANGLE_PADDING + random.nextInt(3);
        double y1 = random.nextInt(2) == 1 ? y1_top : y1_bottom;
 
 
        double x2_right = CUT_WIDTH - RECTANGLE_PADDING - CIRCLE_R + random.nextInt(2 * CIRCLE_R - 4);
        double x2_left = RECTANGLE_PADDING + CIRCLE_R - 2 - random.nextInt(2 * CIRCLE_R - 4);
        double x2 = random.nextInt(2) == 1 ? x2_right : x2_left;
        double y2 = RECTANGLE_PADDING + (CUT_HEIGHT - 2 * RECTANGLE_PADDING) / 2.0 - 4 + random.nextInt(10);
 
        double po = Math.pow(CIRCLE_R, 2);
        for (int i = 0; i < CUT_WIDTH; i++) {
            for (int j = 0; j < CUT_HEIGHT; j++) {
                //矩形区域
                boolean fill;
                if ((i >= RECTANGLE_PADDING && i < CUT_WIDTH - RECTANGLE_PADDING)
                        && (j >= RECTANGLE_PADDING && j < CUT_HEIGHT - RECTANGLE_PADDING)) {
                    data[i][j] = CENTER_INDICATOR;
                    fill = true;
                } else {
                    data[i][j] = OTHER_INDICATOR;
                    fill = false;
                }
                //凸出区域
                double d3 = Math.pow(i - x1, 2) + Math.pow(j - y1, 2);
                if (d3 < po) {
                    data[i][j] = CENTER_INDICATOR;
                } else {
                    if (!fill) {
                        data[i][j] = OTHER_INDICATOR;
                    }
                }
                //凹进区域
                double d4 = Math.pow(i - x2, 2) + Math.pow(j - y2, 2);
                if (d4 < po) {
                    data[i][j] = OTHER_INDICATOR;
                }
            }
        }
        //边界阴影
        for (int i = 0; i < CUT_WIDTH; i++) {
            for (int j = 0; j < CUT_HEIGHT; j++) {
                //四个正方形边角处理
                for (int k = 1; k <= SLIDER_IMG_OUT_PADDING; k++) {
                    //左上、右上
                    if (i >= RECTANGLE_PADDING - k && i < RECTANGLE_PADDING
                            && ((j >= RECTANGLE_PADDING - k && j < RECTANGLE_PADDING)
                            || (j >= CUT_HEIGHT - RECTANGLE_PADDING - k && j < CUT_HEIGHT - RECTANGLE_PADDING +1))) {
                        data[i][j] = BORDER_INDICATOR;
                    }
 
                    //左下、右下
                    if (i >= CUT_WIDTH - RECTANGLE_PADDING + k - 1 && i < CUT_WIDTH - RECTANGLE_PADDING + 1) {
                        for (int n = 1; n <= SLIDER_IMG_OUT_PADDING; n++) {
                            if (((j >= RECTANGLE_PADDING - n && j < RECTANGLE_PADDING)
                                    || (j >= CUT_HEIGHT - RECTANGLE_PADDING - n && j <= CUT_HEIGHT - RECTANGLE_PADDING ))) {
                                data[i][j] = BORDER_INDICATOR;
                            }
                        }
                    }
                }
 
                if (data[i][j] == 1 && j - SLIDER_IMG_OUT_PADDING > 0 && data[i][j - SLIDER_IMG_OUT_PADDING] == 0) {
                    data[i][j - SLIDER_IMG_OUT_PADDING] = BORDER_INDICATOR;
                }
                if (data[i][j] == 1 && j + SLIDER_IMG_OUT_PADDING > 0 && j + SLIDER_IMG_OUT_PADDING < CUT_HEIGHT && data[i][j + SLIDER_IMG_OUT_PADDING] == 0) {
                    data[i][j + SLIDER_IMG_OUT_PADDING] = BORDER_INDICATOR;
                }
                if (data[i][j] == 1 && i - SLIDER_IMG_OUT_PADDING > 0 && data[i - SLIDER_IMG_OUT_PADDING][j] == 0) {
                    data[i - SLIDER_IMG_OUT_PADDING][j] = BORDER_INDICATOR;
                }
                if (data[i][j] == 1 && i + SLIDER_IMG_OUT_PADDING > 0 && i + SLIDER_IMG_OUT_PADDING < CUT_WIDTH && data[i + SLIDER_IMG_OUT_PADDING][j] == 0) {
                    data[i + SLIDER_IMG_OUT_PADDING][j] = BORDER_INDICATOR;
                }
            }
        }


        //最外边界
        /*for (int i = 0; i < CUT_WIDTH; i++) {
            data[0][i] = data[CUT_WIDTH-1][i] = 3;
        }
        for (int i = 0; i < CUT_HEIGHT; i++) {
            data[i][0] = data[i][CUT_HEIGHT-1] = 3;
        }*/

        return data;
    }
 
    /**
     * 裁剪区块
     * 根据生成的滑块形状，对原图和裁剪块进行变色处理
     * @param oriImage    原图
     * @param targetImage 裁剪图
     * @param blockImage  滑块
     * @param x           裁剪点x
     * @param y           裁剪点y
     */
    private static void cutImgByTemplate(BufferedImage oriImage, BufferedImage targetImage, int[][] blockImage, int x, int y) {

        print(blockImage);

        for (int i = 0; i < CUT_WIDTH; i++) {
            for (int j = 0; j < CUT_HEIGHT; j++) {
                int xx = x + i;
                int yy = y + j;
                int rgbFlg = blockImage[i][j];
                int rgb_ori = oriImage.getRGB(xx, yy);

                // 原图中对应位置变色处理
                if (rgbFlg == CENTER_INDICATOR) {
                    //中心
                    //抠图上复制对应颜色值
                    targetImage.setRGB(i,j, rgb_ori);
                    //原图对应位置颜色变化[透明50%]
                    oriImage.setRGB(xx, yy, rgb_ori & 0x80ffffff);
                } else if (rgbFlg == BORDER_INDICATOR) {
                    //边框
                    targetImage.setRGB(i, j, Color.WHITE.getRGB());
                    oriImage.setRGB(xx, yy, Color.GRAY.getRGB());
                }else if(rgbFlg == OTHER_INDICATOR){
                    //int alpha = 0;
                    targetImage.setRGB(i, j, rgb_ori & 0x00ffffff);
                }
            }
 
        }
    }
    private static void print(int[][] blockData){
        StringBuilder builder = new StringBuilder("\r\n");
        for (int i = 0; i < blockData.length; i++) {
            for (int j = 0; j < blockData[i].length; j++) {
                builder.append(blockData[i][j]).append(" ");
            }
            builder.append("\r\n");
        }
        logger.debug(builder.toString());
    }
 
 
    /**
     * 随机获取一张图片文件
     */
    public static File getRandomFile(File dir) throws IOException {
        File[] fileList = dir.listFiles();
        if(ArrayUtil.isEmpty(fileList)){
            return null;
        }
        List<String> fileNameList = new ArrayList<>(fileList.length);
        for (File tempFile: fileList){
            if (tempFile.isDirectory()) {
                continue;
            }
            if (tempFile.getName().endsWith(".png") || tempFile.getName().endsWith(".jpg")){
                fileNameList.add(tempFile.getAbsolutePath().trim());
            }
        }
        int randomIndex = new SecureRandom().nextInt(fileNameList.size());
        //int randomIndex = new Random().nextInt(fileNameList.size());
        return new File(fileNameList.get(randomIndex));
    }
 
    /**
     * 将IMG输出为文件
     */
    public static void writeImg(BufferedImage image, File file) throws IOException {
        ByteArrayOutputStream bao=new ByteArrayOutputStream();
        ImageIO.write(image,"png",bao);
        try (FileOutputStream out = new FileOutputStream(file)){
            out.write(bao.toByteArray());
        }
    }
 
    /**
     * 将图片转换为BASE64
     */
    public static String getImageBASE64(BufferedImage image) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ImageIO.write(image,"png",out);
        //转成byte数组
        byte[] bytes = out.toByteArray();

        return Base64.getEncoder().encodeToString(bytes);
    }
 
    /**
     * 将BASE64字符串转换为图片
     */
    public static BufferedImage base64String2Image(String base64String) throws IOException {
        BASE64Decoder decoder=new BASE64Decoder();
        byte[] bytes1 = decoder.decodeBuffer(base64String);
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes1);
        return ImageIO.read(byteArrayInputStream);
    }

    /*public static void main(String[] args) throws IOException {
        VerifyImage verifyImage = VerifyImageUtil.getVerifyImage(new File("C:\\Users\\xiongshiyan\\Desktop\\ssss.png"));
        System.out.println(verifyImage);
        VerifyImageUtil.writeImg(VerifyImageUtil.base64String2Image(verifyImage.getSrcImage()), new File("C:\\Users\\xiongshiyan\\Desktop\\src.png"));
        VerifyImageUtil.writeImg(VerifyImageUtil.base64String2Image(verifyImage.getCutImage()), new File("C:\\Users\\xiongshiyan\\Desktop\\cut.png"));
    }*/
 
}