package com.yinlihupo.enlish.service.utils;

import com.yinlihupo.enlish.service.constant.ExamWordsConstant;
import com.yinlihupo.enlish.service.model.bo.CoordinatesXY;
import com.yinlihupo.enlish.service.model.bo.StudentExamId;
import lombok.extern.slf4j.Slf4j;
import net.sourceforge.tess4j.ITesseract;
import net.sourceforge.tess4j.Tesseract;
import net.sourceforge.tess4j.TesseractException;
import nu.pattern.OpenCV;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class PngUtil {

    static {
        OpenCV.loadLocally();
    }

    // 获取起始坐标
    public static List<CoordinatesXY> analysisXY(String imagePath) {

        Mat src = Imgcodecs.imread(imagePath);

        if (src.empty()) {
            System.out.println("无法读取图片，请检查路径。");
            return null;
        }

        // 3. 预处理
        // 3.1 转换为灰度图
        Mat gray = new Mat();
        Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);

        // 3.2 二值化处理 (Thresholding)
        // 使用 THRESH_BINARY_INV (反转二值化)，因为我们需要找的是白色背景上的黑色块。
        // 反转后，黑色块变成白色(255)，背景变成黑色(0)，方便 findContours 查找。
        Mat binary = new Mat();
        // 阈值设为 50 左右即可，因为块是纯黑的
        Imgproc.threshold(gray, binary, 80, 255, Imgproc.THRESH_BINARY_INV);

        // 4. 查找轮廓
        List<MatOfPoint> contours = new ArrayList<>();
        Mat hierarchy = new Mat();
        // RETR_EXTERNAL 只检测最外层轮廓，忽略块内部可能存在的噪点
        Imgproc.findContours(binary, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
        //Imgcodecs.imwrite("output_red___v1.png", binary);
        System.out.println("检测到的轮廓总数: " + contours.size());
        System.out.println("------------------------------------------------");

        // 5. 遍历并筛选轮廓
        int blockCount = 0;

        // 设定最小面积阈值，过滤掉文字、表格细线等噪点
        // 根据图片分辨率，这两个块看起来比较大，我们可以设一个较大的值，例如 500 或 1000 像素
        double minArea = 1000.0;

        List<CoordinatesXY> list = new ArrayList<>();
        for (int i = 0; i < contours.size(); i++) {
            MatOfPoint contour = contours.get(i);

            // 计算轮廓面积
            double area = Imgproc.contourArea(contour);

            // 获取边界矩形
            Rect rect = Imgproc.boundingRect(contour);
            // 筛选条件：
            // 1. 面积必须足够大 (排除文字)
            // 2. 宽高比不能太极端 (排除长长的表格横线或竖线)
            // 3. 宽度和高度必须有一定的尺寸
            if (area > minArea && rect.width > 20 && rect.height > 20) {

                blockCount++;
                System.out.println("找到黑色块 #" + blockCount);
                System.out.println("坐标 (X, Y): (" + rect.x + ", " + rect.y + ")");
                System.out.println("尺寸 (宽 x 高): " + rect.width + " x " + rect.height);
                System.out.println("中心点: (" + (rect.x + rect.width / 2) + ", " + (rect.y + rect.height / 2) + ")");
                System.out.println("------------------------------------------------");
                list.add(CoordinatesXY.builder().x(rect.x).y(rect.y).width(rect.width).height(rect.height).build());
                // 可选：在原图上画出框，用于调试验证
                Imgproc.rectangle(src, rect, new Scalar(0, 0, 255), 2); // 红色框
                Imgproc.putText(src, "#" + blockCount, new Point(rect.x, rect.y - 5), Imgproc.FONT_HERSHEY_SIMPLEX, 0.5, new Scalar(0, 0, 255), 1);
                //Imgcodecs.imwrite("output_red.png", src);
            }
        }
        System.out.println("找到 " + blockCount + " 个黑色块。");


        // 计算起始坐标
        list.sort(Comparator.comparingInt(CoordinatesXY::getX));

        return list;
    }

    // 获取（未背熟）单词的 id
    public static List<Integer> analyzePngForUnmemorizedWordIds(String filePath, List<Integer> wordIds, List<CoordinatesXY> coordinatesXYList) {

        Mat src = Imgcodecs.imread(filePath);
        if (src.empty()) {
            log.error("无法读取图片，请检查路径: {}", filePath);
            throw new RuntimeException("无法读取图片");
        }

        Mat gray = new Mat();
        Mat binary = new Mat();

        try {
            Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
            // 建议：如果光照不均匀，考虑使用 THRESH_OTSU 自动阈值，或者自适应阈值
            Imgproc.threshold(gray, binary, 150, 255, Imgproc.THRESH_BINARY_INV);
//            调试时打印
            //Imgcodecs.imwrite("output_binary.png", binary);
            List<Integer> answer = new ArrayList<>();
            int words_index = 0;

            for (CoordinatesXY coordinatesXY : coordinatesXYList) {
                int width = coordinatesXY.getWidth();
                int height = coordinatesXY.getHeight();
                int currentX = coordinatesXY.getX();
                int currentY = coordinatesXY.getY() + height;

                // 内层循环：遍历这一列的每一行
                for (int j = 0; j < 50; j++) {
                    // 安全检查：防止单词列表比格子少导致越界
                    if (words_index >= wordIds.size()) {
                        log.warn("单词列表耗尽，停止检测。格子数多于单词数。");
                        break;
                    }
                    log.info("当前坐标 x = {} y = {}", currentX, currentY);
                    // 安全检查：防止 Rect 超出图片边界
                    if (currentX < 0 || currentY < 0 ||
                            currentX + width > binary.cols() || currentY + height > binary.rows()) {
                        log.warn("检测区域越界，跳过: x={}, y={}", currentX, currentY);
                        words_index++;
                        currentY += height; // 依然要移动坐标
                        continue;
                    }

                    Rect rect = new Rect(currentX + 1, currentY + 1, width - 2, height - 2);
                    Mat region = binary.submat(rect);
                    int countNonZero = Core.countNonZero(region);

                    if (countNonZero > 370) {
                        Integer id = wordIds.get(words_index);
                        answer.add(id);
                        log.info("检测到标记（未背熟）：ID={}, 当前坐标 x = {} y = {} ", id, currentX + 1, currentY + 1);
                    }
                    if (countNonZero == 0) {
                        break;
                    }

                    region.release();
                    words_index++;
                    currentY += height;
                }
            }

            return answer;

        } finally {

            src.release();
            gray.release();
            binary.release();
        }
    }

    public static StudentExamId analyzeExamWordsIdAndStudentId(String imagePath, String tessdataPath, List<CoordinatesXY> coordinatesXIES) {
        // 1. 读取图片
        Mat src = Imgcodecs.imread(imagePath);
        if (src.empty()) {
            System.out.println("无法加载图片");
            return null;
        }

        // 2. 截取左上角区域 (ROI)
        // 根据图片大概估算：从 (0,0) 开始，宽约 300像素，高约 80像素
        // 你可以根据实际情况调整这个范围
        CoordinatesXY left = coordinatesXIES.get(0);
        Rect roiRect = new Rect(0, 0, left.getX(), left.getY());
        Mat roi = new Mat(src, roiRect);

        // 3. 图像预处理 (提高 OCR 准确率)
        // 3.1 转为灰度图
        Mat gray = new Mat();
        Imgproc.cvtColor(roi, gray, Imgproc.COLOR_BGR2GRAY);

        // 3.2 二值化 (Thresholding)
        // 使用 OTSU 算法自动寻找最佳阈值，或者手动指定阈值
        Mat binary = new Mat();
        Imgproc.threshold(gray, binary, 0, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);

        // 可选：保存预处理后的图片查看效果
        //Imgcodecs.imwrite("debug_roi.jpg", binary);

        // 4. 将 OpenCV Mat 转换为 BufferedImage (供 Tess4J 使用)
        BufferedImage processedImage = matToBufferedImage(binary);

        // 5. 使用 Tesseract 进行 OCR 识别
        ITesseract instance = new Tesseract();
        instance.setDatapath(tessdataPath);
        instance.setLanguage("eng");

        try {
            String result = instance.doOCR(processedImage);
            System.out.println("OCR 识别原始内容: \n" + result);

            // 6. 使用正则表达式提取 ID
            // 匹配 "Assessment_id" 后面的数字
            Pattern pattern = Pattern.compile("\\d+");
            StudentExamId studentExamId = getStudentExamId(pattern, result);
            if (studentExamId.getExamId() != 0 && studentExamId.getStudentId() != 0) {
                return studentExamId;
            }

        } catch (TesseractException e) {
            System.err.println("OCR 识别出错: " + e.getMessage());
        }
        return null;
    }

    private static @NonNull StudentExamId getStudentExamId(Pattern pattern, String result) {
        Matcher matcher = pattern.matcher(result);
        StudentExamId studentExamId = new StudentExamId(0, 0);
        for (int i = 0; i < 2; i++) {
            if (matcher.find()) {
                String group = matcher.group();
                if (i == 0) {
                    studentExamId.setExamId(Integer.parseInt(group));
                } else {
                    studentExamId.setStudentId(Integer.parseInt(group));
                }
            }
        }
        return studentExamId;
    }

    // 辅助方法：Mat 转 BufferedImage
    public static BufferedImage matToBufferedImage(Mat m) {
        int type = BufferedImage.TYPE_BYTE_GRAY;
        if (m.channels() > 1) {
            type = BufferedImage.TYPE_3BYTE_BGR;
        }
        int bufferSize = m.channels() * m.cols() * m.rows();
        byte[] b = new byte[bufferSize];
        m.get(0, 0, b); // 获取所有像素
        BufferedImage image = new BufferedImage(m.cols(), m.rows(), type);
        final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
        System.arraycopy(b, 0, targetPixels, 0, b.length);
        return image;
    }

}
