package cn.iocoder.yudao.framework.common.util.qrcode;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import org.apache.commons.lang3.StringUtils;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 二维码相关工具类
 *
 * @author yellow duck
 * @date 2022-04-08 14:34
 */
public class QrCodeUtil {
    /**
     * 二维码宽和高
     */
//    private static final int WIDTH_AND_HEIGHT = 570;
//
//    private static final int WIDTH_AND_WIDTH = 780;

    // 二维码的宽度
    private static final int QRCODE_WIDTH = 600;
    // 二维码的宽度
    private static final int QRCODE_HEIGHT = 600;
    // 二维码与文字之间的间距
    private static final int PADDING = 20;
    // 文字的最大长度
    private static final int MAX_TEXT_LENGTH = 24;
    /**
     * 标题的Y轴坐标
     */
    private static int TOPIT_Y = 563;
    /**
     * 文字的X轴坐标
     */
    private static final int TEXT_X = 35;
    /**
     * 文字的Y轴坐标
     */
    private static int TEXT_Y = 35;
    /**
     * 文字的段落高度
     */
    private static final int SPACE_Y = 35;
    /**
     * 二维码参数设置
     */
    private static final Map<EncodeHintType, Object> HINTS;

    static {
        HINTS = new EnumMap<>(EncodeHintType.class);
        //设置编码
        HINTS.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        //设置二维码的纠错级别
        HINTS.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        HINTS.put(EncodeHintType.MARGIN, 2);
    }

    /**
     * 绘制二维码并返回图片流
     * @param contents 二维码内容
     * @param text 二维码下方的文字，传空将不写入文字
     * @return 二维码图片流
     * @throws WriterException
     */
    public static BufferedImage createQrCode(String contents, String text) throws WriterException {

        QRCodeWriter qrCodeWriter = new QRCodeWriter();
        int qrCodeWidth = QRCODE_WIDTH;
        int qrCodeHeight = QRCODE_HEIGHT;
        // 创建一个空白的BufferedImage对象作为画布
        BufferedImage image = new BufferedImage(qrCodeWidth, qrCodeHeight + PADDING, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics = (Graphics2D) image.getGraphics();
        // 填充画布为白色
        graphics.setColor(Color.WHITE);
        graphics.fillRect(0, 0, qrCodeWidth, qrCodeHeight + PADDING);
        if (StringUtils.isNotBlank(text)) {
            // 绘制文字到画布
            graphics.setFont(new Font("Arial", Font.BOLD, 40));
            graphics.setColor(Color.BLACK);
            FontRenderContext fontRenderContext = graphics.getFontRenderContext();
            TextLayout textLayout = new TextLayout(text, graphics.getFont(), fontRenderContext);
            Rectangle textBounds = textLayout.getBounds().getBounds();
            int textX = (qrCodeWidth - textBounds.width) / 2;
            int textY = qrCodeHeight + PADDING + (PADDING - textBounds.height) / 2;
            textLayout.draw(graphics, textX, textY);
        }

        graphics.dispose();

        // 生成二维码并绘制到画布
        BitMatrix bitMatrix = qrCodeWriter.encode(contents, BarcodeFormat.QR_CODE, qrCodeWidth, qrCodeHeight, HINTS);
        for (int x = 0; x < qrCodeWidth; x++) {
            for (int y = 0; y < qrCodeHeight; y++) {
                if (bitMatrix.get(x, y)) {
                    image.setRGB(x, y, Color.BLACK.getRGB());
                }
            }
        }

        return image;
    }


    /**
     * 把生成的二维码打进压缩包并下载
     *
     * @param images: 二维码流集合
     * @date 2022/4/8 15:26
     * @author YD
     */
    public static void compressQrCodeToZip(List<BufferedImage> images, ByteArrayOutputStream os) throws IOException {
        ZipOutputStream zos = new ZipOutputStream(os);
        //文件的名称
        String downloadFilename = "二维码压缩包.zip";
        //转换中文否则可能会产生乱码
        downloadFilename = URLEncoder.encode(downloadFilename, "UTF-8");
        //添加二维码进zip压缩包
        images.forEach(
            image -> {
                try {
                    byte[] buf = new byte[8192];
                    int len;
                    //添加二维码进zip
                    ZipEntry entry = new ZipEntry(UUID.randomUUID() + ".png");
                    zos.putNextEntry(entry);
                    InputStream inputStream = bufferedImageToInputStream(image);
                    //输出压缩包
                    while ((len = inputStream.read(buf)) > 0) {
                        zos.write(buf, 0, len);
                    }
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        );
        zos.close();
        os.close();
    }

    /**
     * 将BufferedImage转换为InputStream
     *
     * @param image: 图片流
     * @date 2022/4/8 15:29
     * @author YD
     */
    public static InputStream bufferedImageToInputStream(BufferedImage image) throws IOException {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ImageIO.write(image, "png", os);
        try (InputStream input = new ByteArrayInputStream(os.toByteArray())) {
            return input;
        }
    }

}
