# -*- coding: utf-8 -*-
"""
代码一：定义数据并保存（支持多个ROI）

功能：
1. 加载图一。
2. 允许用户交互式地绘制多个多边形 ROI，并为每个ROI命名。
3. 允许用户交互式地绘制多个矩形屏蔽区。
4. 将所有ROI坐标、名称、屏蔽区坐标和相关参数打包保存到一个 JSON 文件中。
"""
import cv2
import numpy as np
import json
import os
import base64

# ========== 全局变量 ==========
# --- ROI 绘制相关 ---
current_roi_points = []
all_rois = []  # 存储所有ROI及其名称
drawing_complete = False
drawing_image = None
original_image = None
# --- 屏蔽区绘制相关 ---
ignore_polygons = []
drawing_ignore = None
drawing_rect = False
rect_start_point = None

# ========== 可配置参数 ==========
CONFIG_PARAMS = {
    "MARGIN_RATIO": 0.25,
    "MIN_MATCH_COUNT": 10,
    "USE_CLAHE": True
}


# -------------------------------------------------
# 1. 回调函数：选择 ROI
# -------------------------------------------------
def select_roi(event, x, y, flags, param):
    """用于绘制 ROI 多边形的回调函数"""
    global current_roi_points, drawing_complete, drawing_image

    if drawing_complete:
        return

    if event == cv2.EVENT_LBUTTONDOWN:
        current_roi_points.append((x, y))
        cv2.circle(drawing_image, (x, y), 4, (0, 255, 0), -1)
        if len(current_roi_points) > 1:
            cv2.line(drawing_image, current_roi_points[-2], current_roi_points[-1], (0, 255, 0), 2)
        cv2.imshow("Image 1 - Select ROI", drawing_image)

    elif event == cv2.EVENT_RBUTTONDOWN:
        if len(current_roi_points) < 3:
            print("[提示] ROI 至少需要 3 个点构成多边形!")
            return
        drawing_complete = True
        cv2.line(drawing_image, current_roi_points[-1], current_roi_points[0], (0, 255, 0), 2)
        cv2.imshow("Image 1 - Select ROI", drawing_image)
        print("[提示] 当前ROI选择完毕，按任意键继续...")


# -------------------------------------------------
# 2. 回调函数：选择屏蔽区
# -------------------------------------------------
def select_ignore_rect(event, x, y, flags, param):
    """用于绘制屏蔽矩形的回调函数"""
    global rect_start_point, drawing_rect, ignore_polygons, drawing_ignore

    if event == cv2.EVENT_LBUTTONDOWN:
        rect_start_point = (x, y)
        drawing_rect = True

    elif event == cv2.EVENT_MOUSEMOVE:
        if drawing_rect:
            preview_img = drawing_ignore.copy()
            cv2.rectangle(preview_img, rect_start_point, (x, y), (0, 0, 255), 2)
            cv2.imshow("Image 1 - Ignore Area", preview_img)

    elif event == cv2.EVENT_LBUTTONUP:
        if not drawing_rect:
            return
        drawing_rect = False
        x_start, y_start = rect_start_point
        x1, y1 = min(x_start, x), min(y_start, y)
        x2, y2 = max(x_start, x), max(y_start, y)

        if x2 - x1 > 5 and y2 - y1 > 5:
            cv2.rectangle(drawing_ignore, (x1, y1), (x2, y2), (0, 0, 255), 2)
            rect_as_poly = [(x1, y1), (x2, y1), (x2, y2), (x1, y2)]
            ignore_polygons.append(rect_as_poly)
            print(f"[提示] 已新增屏蔽区 #{len(ignore_polygons)}，可继续拉框，完成后按 Enter 键。")
        cv2.imshow("Image 1 - Ignore Area", drawing_ignore)


# -------------------------------------------------
# 3. 工具函数
# -------------------------------------------------
def read_image_chinese(path):
    """读取可能包含中文路径的图片"""
    try:
        with open(path, 'rb') as f:
            data = np.frombuffer(f.read(), np.uint8)
        return cv2.imdecode(data, cv2.IMREAD_COLOR)
    except Exception as e:
        print(f"读取图片失败: {e}")
        return None


def draw_all_rois(image, rois_list):
    """在图像上绘制所有已保存的ROI"""
    result = image.copy()
    colors = [(0, 255, 0), (255, 0, 0), (0, 0, 255), (255, 255, 0),
              (255, 0, 255), (0, 255, 255), (128, 255, 0), (255, 128, 0)]

    for idx, roi_data in enumerate(rois_list):
        color = colors[idx % len(colors)]
        points = roi_data["points"]
        name = roi_data["name"]

        # 绘制多边形
        cv2.polylines(result, [np.array(points, np.int32)], True, color, 2)

        # 计算中心点并显示名称
        center = np.mean(points, axis=0).astype(int)
        cv2.putText(result, name, tuple(center), cv2.FONT_HERSHEY_SIMPLEX,
                    0.7, color, 2, cv2.LINE_AA)

    return result


# -------------------------------------------------
# 4. 主函数
# -------------------------------------------------
def define_and_save_data(image1_path, output_json_path):
    """
    主流程函数：加载图像，引导用户选择多个ROI并命名，最终保存数据。
    """
    global current_roi_points, drawing_complete, drawing_image, all_rois
    global drawing_ignore, ignore_polygons, original_image

    # --- 步骤 1: 加载图像 ---
    img1 = read_image_chinese(image1_path)
    if img1 is None:
        print(f"错误：无法加载图像 '{image1_path}'，请检查路径！")
        return

    original_image = img1.copy()

    # --- 步骤 2: 循环选择多个ROI ---
    print("\n========== 开始选择ROI ==========")
    roi_count = 0

    while True:
        roi_count += 1
        current_roi_points = []
        drawing_complete = False

        # 显示已有的ROI
        if all_rois:
            drawing_image = draw_all_rois(original_image, all_rois)
        else:
            drawing_image = original_image.copy()

        cv2.namedWindow("Image 1 - Select ROI")
        cv2.setMouseCallback("Image 1 - Select ROI", select_roi)

        print(f"\n【ROI #{roi_count}】")
        print("用鼠标左键多点圈出ROI，右键闭合完成当前ROI。")
        print("按 ESC 键结束所有ROI选择，按其他键继续添加下一个ROI。")
        cv2.imshow("Image 1 - Select ROI", drawing_image)

        key = cv2.waitKey(0)

        if key == 27:  # ESC键，结束ROI选择
            cv2.destroyWindow("Image 1 - Select ROI")
            if len(current_roi_points) >= 3:
                # 保存最后一个ROI
                roi_name = input(f"请输入ROI #{roi_count} 的名称: ").strip()
                if not roi_name:
                    roi_name = f"ROI_{roi_count}"
                all_rois.append({
                    "name": roi_name,
                    "points": current_roi_points
                })
                print(f"已保存ROI: {roi_name}")
            break
        else:
            if len(current_roi_points) >= 3:
                # 为当前ROI输入名称
                roi_name = input(f"请输入ROI #{roi_count} 的名称: ").strip()
                if not roi_name:
                    roi_name = f"ROI_{roi_count}"

                all_rois.append({
                    "name": roi_name,
                    "points": current_roi_points
                })
                print(f"已保存ROI: {roi_name}")
            else:
                print("当前ROI点数不足，跳过保存。")
                roi_count -= 1

    if not all_rois:
        print("错误：未选择任何ROI，程序退出。")
        return

    print(f"\n总共选择了 {len(all_rois)} 个ROI")

    # --- 步骤 3: 交互式选择屏蔽区 ---
    drawing_ignore = draw_all_rois(original_image, all_rois)
    cv2.namedWindow("Image 1 - Ignore Area")
    cv2.setMouseCallback("Image 1 - Ignore Area", select_ignore_rect)
    print("\n【步骤 2】在弹窗中，用鼠标左键拖拽拉框来选择屏蔽区，可画多个。")
    print("         所有屏蔽区画完后，按 Enter 键保存并结束，或按 Esc 键退出。")
    cv2.imshow("Image 1 - Ignore Area", drawing_ignore)

    while True:
        key = cv2.waitKey(0)
        if key in [13, 10]:  # Enter键
            break
        elif key == 27:  # Esc
            cv2.destroyAllWindows()
            print("用户取消操作。")
            return
    cv2.destroyAllWindows()

    # --- 步骤 4: 封装数据并保存到 JSON ---
    try:
        with open(image1_path, 'rb') as f:
            image_bytes = f.read()
        image_base64 = base64.b64encode(image_bytes).decode('utf-8')
        image_filename = os.path.basename(image1_path)
    except Exception as e:
        print(f"\n[失败] 读取并编码图像文件时出错: {e}")
        return

    # 封装数据
    data_to_save = {
        "image1_filename": image_filename,
        "image1_data_base64": image_base64,
        "rois": all_rois,  # 保存所有ROI及其名称
        "ignore_polygons": ignore_polygons,
        "parameters": CONFIG_PARAMS
    }

    try:
        with open(output_json_path, 'w', encoding='utf-8') as f:
            json.dump(data_to_save, f, indent=4, ensure_ascii=False)
        print(f"\n[成功] {len(all_rois)} 个ROI、屏蔽区和图像数据已成功保存到: {output_json_path}")
    except Exception as e:
        print(f"\n[失败] 保存 JSON 文件时出错: {e}")


# -------------------------------------------------
if __name__ == "__main__":
    IMG1_PATH = r"20250615000002.jpg"
    OUTPUT_JSON = "roi_json_dir/matching_data_multi_roi.json"

    # 创建输出目录
    os.makedirs(os.path.dirname(OUTPUT_JSON), exist_ok=True)

    define_and_save_data(IMG1_PATH, OUTPUT_JSON)