package com.sz.admin.monitor.utils;

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

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

public class BlurDetectorV4 {

    // ===============================
    // 数据返回类 (从 V3 移植)
    // ===============================
    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{type='%s', value='%s', code='%s', desc='%s', conf=%.1f, score=%.2f, ratio=%.2f}",
                    type, value, code, desc, conf, score, ratio);
        }
    }

    // ===============================
    // 光照归一化 (保留 V4 的 3.0 参数)
    // ===============================
    public static Mat normalizeLighting(Mat gray) {
        CLAHE clahe = createCLAHE(1.5, new Size(8, 8));
        Mat dst = new Mat();
        clahe.apply(gray, dst);
        clahe.close();
        return dst;
    }

    // ===============================
    // Laplacian 清晰度
    // ===============================
    public static double laplacianScore(Mat gray) {
        Mat lap = new Mat();
        Laplacian(gray, lap, CV_64F);

        Mat mean = new Mat();
        Mat stddev = new Mat();
        meanStdDev(lap, mean, stddev);

        double std = stddev.createIndexer().getDouble(0);
        lap.close();
        mean.close();
        stddev.close();

        return std * std;
    }

    // ===============================
    // Tenengrad 梯度
    // ===============================
    public static double tenengradScore(Mat gray) {
        Mat gx = new Mat();
        Mat gy = 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);

        Mat gx2 = new Mat();
        Mat gy2 = new Mat();
        Mat g = new Mat();

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

        double score = mean(g).get(0);

        gx.close(); gy.close();
        gx2.close(); gy2.close(); g.close();

        return score;
    }

    // ===============================
    // Edge Density（边缘密度）
    // ===============================
    public static double edgeDensity(Mat gray) {
        Mat edges = new Mat();
        Canny(gray, edges, 80, 150);

        int nonZero = countNonZero(edges);
        long totalPixels = edges.total();

        edges.close();
        return (double) nonZero / totalPixels;
    }

    // ===============================
    // FFT 高频能量 (保留 V4 优秀的内存管理)
    // ===============================
    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()) {

            gray.convertTo(f32, CV_32F);
            planes.put(0, f32);
            planes.put(1, zeros);
            merge(planes, complexI);

            dft(complexI, complexI);

            split(complexI, planes);
            magnitude(planes.get(0), planes.get(1), mag);
            add(mag, ones, mag);
            log(mag, 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;

                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))) {

                    q0.copyTo(tmp); q3.copyTo(q0); tmp.copyTo(q3);
                    q1.copyTo(tmp); q2.copyTo(q1); tmp.copyTo(q2);

                    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);

                    multiply(croppedMag, mask, croppedMag);
                    return mean(croppedMag).get(0);
                }
            }
        }
    }

    // ===============================
    // ROI中心区域
    // ===============================
    public static Mat extractRoi(Mat image) {
        int h = image.rows();
        int w = image.cols();
        int x = w / 5;
        int y = h / 5;
        int roiW = (4 * w / 5) - x;
        int roiH = (4 * h / 5) - y;

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

    // ===============================
    // 模糊检测主函数 (改为返回 DetectResult)
    // ===============================
    public static DetectResult detectBlur(Object imageObj, String detectTypeKey, double threshold) {
        Mat image = null;
        boolean needsRelease = false;

        if (imageObj instanceof String) {
            image = imread((String) imageObj);
            needsRelease = true;
        } else if (imageObj instanceof Mat) {
            image = (Mat) imageObj;
        }

        if (image == null || image.empty()) {
            throw new IllegalArgumentException("Image read error");
        }

        Mat roiImage = extractRoi(image);
        Mat gray = new Mat();
        cvtColor(roiImage, gray, COLOR_BGR2GRAY);
        Mat normalizedGray = normalizeLighting(gray);

        double lap = laplacianScore(normalizedGray);
        double ten = tenengradScore(normalizedGray);
        double edge = edgeDensity(normalizedGray);
        double fft = fftHighFrequency(normalizedGray);

        double lapN = Math.min(lap / 2000.0, 1.0);
        double tenN = Math.min(ten / 10000.0, 1.0);
        double edgeN = Math.min(edge * 10.0, 1.0);
        double fftN = Math.min(fft / 10.0, 1.0);

        double score = (0.35 * lapN + 0.30 * tenN + 0.20 * edgeN + 0.15 * fftN) * 100;
        String descStr = String.format("score=%.2f", score);

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

        roiImage.close();
        gray.close();
        normalizedGray.close();
        if (needsRelease) image.close();

        return result;
    }

    public static DetectResult detectBlur(Object imageObj) {
        return detectBlur(imageObj, "bpmh", 70.0);
    }

    // ===============================
    // 双图检测 (改为返回 DetectResult)
    // ===============================
    public static DetectResult doubleDetectBlur(Object imgObj, Object refObj, double ratioThreshold) {
        boolean useRef = true;
        Mat refMat = null;
        boolean refNeedsRelease = false;

        if (refObj == null || (refObj instanceof String && ((String) refObj).isEmpty())) {
            useRef = false;
        } else if (refObj instanceof String) {
            refMat = imread((String) refObj);
            if (refMat == null || refMat.empty()) useRef = false;
            else refNeedsRelease = true;
        } else if (refObj instanceof Mat) {
            refMat = (Mat) refObj;
            if (refMat.empty()) useRef = false;
        }

        // 单图模式兜底
        if (!useRef) {
            return detectBlur(imgObj);
        }

        // 双图模式
        DetectResult imgResult = detectBlur(imgObj);
        DetectResult refResult = detectBlur(refMat);

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

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

        double ratio = scoreRef / scoreImg;


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

        if (refNeedsRelease) refMat.close();

        return finalResult;
    }

    public static DetectResult doubleDetectBlur(Object imgObj, Object refObj) {
        return doubleDetectBlur(imgObj, refObj, 0.85);
    }

    // ===============================
    // 测试
    // ===============================
    public static void main(String[] args) {
        String path = "D:\\work\\imgs\\CAPTURE_266_20260317123416804_ac0f924c.jpg";
        try {
            DetectResult result = detectBlur(path);
            System.out.println(result.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}