/**
 * 基于 OpenCV SIFT 的几何一致性找图脚本
 * 修复：色彩校正、逻辑平铺、参数详细 JSDoc 注释、区域裁剪补偿、小图尺度保护、Scalar 定义修复、DMatch 强类型数组转换
 */

function main() {
    let largeImg = images.read(files.cwd() + "/大图.png");
    let smallImg = images.read(files.cwd() + "/小图.png");
    let debugPath = "/sdcard/match_result.png";

    // 图像特征提取与预处理
    console.time("大图耗时");
    let largeImgFeatures = detectAndComputeFeatures(largeImg, { scale: 0.5, grayscale: true, region: [] });
    console.timeEnd("大图耗时");

    console.time("小图耗时");
    let smallImgFeatures = detectAndComputeFeatures(smallImg, { scale: 0.5, grayscale: true });
    console.timeEnd("小图耗时");

    // 特征匹配与中心点计算
    console.time("匹配耗时");
    let result = matchFeatures(largeImgFeatures, smallImgFeatures, {
        threshold: 0.9,
        drawMatches: debugPath
    });
    console.timeEnd("匹配耗时");

    if (result) {
        console.log("定位成功!");
        console.log("中心点: (" + result.centerX + ", " + result.centerY + ")");
        console.log("左上角: (" + result.topLeft.x + ", " + result.topLeft.y + ")");
        console.log("右上角: (" + result.topRight.x + ", " + result.topRight.y + ")");
        console.log("右下角: (" + result.bottomRight.x + ", " + result.bottomRight.y + ")");
        console.log("左下角: (" + result.bottomLeft.x + ", " + result.bottomLeft.y + ")");
        console.log("匹配点数: " + result.matchCount);
        app.viewFile(debugPath);

    } else {
        console.log("定位失败");
    }

    if (largeImgFeatures) largeImgFeatures.recycle();
    if (smallImgFeatures) smallImgFeatures.recycle();
    if (largeImg) largeImg.recycle();
    if (smallImg) smallImg.recycle();
}

/**
 * 图像特征提取与预处理
 * @param {Image} img - Auto.js 图片对象。通过 images.read() 或 captureScreen() 获取
 * @param {Object} options - 提取配置对象：
 * @param {number} options.scale - 计算特征时图片的缩放比例，缩放比例越小，计算特征越快，但可能因为放缩过度导致特征计算错误。对于宽度 * 高度 > 2500 的图片，scale 参数默认为 0.5，否则 scale 默认为 1
 * @param {boolean} options.grayscale - 是否启用灰度化。true 为单通道处理（快），false 为三通道处理（准），默认为 true
 * @param {Array} options.region - [x, y, w, h] 图像的匹配区域，不填此字段时则为整个图片计算特征
 * @returns {Object} 返回特征数据包：{keypoints, descriptors, workMat, width, height, scale, offsetX, offsetY, recycle}
 */
function detectAndComputeFeatures(img, options) {
    importClass(org.opencv.imgproc.Imgproc);
    importClass(org.opencv.features2d.SIFT);
    importClass(org.opencv.core.Mat);
    importClass(org.opencv.core.Size);
    importClass(org.opencv.core.Rect);
    importClass(org.opencv.core.MatOfKeyPoint);

    let src = img.getMat();
    let workMat = new Mat();
    let region = options.region;
    let offsetX = 0, offsetY = 0;

    // 处理区域裁剪
    if (region && region.length === 4) {
        let rect = new Rect(
            Math.max(0, region[0]),
            Math.max(0, region[1]),
            Math.min(src.cols() - region[0], region[2]),
            Math.min(src.rows() - region[1], region[3])
        );
        workMat = src.submat(rect);
        offsetX = rect.x;
        offsetY = rect.y;
    } else {
        src.copyTo(workMat);
    }

    let width = workMat.cols();
    let height = workMat.rows();
    // 小图面积保护阈值 2500 像素
    let scale = (width * height > 2500) ? (options.scale || 0.5) : 1.0;

    // Auto.js(RGBA) -> OpenCV(BGR)
    Imgproc.cvtColor(workMat, workMat, Imgproc.COLOR_RGBA2BGR);

    if (scale !== 1.0) {
        let temp = new Mat();
        Imgproc.resize(workMat, temp, new Size(width * scale, height * scale));
        workMat.release();
        workMat = temp;
    }

    if (options.grayscale !== false) Imgproc.cvtColor(workMat, workMat, Imgproc.COLOR_BGR2GRAY);

    let sift = SIFT.create();
    let keypoints = new MatOfKeyPoint();
    let descriptors = new Mat();
    sift.detectAndCompute(workMat, new Mat(), keypoints, descriptors);

    return {
        keypoints: keypoints, descriptors: descriptors, workMat: workMat,
        width: width, height: height, scale: scale,
        offsetX: offsetX, offsetY: offsetY,
        recycle: function () {
            if (this.keypoints) this.keypoints.release();
            if (this.descriptors) this.descriptors.release();
            if (this.workMat) this.workMat.release();
        }
    };
}

/**
 * 特征匹配与中心点计算
 * @param {Object} scene - 场景特征数据包（由 detectAndComputeFeatures 生成的大图数据）
 * @param {Object} object - 模板特征数据包（由 detectAndComputeFeatures 生成的小图数据）
 * @param {Object} options - 匹配配置对象：
 * @param {number} options.threshold - 匹配阈值，越小越严苛，误报越少，默认为 0.7
 * @param {string} options.drawMatches - 绘制图片匹配详情的路径，若为空则不绘制匹配详情
 * @returns {Object|null} 返回 {topLeft, topRight, bottomLeft, bottomRight, centerX, centerY, center, matchCount}，若未找到则返回 null
 */
function matchFeatures(scene, object, options) {
    importClass(org.opencv.features2d.DescriptorMatcher);
    importClass(org.opencv.features2d.Features2d);
    importClass(org.opencv.calib3d.Calib3d);
    importClass(org.opencv.imgcodecs.Imgcodecs);
    importClass(org.opencv.imgproc.Imgproc);
    importClass(org.opencv.core.Core);
    importClass(org.opencv.core.Mat);
    importClass(org.opencv.core.Scalar);
    importClass(org.opencv.core.Point);
    importClass(org.opencv.core.MatOfByte);
    importClass(org.opencv.core.MatOfDMatch);
    importClass(org.opencv.core.MatOfPoint2f);
    importClass(org.opencv.core.MatOfKeyPoint);

    if (object.descriptors.empty() || scene.descriptors.empty() || object.descriptors.rows() < 4) return null;

    let matcher = DescriptorMatcher.create(DescriptorMatcher.FLANNBASED);
    let knnMatches = new java.util.ArrayList();
    matcher.knnMatch(object.descriptors, scene.descriptors, knnMatches, 2);

    let initialMatches = new java.util.ArrayList();
    let srcPts = new java.util.ArrayList();
    let dstPts = new java.util.ArrayList();
    let objKpArr = object.keypoints.toArray();
    let sceneKpArr = scene.keypoints.toArray();

    let threshold = options.threshold || 0.7;
    for (let i = 0; i < knnMatches.size(); i++) {
        let m = knnMatches.get(i).toArray();
        if (m.length >= 2 && m[0].distance < threshold * m[1].distance) {
            initialMatches.add(m[0]);
            srcPts.add(objKpArr[m[0].queryIdx].pt);
            dstPts.add(sceneKpArr[m[0].trainIdx].pt);
        }
    }

    if (srcPts.size() < 4) return null;

    let srcMat = new MatOfPoint2f(); srcMat.fromList(srcPts);
    let dstMat = new MatOfPoint2f(); dstMat.fromList(dstPts);
    let mask = new MatOfByte();
    let H = Calib3d.findHomography(srcMat, dstMat, Calib3d.RANSAC, 5.0, mask);

    if (!H || H.empty()) {
        srcMat.release(); dstMat.release(); mask.release();
        return null;
    }

    // 过滤 RANSAC 内点
    let maskArr = mask.toArray();
    let finalMatchesList = new java.util.ArrayList();
    let thinObjKpList = new java.util.ArrayList();
    let thinSceneKpList = new java.util.ArrayList();

    for (let k = 0; k < maskArr.length; k++) {
        if (maskArr[k] === 1) {
            let match = initialMatches.get(k);
            let newMatch = new org.opencv.core.DMatch(thinObjKpList.size(), thinSceneKpList.size(), match.distance);
            finalMatchesList.add(newMatch);
            thinObjKpList.add(objKpArr[match.queryIdx]);
            thinSceneKpList.add(sceneKpArr[match.trainIdx]);
        }
    }

    // 计算几何角点
    let objW = object.width * object.scale;
    let objH = object.height * object.scale;
    let objCorners = new MatOfPoint2f(new Point(0, 0), new Point(objW, 0), new Point(objW, objH), new Point(0, objH));
    let sceneCorners = new MatOfPoint2f();
    Core.perspectiveTransform(objCorners, sceneCorners, H);
    let corners = sceneCorners.toArray();

    // 全坐标还原
    let sf = 1.0 / scene.scale;
    let ox = scene.offsetX;
    let oy = scene.offsetY;
    let fix = (pt) => ({ x: pt.x * sf + ox, y: pt.y * sf + oy });

    // 计算四个角点
    let topLeft = fix(corners[0]);
    let topRight = fix(corners[1]);
    let bottomRight = fix(corners[2]);
    let bottomLeft = fix(corners[3]);
    
    // 计算中心点
    let centerX = (topLeft.x + topRight.x + bottomRight.x + bottomLeft.x) / 4;
    let centerY = (topLeft.y + topRight.y + bottomRight.y + bottomLeft.y) / 4;
    
    let result = {
        topLeft: topLeft,
        topRight: topRight,
        bottomRight: bottomRight,
        bottomLeft: bottomLeft,
        centerX: centerX,
        centerY: centerY,
        center: { centerX, centerY },
        matchCount: finalMatchesList.size()
    };

    // 调试绘图逻辑
    if (options.drawMatches) {
        let outImg = new Mat();
        let thinObjKp = new MatOfKeyPoint(); thinObjKp.fromList(thinObjKpList);
        let thinSceneKp = new MatOfKeyPoint(); thinSceneKp.fromList(thinSceneKpList);

        // 强类型转换
        let matchArray = java.lang.reflect.Array.newInstance(org.opencv.core.DMatch, finalMatchesList.size());
        for (let i = 0; i < finalMatchesList.size(); i++) { matchArray[i] = finalMatchesList.get(i); }
        let finalMatchesMat = new MatOfDMatch(matchArray);

        let drawObj = new Mat(), drawScene = new Mat();
        if (object.workMat.channels() === 1) Imgproc.cvtColor(object.workMat, drawObj, Imgproc.COLOR_GRAY2BGR);
        else object.workMat.copyTo(drawObj);
        if (scene.workMat.channels() === 1) Imgproc.cvtColor(scene.workMat, drawScene, Imgproc.COLOR_GRAY2BGR);
        else scene.workMat.copyTo(drawScene);

        Features2d.drawMatches(drawObj, thinObjKp, drawScene, thinSceneKp, finalMatchesMat, outImg);

        let offset = drawObj.cols();
        for (let j = 0; j < 4; j++) {
            Imgproc.line(outImg,
                new Point(corners[j].x + offset, corners[j].y),
                new Point(corners[(j + 1) % 4].x + offset, corners[(j + 1) % 4].y),
                new Scalar(0, 255, 0), 4);
        }

        Imgcodecs.imwrite(options.drawMatches, outImg);
        outImg.release(); thinObjKp.release(); thinSceneKp.release();
        finalMatchesMat.release(); drawObj.release(); drawScene.release();
    }

    srcMat.release(); dstMat.release(); H.release(); mask.release();
    objCorners.release(); sceneCorners.release();

    return result;
}

main();