"""依頼113 G: g_timing.csv と g_grasp_metrics.csv を 1 本 1 行にまとめ (g_table.csv), README 用の表を出す (標準出力).

深さの物差しは「爪の向きに沿うはみ出し」(g_images.measure_axis. axis_protrude_px). E の値 (縦の差) とは 0〜7px 違うので,
E の 16 件も同じ物差しで測り直した値 (dataset=E_failures / E_successes) と並べる.
scripted_v1 はカメラが少し引いていて爪先の印の間 (開き) が 122〜127px (ほかは 134〜140px) なので,
px を「開いた印の間 137px」にそろえた値 (scaled) も出す.
「手」: 閉じる前のコマに人の手が写る本 (画で確かめた. HAND). 肌を赤として拾うと値が 100px を超えるので,
閉じる前の値で 100px を超えるものは使わない (表では「—」).
"""
import csv
from pathlib import Path

import numpy as np

OUT = Path(__file__).resolve().parent
HAND = {"run_0001_20261019_191347", "run_0001_20261019_191441",
        # scripted_v1: 182552・185536・175444 は直前まで, 183310・184725・190925 は 1.5 秒前のコマだけ手が写る
        "run_0001_20261015_182552", "run_0001_20261015_183310", "run_0001_20261015_185536",
        "run_0001_20261016_175444", "run_0001_20261016_184725", "run_0001_20261016_190925"}
SUCCESS_MAX_AFTER = None    # E の成功の閉じたあとの最大 (下で計算)


def f(v):
    try:
        return float(v)
    except (TypeError, ValueError):
        return None


def main():
    tim = {(r["dataset"], r["run"]): r for r in csv.DictReader(open(OUT / "g_timing.csv"))}
    gm = list(csv.DictReader(open(OUT / "g_grasp_metrics.csv")))
    e_succ = [f(r["after_axis_protrude_px"]) for r in gm if r["dataset"] == "E_successes"]
    e_shallow = [f(r["after_axis_protrude_px"]) for r in gm if r["dataset"] == "E_failures"
                 and f(r["after_axis_marker_gap_px"]) > 90 and f(r["after_axis_protrude_px"]) > max(e_succ) + 10]
    line = max(e_succ) + 10
    rows = []
    for g in gm:
        t = tim.get((g["dataset"], g["run"]), {})
        gap_open = f(g.get("before_axis_marker_gap_px")) or 137.0
        sc = 137.0 / gap_open if g["dataset"] == "scripted_v1" else 1.0
        o = {"dataset": g["dataset"], "run": g["run"], "kind": g["kind"],
             "hand_in_frame": g["run"] in HAND,
             "pre1p5_px": g.get("pre1p5_axis_protrude_px"), "pre0p5_px": g.get("pre0p5_axis_protrude_px"),
             "before_px": g.get("before_axis_protrude_px"), "label_frame_px": g.get("ep_label_axis_protrude_px"),
             "after_px": g.get("after_axis_protrude_px"), "after_gap_px": g.get("after_axis_marker_gap_px"),
             "scale_to_137": round(sc, 3),
             "E_before_px": g.get("before_protrude_px"), "E_after_px": g.get("after_protrude_px")}
        a = f(o["after_px"])
        o["after_px_scaled"] = None if a is None else round(a * sc, 1)
        o["after_mm_approx"] = None if a is None else round(a * sc / 2.6, 1)
        o["shallow_like_failures"] = (a is not None and a * sc > line and (f(o["after_gap_px"]) or 0) * sc > 90)
        for k in ("label_mode", "t_label_minus_cmd_s", "t_cmd_minus_stop_s", "stop_gap_before_s", "speed_pre0p3_deg_s",
                  "move_after_cmd_1p5s_deg", "d_j1_pre", "d_j2_pre", "d_j4_pre", "d_reach_pre_mm",
                  "d_reach_last0p5_mm", "d_h_pre_mm", "d_h_last0p5_mm", "grid_steps_pm1p5", "ep_steps_pm1p5",
                  "t_label_minus_grid_first1_s", "act-1_d_reach_mm", "act-1_max_joint_deg",
                  "correct_steps_before_label", "correct_d_reach_mm", "correct_d_h_mm", "correct_d_joints"):
            o[k] = t.get(k)
        rows.append(o)
    with open(OUT / "g_table.csv", "w", newline="") as fo:
        w = csv.DictWriter(fo, fieldnames=list(rows[0])); w.writeheader(); w.writerows(rows)
    print(f"E 成功の閉じたあと最大 {max(e_succ)} px → 浅いとみなす線 {line:.1f} px / E 浅い失敗 {sorted(e_shallow)}")

    def ok(v):
        v = f(v)
        return "—" if v is None or v > 100 else v

    def short(r):
        return r.replace("run_0001_", "").replace("pi05_", "")
    print("\n| 本 | 種類 | 閉じたモード | 命令−止まり 秒 | 閉じる前 0.3 秒 °/s | 前 1.5 秒 j1/j2/j4 ° | 前 1.5 秒 伸び/高さ mm | "
          "最後 0.5 秒 伸び/高さ mm | 1.5 秒前 px | 直前 px | ラベルのコマ px | 1.0 秒後 px | 浅い |")
    print("|" + "---|" * 13)
    for o in rows:
        if o["dataset"] != "corr_v5_main":
            continue
        hand = "（手）" if o["hand_in_frame"] else ""
        print(f"| {short(o['run'])} | {'補正入り' if o['kind'] == 'corr' else '教えた'} | {o['label_mode']} | "
              f"{o['t_cmd_minus_stop_s']} | {o['speed_pre0p3_deg_s']} | {o['d_j1_pre']}/{o['d_j2_pre']}/{o['d_j4_pre']} | "
              f"{o['d_reach_pre_mm']}/{o['d_h_pre_mm']} | {o['d_reach_last0p5_mm']}/{o['d_h_last0p5_mm']} | "
              f"{ok(o['pre1p5_px'])} | {ok(o['before_px'])}{hand} | "
              f"{ok(o['label_frame_px'])} | {o['after_px']} | {'**浅い**' if o['shallow_like_failures'] else ''} |")
    print("\n群ごと (閉じたあと px. 手の本も閉じたあとは数える)")
    groups = {"教えた 19": lambda o: o["dataset"] == "corr_v5_main" and o["kind"] == "teach",
              "教えた うち試す置き場所 (194818〜195120) 4": lambda o: o["dataset"] == "corr_v5_main" and o["run"][-6:] in ("194818", "194913", "195023", "195120"),
              "補正入り 11": lambda o: o["dataset"] == "corr_v5_main" and o["kind"] == "corr",
              "scripted_v1 20 (137px にそろえた)": lambda o: o["dataset"] == "scripted_v1",
              "E 成功 4": lambda o: o["dataset"] == "E_successes",
              "E 失敗 12": lambda o: o["dataset"] == "E_failures"}
    for name, fn in groups.items():
        sel = [o for o in rows if fn(o)]
        af = [o["after_px_scaled"] for o in sel if o["after_px_scaled"] is not None]
        bf = [f(o["before_px"]) * o["scale_to_137"] for o in sel if f(o["before_px"]) is not None and f(o["before_px"]) <= 100]
        n_sh = sum(o["shallow_like_failures"] for o in sel)
        print(f"- {name}: 直前 {min(bf):.0f}〜{max(bf):.0f} (中央値 {np.median(bf):.0f}) / 1.0 秒後 {min(af):.0f}〜{max(af):.0f} "
              f"(中央値 {np.median(af):.0f}) / 浅い (> {line:.0f}px) {n_sh} 本: "
              + ", ".join(short(o["run"]) for o in sel if o["shallow_like_failures"]))


if __name__ == "__main__":
    main()
