"""比較動画のストックを、録画ストック（night/{夜の名前}）から順に作る（2026-10-31）
使い方: python3 make_compare_stock.py [--night 20261031_rec] [--out フォルダ] [--only N]   ※ できている mp4 は飛ばす
- 既定の夜は 20261031_rec（入口番・持ち場ありで撮り直した素材）。出力は stock/{夜の名前}/
- 1店7本：空いている・スタッフが多い・混んでいる・スタッフが少ない・普通（0台と2台）、普通の0台と1台・1台と2台
- 並べる順は、店 ③⑦④⑤⑥②、店の中は上の順（図の右上から）
- 片側は元の録画の半分の大きさ（幅1280）。縦は店の形に合わせる（縦横比を保つ）
- 30倍速（録画は15倍速なので1コマおき）
出力: videos/service_sim_compare/stock/{店}_{条件}_r{左}_vs_r{右}.mp4（字幕の控え .ass も）・進み具合は stock/progress.log
"""
import csv, sys, os, subprocess, bisect, time, tempfile
TMP = tempfile.mkdtemp(prefix='cmp_')   # 作業用（前の回の残りとぶつからないよう、毎回新しく作る）
FONT = os.environ.get('CMP_FONT', 'Noto Sans CJK JP')   # Mac で Noto がなければ CMP_FONT='Hiragino Sans' にする
MAXSEC = float(sys.argv[sys.argv.index('--max-sec') + 1]) if '--max-sec' in sys.argv else None   # 試しに頭の数秒だけ作る
HOME = os.path.expanduser('~')
U = os.environ.get('SIM_UNITY', HOME + '/mnt/KIOXIA/r2r2r-data/service_sim/unity')
NIGHT = sys.argv[sys.argv.index('--night') + 1] if '--night' in sys.argv else '20261031_rec'
OUTD = sys.argv[sys.argv.index('--out') + 1] if '--out' in sys.argv else os.environ.get('CMP_OUT', HOME + '/mnt/KIOXIA/r2r2r-data/videos/service_sim_compare/stock/' + NIGHT)
os.makedirs(OUTD, exist_ok=True)
NAME = {'fam_p0': '② 定番の郊外型', 'fam_p1': '③ 効率重視・入口が角', 'fam_p2': '④ 効率重視・小さめの店',
        'fam_p3': '⑤ ゆったり・入口が正面', 'fam_p4': '⑥ ゆったり・入口が横', 'fam_p5': '⑦ 効率重視・席を詰めた店'}
STORES = ['fam_p1', 'fam_p5', 'fam_p2', 'fam_p3', 'fam_p4', 'fam_p0']
CONDS = [('空いている', 'aki', 0, 2), ('スタッフが多い', 'stmany', 0, 2), ('混んでいる', 'komu', 0, 2),
         ('スタッフが少ない', 'stfew', 0, 2), ('普通・真ん中の人数', 'futsu', 0, 2), ('普通・真ん中の人数', 'futsu', 0, 1), ('普通・真ん中の人数', 'futsu', 1, 2)]
SPD = 2
rows = list(csv.DictReader(open(U + '/night/' + NIGHT + '/rec_index.csv', encoding='utf-8')))
IDX = {r['回の名前']: r for r in rows}
def find(store, cond, r):
    for x in rows:
        if store in x['条件の名前'] and x['条件の種類'] == cond and int(x['ロボット台数']) == r: return x
    raise KeyError((store, cond, r))
def ts(v):
    return f'{int(v // 3600)}:{int(v % 3600 // 60):02d}:{v % 60:05.2f}'
def clock(t):
    return f'{int(t // 3600)}:{int(t % 3600 // 60):02d}'
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
def make(L, R, out):
    W = 1280
    sw, sh = map(int, L['解像度'].split('x')); H = round(sh * W / sw / 2) * 2
    TOP, BOT = 150, 70
    store = L['条件の名前'].split('_')[1]; store = 'fam_' + store
    cap = f'{NAME[store]}　{L["条件の種類"]}：スタッフ{L["スタッフ"]}人・1時間{L["来客/時"]}組　同じお客さんの来方（種{L["種"]}）で並べた Unity の3Dシミュレーション　30倍速'
    ev = []; dur = 0
    for side, x in ((0, L), (1, R)):
        run = x['回の名前']; T, S, fr = info(run)
        vlen = len(fr) / 30 / SPD; dur = max(dur, vlen); px = side * W + 40
        ev.append((0, 99999, f'{{\\an7\\pos({px},18)\\fs46\\b1}}ロボット{x["ロボット台数"]}台'))
        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({px},84)\\fs30}}店の時刻 {clock(t)}　入店待ちの組 {wg}　片付け待ちの卓 {dirty}　動けなくなった 累計{n}回'))
        ev.append((vlen, 99999, f'{{\\an7\\pos({px},84)\\fs30\\b1}}おわり　入店待ちの平均 {float(x["入店待ち（分）"]):.1f}分　動けなくなった {x["動けなくなった（回）"]}回'))
    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&}} 配膳ロボット'))
    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',
           f'Style: D,{FONT},30,&H00F0F5F5,&H000D0D0D,&H000D0D0D,0,0,1,0,0,7,0,0,0', '', '[Events]', 'Format: Layer, Start, End, Style, Text']
    ass += [f'Dialogue: 0,{ts(a)},{ts(min(b, end))},D,{t}' for a, b, t in ev]
    af = os.path.join(TMP, os.path.basename(out)[:-4] + '.ass')
    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}/{x["回の名前"]}/recordings/{x["回の名前"]}_whole.mp4' for x in (L, R)]
    sel = f'select=not(mod(n\\,{SPD})),setpts=N/30/TB,'
    fc = (f'[0:v]{sel}scale={W}:{H},tpad=stop_mode=clone:stop_duration=900[a];[1:v]{sel}scale={W}:{H},tpad=stop_mode=clone:stop_duration=900[b];'
          f'[a][b]hstack=2,pad={2 * W}:{TOP + H + BOT}:0:{TOP}:color=0x0d0d0d,drawbox=x={W - 1}:y=0:w=3:h={TOP + H}:color=0xf5f5f0:t=fill,ass={af},trim=duration={(min(end, MAXSEC) if MAXSEC else end):.2f}[v]')
    tmp = os.path.join(TMP, os.path.basename(out))
    subprocess.run(['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', tmp], check=True)
    subprocess.run(['cp', tmp, out], check=True); os.remove(tmp)
    return end
jobs = [(s, c, k, a, b) for s in STORES for c, k, a, b in CONDS]
only = int(sys.argv[sys.argv.index('--only') + 1]) if '--only' in sys.argv else None
log = open(OUTD + '/progress.log', 'a', encoding='utf-8')
for n, (s, c, k, a, b) in enumerate(jobs, 1):
    if only and n != only: continue
    out = f'{OUTD}/{s.replace("fam_", "")}_{k}_r{a}_vs_r{b}.mp4'
    if os.path.exists(out): continue
    t0 = time.time()
    try:
        end = make(find(s, c, a), find(s, c, b), out)
        log.write(f'{time.strftime("%m-%d %H:%M")} できた {n}/{len(jobs)} {os.path.basename(out)} 長さ{end:.0f}秒 作るのに{time.time() - t0:.0f}秒\n')
    except Exception as e:
        log.write(f'{time.strftime("%m-%d %H:%M")} 失敗 {n}/{len(jobs)} {os.path.basename(out)} {e}\n')
    log.flush()
log.write(f'{time.strftime("%m-%d %H:%M")} 全部おわり\n')
