#!/usr/bin/env python3
"""
自动检测 m02.png 中的小标题位置并在下方写入新内容
基于 easyocr 实现自动文字定位
"""
import json
import sys
import os
from pathlib import Path

try:
    import easyocr
    import numpy as np
    from PIL import Image, ImageDraw, ImageFont
except ImportError as e:
    print(f"缺少依赖: {e}")
    print("请安装: pip install easyocr pillow numpy")
    sys.exit(1)

FONT_DIR = Path("/usr/share/fonts/truetype/dejavu")
FONT_TEXT = os.path.join(FONT_DIR, "DejaVuSans.ttf")
FONT_BOLD = os.path.join(FONT_DIR, "DejaVuSans-Bold.ttf")

def _find_font(size, bold=False):
    try:
        path = FONT_BOLD if bold else FONT_TEXT
        return ImageFont.truetype(str(path), size)
    except Exception:
        return ImageFont.load_default()

def hex_to_rgb(hex_color):
    h = hex_color.lstrip('#')
    if len(h) == 3:
        h = ''.join(c*2 for c in h)
    return tuple(int(h[i:i+2], 16) for i in (0, 2, 4))

def detect_text_regions(image_path):
    """使用 easyocr 检测图片中的所有文字区域"""
    print(" 正在初始化 OCR 模型并扫描图片...")
    reader = easyocr.Reader(['ch_sim', 'en'], gpu=False, verbose=False)
    img = Image.open(image_path).convert('RGB')
    results = reader.readtext(np.array(img))
    
    detections = []
    for (bbox, text, conf) in results:
        xs = [point[0] for point in bbox]
        ys = [point[1] for point in bbox]
        x0, y0 = int(min(xs)), int(min(ys))
        x1, y1 = int(max(xs)), int(max(ys))
        cx = (x0 + x1) // 2
        cy = (y0 + y1) // 2
        detections.append({
            "text": text.strip(),
            "bbox": [x0, y0, x1, y1],
            "conf": float(conf),
            "center": (cx, cy),
            "left": x0, "top": y0, "right": x1, "bottom": y1,
            "width": x1 - x0, "height": y1 - y0
        })
    
    print(f" 检测到 {len(detections)} 个文本区域。")
    return detections

def sort_text_regions(regions, y_tolerance=20):
    """对文本区域进行排序：先按行（Y轴），再按列（X轴）"""
    return sorted(regions, key=lambda r: (r["center"][1], r["center"][0]))

def auto_fill_text(template_path, output_path, new_contents, font_size=24, text_color="#000000", y_offset=15, debug=False):
    """自动检测小标题并在下方写入新内容"""
    try:
        detections = detect_text_regions(template_path)
        if not detections:
            print(" 错误: 未检测到任何文本区域")
            return False
        
        sorted_detections = sort_text_regions(detections)
        
        # 过滤
        filtered = [d for d in sorted_detections if 30 <= d["width"] <= 800 and 10 <= d["height"] <= 50]
        print(f" 过滤后剩余 {len(filtered)} 个有效文本区域。")
        
        if len(new_contents) > len(filtered):
            new_contents = new_contents[:len(filtered)]
        elif len(new_contents) < len(filtered):
            filtered = filtered[:len(new_contents)]
        
        img = Image.open(template_path).convert("RGB")
        draw = ImageDraw.Draw(img)
        font = _find_font(font_size, bold=True)
        
        coord_mapping = []
        
        for idx, (det, new_text) in enumerate(zip(filtered, new_contents)):
            left_x = det["left"]
            bottom_y = det["bottom"]
            target_x = left_x
            target_y = bottom_y + y_offset
            
            img_w, img_h = img.size
            target_x = max(0, min(target_x, img_w - 1))
            target_y = max(0, min(target_y, img_h - 1))
            
            coord_mapping.append({
                "index": idx,
                "detected_text": det["text"],
                "new_text": new_text,
                "detection_bbox": det["bbox"],
                "write_position": [target_x, target_y],
                "detection_confidence": det["conf"]
            })
            
            if debug:
                bbox = det["bbox"]
                draw.rectangle([bbox[0], bbox[1], bbox[2], bbox[3]], outline="#0000FF", width=1)
                draw.line([target_x-5, target_y, target_x+5, target_y], fill="#FF0000", width=1)
                draw.line([target_x, target_y-5, target_x, target_y+5], fill="#FF0000", width=1)
                draw.text((target_x, target_y - 20), str(idx), fill="#FF00FF", font=_find_font(12))
            
            draw.text((target_x, target_y), new_text, font=font, fill=text_color)
            print(f" [标题 {idx+1:2d}]: '{det['text'][:15]:<15}' -> 在 ({target_x:4d}, {target_y:4d}) 写入: '{new_text}'")
        
        img.save(output_path, dpi=(300, 300), quality=95)
        print(f"\n 处理完成！结果已保存至: {output_path}")
        
        # 保存坐标映射 JSON
        json_path = output_path.rsplit('.', 1)[0] + '_coords.json'
        with open(json_path, 'w', encoding='utf-8') as f:
            json.dump({"template": template_path, "output": output_path, "settings": {"font_size": font_size, "text_color": text_color, "y_offset": y_offset}, "mapping": coord_mapping}, f, ensure_ascii=False, indent=2)
        print(f" 坐标映射已保存至: {json_path}")
        
        return True
    except Exception as e:
        print(f" 错误: {e}")
        import traceback
        traceback.print_exc()
        return False

def main():
    import argparse
    parser = argparse.ArgumentParser(description="自动检测 m02.png 中的小标题并在下方写入新内容")
    parser.add_argument("--template", "-t", required=True, help="模板图片路径")
    parser.add_argument("--output", "-o", required=True, help="输出图片路径")
    parser.add_argument("--contents", "-c", required=True, help="新内容的 JSON 文件路径或 JSON 字符串")
    parser.add_argument("--font-size", "-f", type=int, default=24, help="字号 (默认: 24)")
    parser.add_argument("--color", "-cl", default="#000000", help="颜色 (十六进制, 默认: #000000)")
    parser.add_argument("--y-offset", "-y", type=int, default=15, help="垂直偏移 (默认: 15)")
    parser.add_argument("--debug", "-d", action="store_true", help="调试模式")
    args = parser.parse_args()
    
    try:
        if os.path.isfile(args.contents):
            with open(args.contents, 'r', encoding='utf-8') as f:
                new_contents = json.load(f)
        else:
            new_contents = json.loads(args.contents)
        if not isinstance(new_contents, list):
            print(" 错误: 输入必须是一个字符串列表")
            sys.exit(1)
    except Exception as e:
        print(f" 错误: 无法解析新内容: {e}")
        sys.exit(1)
    
    if not os.path.isfile(args.template):
        print(f" 错误: 模板文件不存在: {args.template}")
        sys.exit(1)
    
    print("=" * 60)
    print(" 自动检测并填充文字工具")
    print("=" * 60)
    print(f" 模板: {args.template} | 输出: {args.output}")
    print(f" 内容数: {len(new_contents)} | 字号: {args.font_size} | 偏移: {args.y_offset}px")
    print(f" 调试: {'开' if args.debug else '关'}")
    print("-" * 60)
    
    success = auto_fill_text(args.template, args.output, new_contents, args.font_size, args.color, args.y_offset, args.debug)
    sys.exit(0 if success else 1)

if __name__ == "__main__":
    main()
