"""サービスロボット導入シミュレーションの録画ストック（night/20261030_rec）から、2本を左右に並べた比較動画を作る（2026-10-31）
使い方: python3 make_compare.py <左の回の名前> <右の回の名前> <左の見出し> <右の見出し> <上の説明> <出力の mp4> [倍速 既定2]
- 録画は 0.5 秒ごと・30 コマ/秒（15 倍速）。倍速 2 なら 1 コマおきに使い 30 倍速
- 上に、店の時刻・入店を待っている組・片付け待ちの卓・動けなくなった（累計）を1分ごとに出す（timeline.csv と stops.csv から）
- 最後に、その回の入店待ちの平均（rec_index.csv）を出す
"""
import csv, sys, os, subprocess, bisect
U = os.environ.get('SIM_UNITY', '/Volumes/KIOXIA/r2r2r-data/service_sim/unity')
L, R, HL, HR, CAP, OUT = sys.argv[1:7]
SPD = int(sys.argv[7]) if len(sys.argv) > 7 else 2
IDX = {r['回の名前']: r for r in csv.DictReader(open(U + '/night/20261030_rec/rec_index.csv', encoding='utf-8'))}
W, H, TOP, BOT = 1280, 880, 150, 70     # 片側の大きさ（2560x1760 を半分）・上下の帯

def info(run):
    d = f'{U}/{run}'
    T = [(float(r['t_s']), int(r['waiting_groups']), int(r['tables_dirty_untaken']) + int(r['tables_clearing'])) for r in csv.DictReader(open(d + '/timeline.csv'))]
    S = sorted(float(r['t_s']) for r in csv.DictReader(open(d + '/stops.csv')) if r['resolved'] == '動けなくなった')
    fr = [float(r[list(r.keys())[1]]) for r in csv.DictReader(open(f'{d}/recordings/{run}_whole_frames.csv'))]
    return T, S, fr, IDX[run]

def ts(v):  # 動画の秒 → ASS の時刻
    h = int(v // 3600); m = int(v % 3600 // 60); s = v % 60
    return f'{h}:{m:02d}:{s:05.2f}'

def clock(t):
    return f'{int(t // 3600)}:{int(t % 3600 // 60):02d}'

ev = []
dur = 0
for side, run, head in ((0, L, HL), (1, R, HR)):
    T, S, fr, ix = info(run)
    vlen = len(fr) / 30 / SPD
    dur = max(dur, vlen)
    x = side * W + 40
    ev.append((0, 99999, f'{{\\an7\\pos({x},18)\\fs46\\b1}}{head}'))
    for i, (t, wg, dirty) in enumerate(T):
        a = bisect.bisect_left(fr, t) / 30 / SPD
        b = (bisect.bisect_left(fr, T[i + 1][0]) / 30 / SPD) if i + 1 < len(T) else vlen
        if a >= vlen: break
        n = bisect.bisect_right(S, t)
        ev.append((a, b, f'{{\\an7\\pos({x},84)\\fs30}}店の時刻 {clock(t)}　入店待ちの組 {wg}　片付け待ちの卓 {dirty}　動けなくなった 累計{n}回'))
    ev.append((vlen, 99999, f'{{\\an7\\pos({x},84)\\fs30\\b1}}おわり　入店待ちの平均 {float(ix["入店待ち（分）"]):.1f}分　動けなくなった {ix["動けなくなった（回）"]}回'))
ev.append((0, 99999, f'{{\\an1\\pos(40,{TOP + H + BOT - 18})\\fs26}}{CAP}'))
ev.append((0, 99999, f'{{\\an3\\pos({2 * W - 40},{TOP + H + BOT - 18})\\fs26}}{{\\c&HE07A2A&}}●{{\\c&HF0F5F5&}} スタッフ　{{\\c&H22C8F5&}}●{{\\c&HF0F5F5&}} お客さん　{{\\c&HFFFFFF&}}●{{\\c&HF0F5F5&}} 配膳ロボット'))  # 色の見本（Unity の色に合わせた）
end = dur + 4
ass = ['[Script Info]', 'ScriptType: v4.00+', f'PlayResX: {2 * W}', f'PlayResY: {TOP + H + BOT}', '',
       '[V4+ Styles]', 'Format: Name, Fontname, Fontsize, PrimaryColour, OutlineColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV',
       'Style: D,Noto Sans CJK JP,30,&H00F0F5F5,&H000D0D0D,&H000D0D0D,0,0,1,0,0,7,0,0,0', '',
       '[Events]', 'Format: Layer, Start, End, Style, Text']
for a, b, txt in ev:
    ass.append(f'Dialogue: 0,{ts(a)},{ts(min(b, end))},D,{txt}')
af = '/tmp/' + os.path.basename(OUT)[:-4] + '.ass'   # libass が外付けの KIOXIA 上のファイルを読めないため、いったん /tmp に書く
open(af, 'w', encoding='utf-8').write('\n'.join(ass) + '\n')
open(OUT[:-4] + '.ass', 'w', encoding='utf-8').write('\n'.join(ass) + '\n')   # 控え
vin = [f'{U}/{r}/recordings/{r}_whole.mp4' for r in (L, R)]
sel = f'select=not(mod(n\\,{SPD})),setpts=N/30/TB,' if SPD > 1 else ''
fc = (f'[0:v]{sel}scale={W}:{H},tpad=stop_mode=clone:stop_duration=600[a];'
      f'[1:v]{sel}scale={W}:{H},tpad=stop_mode=clone:stop_duration=600[b];'
      f'[a][b]hstack=2,pad={2 * W}:{TOP + H + BOT}:0:{TOP}:color=0x0d0d0d,'
      f'drawbox=x={W - 1}:y=0:w=3:h={TOP + H}:color=0xf5f5f0:t=fill,ass={af},trim=duration={end:.2f}[v]')
cmd = ['ffmpeg', '-y', '-hide_banner', '-loglevel', 'error', '-i', vin[0], '-i', vin[1], '-filter_complex', fc, '-map', '[v]',
       '-r', '30', '-c:v', 'libx264', '-preset', 'veryfast', '-crf', '22', '-pix_fmt', 'yuv420p', '-movflags', '+faststart', OUT]
print(' '.join(cmd[:6]), '...'); subprocess.run(cmd, check=True)
print(OUT, f'{end:.1f}秒', os.path.getsize(OUT))
