"""DNG 定量モードの輝度化パイプライン.

DNG（RAW_SENSOR）を読み込み，LSC（レンズシェーディング）補正を順適用して
PNG 経路と同じ portrait・linear・Rec.709 輝度画像へ変換する一連の処理をまとめる．

- 読み込みは load_image_dng（output_format="rgb_float32", linear, output_color=raw）
- DNG は RAW_SENSOR で未補正のため，meta.json の lscMap でレンズビネットを順適用（任意）
- DNG はセンサ native が landscape のため，PNG と同じ portrait へ rot90(-1) で向き合わせ
"""

import json
from pathlib import Path

import numpy as np

from src.analysis.uniformity import (
    REC709_COEFF_B,
    REC709_COEFF_G,
    REC709_COEFF_R,
)
from src.io.dng_loader import load_image_dng


def _upsample(grid: np.ndarray, shape: tuple[int, int]) -> np.ndarray:
    """(rows, cols) グリッドを画像サイズ shape へバイリニア補間する．

    Args:
        grid: 補間元グリッドの NumPy 配列（rows x cols, float64）．
        shape: 補間先の画像サイズ (height, width)．

    Returns:
        補間後の NumPy 配列（height x width, float64）．
    """
    rows, cols = grid.shape
    h, w = shape
    ys = np.linspace(0, rows - 1, h)
    xs = np.linspace(0, cols - 1, w)
    y0 = np.floor(ys).astype(int)
    y1 = np.minimum(y0 + 1, rows - 1)
    x0 = np.floor(xs).astype(int)
    x1 = np.minimum(x0 + 1, cols - 1)
    wy = (ys - y0)[:, None]
    wx = (xs - x0)[None, :]
    top = grid[y0][:, x0] * (1 - wx) + grid[y0][:, x1] * wx
    bot = grid[y1][:, x0] * (1 - wx) + grid[y1][:, x1] * wx
    return top * (1 - wy) + bot * wy


def lsc_gain_rgb(meta_path: Path, shape: tuple[int, int]) -> np.ndarray:
    """meta.json の lscMap (4, M, N) を (H, W, 3) の RGB ゲインへ補間する．

    4ch = [R, G_even, G_odd, B]．G は G_even/G_odd の平均を用いる．

    Args:
        meta_path: meta.json ファイルのパス．
        shape: 補間先の画像サイズ (height, width)．

    Returns:
        RGB ゲインの NumPy 配列（H x W x 3, float64）．
    """
    meta = json.loads(meta_path.read_text(encoding="utf-8"))
    rows, cols = meta["lscMapRowCount"], meta["lscMapColumnCount"]
    m = np.array(meta["lscMap"], dtype=np.float64).reshape(rows, cols, 4)
    gr = _upsample(m[:, :, 0], shape)
    gg = _upsample((m[:, :, 1] + m[:, :, 2]) / 2.0, shape)
    gb = _upsample(m[:, :, 3], shape)
    return np.stack([gr, gg, gb], axis=2)


def dng_luminance(image_path: str, apply_lsc: bool) -> np.ndarray:
    """DNG から portrait・linear・（任意で LSC 補正済）の輝度画像を返す．

    - output_format="rgb_float32"（output_color=raw, linear）で読み込み
    - apply_lsc=True なら lscMap を順適用してレンズビネットを除去
    - Rec.709 で輝度化（uniformity.py と同じ係数）
    - PNG と同じ portrait へ rot90(-1) で向き合わせ

    Args:
        image_path: DNG ファイルのパス．
        apply_lsc: True の場合，同名 .meta.json から LSC ゲインを読み込んで補正する．

    Returns:
        portrait 向きの輝度画像（H x W, float64）．値域は 0〜255 の linear スケール．
    """
    rgb = load_image_dng(image_path, output_format="rgb_float32")  # (H,W,3) linear landscape

    if apply_lsc:
        meta_path = Path(image_path).with_suffix("").with_suffix(".meta.json")
        if not meta_path.exists():
            # ".meta.json" は二重拡張子なので with_suffix では拾えない場合がある
            meta_path = Path(str(Path(image_path).with_suffix("")) + ".meta.json")
        if not meta_path.exists():
            raise FileNotFoundError(f"meta.json が見つかりません（LSC 補正に必要）: {meta_path}")
        rgb = rgb * lsc_gain_rgb(meta_path, rgb.shape[:2])

    luma = (
        REC709_COEFF_R * rgb[:, :, 0]
        + REC709_COEFF_G * rgb[:, :, 1]
        + REC709_COEFF_B * rgb[:, :, 2]
    )
    # linear のまま 0〜255 スケールで表現する（PNG 図と軸を揃えて比較するため）．
    # CoV・max/min・中心周辺比はスケール不変なので不変，mean/std のみ ×255 となる．
    # 既存 plot_histogram が range=(0,255) 固定であることにも整合する．
    luma = luma * 255.0
    return np.rot90(luma, -1)  # portrait（PNG と同じ向き）
