"""PNG / DNG 比較スライド 1 枚を一括生成するスクリプト.

PNG（sRGB）と DNG（linear + LSC 補正）の照明均一性を「公平に」比較するため，
スケール差（8bit/10bit, sRGB/linear の絶対値差）を消して **減衰カーブの形** だけを
重ねて見せる．具体的には:

- 輝度マップ 2 枚: 各自の動径ピーク（最内ピーク輪帯平均）で正規化し，共通 colorbar で並べる
- 動径プロファイル重ね描き: 20 分割輪帯平均を **最大=1.0** で正規化して 1 枚に重ねる
  （谷の底がそのまま動径 min/max 比に一致する）
- 数値表: 動径 min/max 比・CoV・max/min 比・中心/周辺比・勾配

注意: PNG（4月）と DNG（6月）は別セッション撮影でフレーミングが異なるため，
厳密な同一シーン比較ではない（REPORT_02 の前提を参照）．本図は評価ドメインの違いが
減衰指標に与える影響を示すもの．

使い方:
    python scripts/compare_png_dng_slide.py
"""

import sys
from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np

PROJECT_ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(PROJECT_ROOT))

from src.analysis.spatial import (  # noqa: E402
    calc_distance_map,
    calc_radial_min_max_ratio,
    calc_radial_profile,
)
from src.analysis.uniformity import (  # noqa: E402
    REC709_COEFF_B,
    REC709_COEFF_G,
    REC709_COEFF_R,
    calc_uniformity,
)
from src.analysis.spatial import calc_spatial_uniformity  # noqa: E402
from src.config import load_roi_config  # noqa: E402
from src.io.dng_pipeline import dng_luminance  # noqa: E402
from src.io.loader import extract_roi, load_image  # noqa: E402

# 比較対象（PNG sRGB と DNG linear+LSC）
PNG_PATH = PROJECT_ROOT / "data" / "smtias" / "whiteboard" / "SmTIAS_20260408_144434.png"
DNG_PATH = PROJECT_ROOT / "data" / "smtias" / "quantitative" / "SmTIAS_QM_20260601_124500.dng"
ROI_CONFIG = PROJECT_ROOT / "config" / "roi_config.json"
ROI_KEY = "smtias.whiteboard"
OUTPUT_PATH = PROJECT_ROOT / "output" / "figures" / "compare_png_dng_slide.png"

# 正規化輝度マップの共通カラースケール（動径ピーク=1.0 を基準とした相対輝度）
MAP_VMIN = 0.80
MAP_VMAX = 1.02


def _png_luminance(roi: dict) -> np.ndarray:
    """PNG を Rec.709 で輝度化し ROI を切り出す（sRGB ドメインのまま）."""
    img = load_image(str(PNG_PATH))
    luma = (
        REC709_COEFF_R * img[:, :, 0]
        + REC709_COEFF_G * img[:, :, 1]
        + REC709_COEFF_B * img[:, :, 2]
    )
    return extract_roi(luma, roi["x"], roi["y"], roi["width"], roi["height"])


def _dng_luminance(roi: dict) -> np.ndarray:
    """DNG を linear + LSC 補正で輝度化し ROI を切り出す."""
    luma = dng_luminance(str(DNG_PATH), apply_lsc=True)
    return extract_roi(luma, roi["x"], roi["y"], roi["width"], roi["height"])


def _profile(luminance: np.ndarray) -> tuple[np.ndarray, dict, dict]:
    """動径プロファイル・min/max 統計・均一性/空間統計をまとめて算出する."""
    distance_map = calc_distance_map(*luminance.shape)
    profile = calc_radial_profile(luminance, distance_map)
    radial = calc_radial_min_max_ratio(profile)
    metrics = {**calc_uniformity(luminance), **calc_spatial_uniformity(luminance)["zone_stats"]}
    metrics["radial_min_max_ratio"] = radial["radial_min_max_ratio"]
    return profile, radial, metrics


def _draw_map(ax, luminance: np.ndarray, peak: float, title: str) -> None:
    """動径ピークで正規化した相対輝度マップを共通カラースケールで描く."""
    relative = luminance / peak
    im = ax.imshow(
        relative, cmap="hot", interpolation="nearest", vmin=MAP_VMIN, vmax=MAP_VMAX
    )
    ax.set_title(title, fontsize=11)
    ax.set_xlabel("X (px)")
    ax.set_ylabel("Y (px)")
    return im


def main() -> None:
    """比較スライドを生成して保存する."""
    roi = load_roi_config(str(ROI_CONFIG), ROI_KEY)
    if roi is None:
        raise SystemExit(f"ROI '{ROI_KEY}' が config に見つかりません")

    png_lum = _png_luminance(roi)
    dng_lum = _dng_luminance(roi)

    png_profile, png_radial, png_metrics = _profile(png_lum)
    dng_profile, dng_radial, dng_metrics = _profile(dng_lum)

    png_peak = png_radial["radial_max"]
    dng_peak = dng_radial["radial_max"]

    fig = plt.figure(figsize=(13, 9))
    gs = fig.add_gridspec(2, 2, height_ratios=[1.05, 1.0], hspace=0.32, wspace=0.28)

    # 上段: 正規化輝度マップ 2 枚（共通カラースケール）
    ax_png = fig.add_subplot(gs[0, 0])
    ax_dng = fig.add_subplot(gs[0, 1])
    _draw_map(ax_png, png_lum, png_peak, "PNG (sRGB)  relative luminance")
    im = _draw_map(ax_dng, dng_lum, dng_peak, "DNG (linear + LSC)  relative luminance")
    cbar = fig.colorbar(im, ax=[ax_png, ax_dng], fraction=0.025, pad=0.02)
    cbar.set_label("Relative luminance (radial peak = 1.0)")

    # 下段左: 動径プロファイル重ね描き（max=1.0 正規化）
    ax_prof = fig.add_subplot(gs[1, 0])
    px = png_profile[:, 0]
    py = png_profile[:, 1] / png_peak
    dx = dng_profile[:, 0]
    dy = dng_profile[:, 1] / dng_peak
    ax_prof.plot(px, py, marker="o", ms=4, color="steelblue",
                 label=f"PNG (sRGB)  min/max={png_radial['radial_min_max_ratio']:.3f}")
    ax_prof.plot(dx, dy, marker="s", ms=4, color="darkorange",
                 label=f"DNG (linear+LSC)  min/max={dng_radial['radial_min_max_ratio']:.3f}")
    # 谷の底（= min/max 比）を水平線で強調
    ax_prof.axhline(png_radial["radial_min_max_ratio"], color="steelblue", ls=":", alpha=0.5)
    ax_prof.axhline(dng_radial["radial_min_max_ratio"], color="darkorange", ls=":", alpha=0.5)
    ax_prof.set_title("Radial profile (normalized, peak = 1.0)", fontsize=11)
    ax_prof.set_xlabel("Normalized distance from center")
    ax_prof.set_ylabel("Normalized mean luminance")
    ax_prof.grid(True, linestyle="--", alpha=0.5)
    ax_prof.legend(loc="lower left", fontsize=9)

    # 下段右: 数値表
    ax_tbl = fig.add_subplot(gs[1, 1])
    ax_tbl.axis("off")
    rows = [
        ("Radial min/max ratio", f"{png_metrics['radial_min_max_ratio']:.3f}",
         f"{dng_metrics['radial_min_max_ratio']:.3f}"),
        ("CoV", f"{png_metrics['cov']:.3f}", f"{dng_metrics['cov']:.3f}"),
        ("Max/min ratio", f"{png_metrics['max_min_ratio']:.3f}",
         f"{dng_metrics['max_min_ratio']:.3f}"),
        ("Center/periphery", f"{png_metrics['center_periphery_ratio']:.3f}",
         f"{dng_metrics['center_periphery_ratio']:.3f}"),
        ("Gradient (%)", f"{png_metrics['gradient_magnitude']:.2f}",
         f"{dng_metrics['gradient_magnitude']:.2f}"),
    ]
    table = ax_tbl.table(
        cellText=rows,
        colLabels=["Metric", "PNG (sRGB)", "DNG (linear+LSC)"],
        cellLoc="center",
        loc="center",
    )
    table.auto_set_font_size(False)
    table.set_fontsize(11)
    table.scale(1.0, 1.8)
    for j in range(3):
        table[0, j].set_facecolor("#dfe6ee")
        table[0, j].set_text_props(weight="bold")
    ax_tbl.set_title(
        "Lower min/max & higher CoV = DNG reveals stronger edge falloff\n"
        "(sRGB gamma flattens PNG; not a strict same-scene comparison)",
        fontsize=9, loc="center",
    )

    fig.suptitle("PNG -> DNG: how peripheral falloff appears under each domain",
                 fontsize=14, y=0.98)

    OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True)
    fig.savefig(str(OUTPUT_PATH), dpi=150, bbox_inches="tight")
    plt.close(fig)
    print(f"比較スライドを保存しました: {OUTPUT_PATH}")
    print(f"  PNG radial min/max = {png_radial['radial_min_max_ratio']:.4f}, CoV = {png_metrics['cov']:.4f}")
    print(f"  DNG radial min/max = {dng_radial['radial_min_max_ratio']:.4f}, CoV = {dng_metrics['cov']:.4f}")


if __name__ == "__main__":
    main()
