"""무가사(무음) 구간에서 **입이 쉬는가** 를 재현 가능한 정의로 잰다.

DoD 3 의 "몇 초 어긋나는지" 는 **지표와 임계를 안 박으면 답이 안 나온다** — 같은 클립이 지표를
motion 으로 두느냐 openness 로 두느냐에 따라 정지 시작이 2.62s 도 되고 2.46s 도 된다
(독립 verifier 재현 2026-08-08). 그래서 정의를 여기에 고정한다:

  정지(rest) = **그 클립 자신의 motion 평균의 `--thresh`(기본 0.4)배 미만**이 `--min-run`(기본 3)
  프레임 이상 연속되는 구간. 평균으로 정규화하므로 클립 간 절대 밝기·얼굴 크기 차이를 흡수한다.

★`--skip` 을 반드시 넘겨라 — LTX 는 f0 가 입력 이미지라 안 빼면 통계가 통째로 오염된다.
★`--end` 로 오디오 길이에 맞춰 잘라라 — LTX 는 영상이 오디오보다 길다.
"""
import argparse
import importlib.util

import numpy as np

spec = importlib.util.spec_from_file_location("an", "analyze.py")
an = importlib.util.module_from_spec(spec)
spec.loader.exec_module(an)
FPS = 24


def rests(video, box, skip=0, end_s=None, thresh=0.4, min_run=3, metric="motion"):
    openness, motion, _ = an.mouth_series(video, box)
    v = (motion if metric == "motion" else openness)[skip:]
    if end_s is not None:
        v = v[:max(1, int(end_s * FPS) - skip)]
    quiet = v < v.mean() * thresh
    out, i = [], 0
    while i < len(quiet):
        if quiet[i]:
            j = i
            while j < len(quiet) and quiet[j]:
                j += 1
            if j - i >= min_run:
                out.append(((i + skip) / FPS, (j + skip) / FPS))
            i = j
        else:
            i += 1
    return out, v


if __name__ == "__main__":
    ap = argparse.ArgumentParser()
    ap.add_argument("video")
    ap.add_argument("box")
    ap.add_argument("--skip", type=int, default=0)
    ap.add_argument("--end", type=float)
    ap.add_argument("--thresh", type=float, default=0.4)
    ap.add_argument("--min-run", type=int, default=3)
    ap.add_argument("--metric", default="motion", choices=["motion", "openness"])
    ap.add_argument("--gap", help="대조할 무가사 구간 'a-b' (초)")
    a = ap.parse_args()
    box = tuple(float(v) for v in a.box.split(","))
    runs, v = rests(a.video, box, a.skip, a.end, a.thresh, a.min_run, a.metric)
    print(f"{a.video} [{a.metric} · thresh {a.thresh} · skip {a.skip} · end {a.end}]")
    print(f"  정지 구간: {[(round(x, 2), round(y, 2)) for x, y in runs] or '없음'}")
    if a.gap:
        g0, g1 = (float(t) for t in a.gap.split("-"))
        ov = [(max(x, g0), min(y, g1)) for x, y in runs if y > g0 and x < g1]
        i, j = int(g0 * FPS) - a.skip, int(g1 * FPS) - a.skip
        print(f"  무가사 {g0}~{g1}s 대비: "
              + (f"정지 시작 {runs[[r[1] > g0 and r[0] < g1 for r in runs].index(True)][0]:.2f}s "
                 f"(오차 {runs[[r[1] > g0 and r[0] < g1 for r in runs].index(True)][0] - g0:+.2f}s) · "
                 f"덮는 길이 {sum(y - x for x, y in ov):.2f}s / {g1 - g0:.2f}s" if ov else "겹치는 정지 없음"))
        print(f"  무가사 구간 {a.metric} 평균 / 전체 평균 = {v[i:j].mean() / v.mean():.3f}")
