package com.sz.admin.monitor.utils;

import org.bytedeco.opencv.opencv_core.*;
import org.bytedeco.opencv.opencv_imgproc.*;

import static org.bytedeco.opencv.global.opencv_core.*;
import static org.bytedeco.opencv.global.opencv_imgcodecs.*;
import static org.bytedeco.opencv.global.opencv_imgproc.*;

public class BlurDetectorV2 {

    // ===============================
    // 数据返回类 (替代 Python 中的 dict)
    // ===============================
    public static class DetectResult {
        public String type;
        public String value;
        public String code;
        public String desc;
        public double conf;
        public double score; // 显式记录分数，替代正则解析
        public double ratio; // 双图比较时的比率

        public DetectResult(String type, String value, String code, String desc, double conf, double score, double ratio) {
            this.type = type;
            this.value = value;
            this.code = code;
            this.desc = desc;
            this.conf = conf;
            this.score = score;
            this.ratio = ratio;
        }

        @Override
        public String toString() {
            return String.format("Result{value='%s', code='%s', desc='%s'}", value, code, desc);
        }
    }

    // ===============================
    // 光照归一化
    // ===============================
    private static Mat normalizeLighting(Mat gray) {
        Mat dst = new Mat();
        try (CLAHE clahe = createCLAHE(3.0, new Size(8, 8))) {
            clahe.apply(gray, dst);
        }
        return dst;
    }

    // ===============================
    // Laplacian 清晰度
    // ===============================
    private static double laplacianScore(Mat gray) {
        try (Mat lap = new Mat(); Mat mean = new Mat(); Mat stddev = new Mat()) {
            Laplacian(gray, lap, CV_64F);
            meanStdDev(lap, mean, stddev);
            double sd = stddev.createIndexer().getDouble();
            return sd * sd; // 方差
        }
    }

    // ===============================
    // Tenengrad 梯度
    // ===============================
    private static double tenengradScore(Mat gray) {
        try (Mat gx = new Mat(); Mat gy = new Mat();
             Mat gx2 = new Mat(); Mat gy2 = new Mat();
             Mat g = new Mat()) {

            Sobel(gray, gx, CV_64F, 1, 0, 3, 1.0, 0.0, BORDER_DEFAULT);
            Sobel(gray, gy, CV_64F, 0, 1, 3, 1.0, 0.0, BORDER_DEFAULT);

            multiply(gx, gx, gx2);
            multiply(gy, gy, gy2);
            add(gx2, gy2, g);

            return mean(g).get(0);
        }
    }

    // ===============================
    // Edge Density（边缘密度）
    // ===============================
    private static double edgeDensity(Mat gray) {
        try (Mat edges = new Mat()) {
            Canny(gray, edges, 80, 150);
            int nonZero = countNonZero(edges);
            return (double) nonZero / (edges.rows() * edges.cols());
        }
    }


    // ===============================
    // FFT 高频能量 (最终完美版)
    // ===============================
    private static double fftHighFrequency(Mat gray) {
        // 第一层：初始化所有基础操作矩阵，这些变量在此块内不可被重新赋值
        try (Mat f32 = new Mat();
             Mat zeros = Mat.zeros(gray.size(), CV_32F).asMat();
             MatVector planes = new MatVector(2);
             Mat complexI = new Mat();
             Mat mag = new Mat();
             Mat ones = Mat.ones(gray.size(), CV_32F).asMat();
             Mat tmp = new Mat()) {

            // 1. 转为浮点并合并为双通道复数矩阵
            gray.convertTo(f32, CV_32F);
            planes.put(0, f32);
            planes.put(1, zeros);
            merge(planes, complexI);

            // 2. 傅里叶变换
            dft(complexI, complexI);

            // 3. 计算幅度: magnitude = log(abs(fshift) + 1)
            split(complexI, planes);
            magnitude(planes.get(0), planes.get(1), mag);
            add(mag, ones, mag);
            log(mag, mag);

            // 4. fftshift 平移 (将低频移到中心)
            // 声明新的变量 croppedMag 来接收裁剪结果，避免给 mag 重新赋值引发报错
            try (Mat croppedMag = new Mat(mag, new Rect(0, 0, mag.cols() & -2, mag.rows() & -2))) {

                int cx = croppedMag.cols() / 2;
                int cy = croppedMag.rows() / 2;

                // 第三层：声明所有的子矩阵 (ROI) 和 mask，确保它们用完后被自动释放
                try (Mat q0 = new Mat(croppedMag, new Rect(0, 0, cx, cy));
                     Mat q1 = new Mat(croppedMag, new Rect(cx, 0, cx, cy));
                     Mat q2 = new Mat(croppedMag, new Rect(0, cy, cx, cy));
                     Mat q3 = new Mat(croppedMag, new Rect(cx, cy, cx, cy));
                     Mat mask = new Mat(croppedMag.size(), CV_32F, new Scalar(1.0))) {

                    // 象限交换 (Shift)
                    q0.copyTo(tmp);
                    q3.copyTo(q0);
                    tmp.copyTo(q3);
                    q1.copyTo(tmp);
                    q2.copyTo(q1);
                    tmp.copyTo(q2);

                    // 5. Mask 屏蔽中心低频
                    int r = Math.min(cx, cy) / 4;
                    rectangle(mask,
                        new Point(cx - r, cy - r),
                        new Point(cx + r, cy + r),
                        new Scalar(0.0),
                        -1, 8, 0);

                    // 6. 对应元素相乘 (只保留高频部分)
                    multiply(croppedMag, mask, croppedMag);

                    // 7. 计算整体高频均值
                    return mean(croppedMag).get(0);
                }
            }
        }
    }

    // ===============================
    // 白墙和纯色背景图检测
    // ===============================
    private static boolean isWhiteWall(Mat image) {
        try (Mat hsv = new Mat();
             Mat mask = new Mat();
             Mat lower = new Mat(1, 1, CV_8UC3, new Scalar(0, 0, 200, 0));
             Mat upper = new Mat(1, 1, CV_8UC3, new Scalar(180, 40, 255, 0));
             Mat gray = new Mat()) {

            cvtColor(image, hsv, COLOR_BGR2HSV);
            inRange(hsv, lower, upper, mask);

            double ratio = (double) countNonZero(mask) / (mask.rows() * mask.cols());

            cvtColor(image, gray, COLOR_BGR2GRAY);
            double lap = laplacianScore(gray);
            double edge = edgeDensity(gray);

            if (ratio > 0.7 && lap < 15) {
                return true;
            }
            if (lap < 10 && edge < 0.02) {
                return true;
            }
            return false;
        }
    }

    // ===============================
    // ROI中心区域 (切片等价实现)
    // ===============================
    private static Mat extractRoi(Mat image) {
        int h = image.rows();
        int w = image.cols();

        int y = h / 5;
        int x = w / 5;
        int roiH = (4 * h / 5) - y;
        int roiW = (4 * w / 5) - x;

        return new Mat(image, new Rect(x, y, roiW, roiH));
    }

    // ===============================
    // 模糊检测单图主函数
    // ===============================
    public static DetectResult detectBlur(Mat image, String detectTypeKey, double threshold) {
        if (image == null || image.empty()) {
            throw new IllegalArgumentException("image read error");
        }

        // ===============================
        // 白墙过滤
        // ===============================
        if (isWhiteWall(image)) {
            return new DetectResult(detectTypeKey, "1", "4001", "白墙/纯色背景 score=0", 0.0, 0.0, 0.0);
        }

        try (Mat roiImage = extractRoi(image);
             Mat gray = new Mat()) {

            cvtColor(roiImage, gray, COLOR_BGR2GRAY);

            try (Mat normGray = normalizeLighting(gray)) {
                // ===============================
                // 计算指标
                // ===============================
                double lap = laplacianScore(normGray);
                double ten = tenengradScore(normGray);
                double edge = edgeDensity(normGray);
                double fft = fftHighFrequency(normGray);

                // ===============================
                // 归一化
                // ===============================
                double lap_n = Math.min(lap / 2000.0, 1.0);
                double ten_n = Math.min(ten / 10000.0, 1.0);
                double edge_n = Math.min(edge * 10.0, 1.0);
                double fft_n = Math.min(fft / 10.0, 1.0);

                // ===============================
                // 综合评分
                // ===============================
                double score = (0.35 * lap_n + 0.30 * ten_n + 0.20 * edge_n + 0.15 * fft_n) * 100.0;
                String descStr = String.format("score=%.2f", score);

                // ===============================
                // 判断
                // ===============================
                if (score < threshold) {
                    return new DetectResult(detectTypeKey, "1", "2000", "模糊 " + descStr, 1.0, score, 0.0);
                } else {
                    return new DetectResult(detectTypeKey, "0", "2000", "正常 " + descStr, 1.0, score, 0.0);
                }
            }
        }
    }

    public static DetectResult detectBlur(String imagePath) {
        try (Mat img = imread(imagePath)) {
            return detectBlur(img, "bpmh", 90.0);
        }
    }

    // ===============================
    // 双图检测
    // ===============================
    public static DetectResult doubleDetectBlur(String imgPath, String refPath, double ratioThreshold) {
        boolean useRef = true;

        if (refPath == null || refPath.isEmpty()) {
            useRef = false;
        }

        try (Mat imgMat = imread(imgPath);
             Mat refMat = useRef ? imread(refPath) : null) {

            if (refMat == null || refMat.empty()) {
                useRef = false;
            }

            // 单图模式兜底
            if (!useRef) {
                return detectBlur(imgMat, "bpmh", 60.0);
            }

            // 双图模式
            DetectResult imgResult = detectBlur(imgMat, "bpmh", 60.0);
            DetectResult refResult = detectBlur(refMat, "bpmh", 60.0);

            double scoreImg = imgResult.score;
            double scoreRef = refResult.score;

            if (scoreRef < 1e-6) {
                return new DetectResult("bpmh", "0", "2000", "参考图异常", 0.0, scoreImg, 0.0);
            }

            double ratio = scoreRef / scoreImg;
            System.out.println(scoreImg + ", " + scoreRef);

            if (ratio < ratioThreshold) {
                return new DetectResult("bpmh", "1", "2000", String.format("模糊 ratio=%.2f", ratio), 1.0, scoreImg, ratio);
            } else {
                return new DetectResult("bpmh", "0", "2000", String.format("正常 ratio=%.2f", ratio), 1.0, scoreImg, ratio);
            }
        }
    }

    // ===============================
    // 测试入口
    // ===============================
    public static void main(String[] args) {
        String img = "D:\\work\\images\\CAPTURE_265_20260319160327655_79b39c57.jpg";       // 基准图提前给定
        String ref = "D:\\work\\images\\CAPTURE_266_20260319160512854_13b52e7e.jpg"; // 摄像头后面传入图像
        //Result{value='0', code='2000', desc='正常 ratio=1.01'}
        // Result{value='1', code='2000', desc='模糊 ratio=0.69'}
        // 默认 ratio_threshold=0.85
       // DetectResult result = doubleDetectBlur(img, ref, 0.85);
        // 60 到 70
        DetectResult detectResult = detectBlur(ref);
        System.out.println("单图检测："+detectResult.toString());
       // System.out.println(result.toString());
    }
}