#!/usr/bin/env python3
"""make_queue.py — 夜に回す「条件 × 種」を queue.json に並べる（依頼 v12 の 4 章）

条件は Unity と同じ所から引く（手で写さない）:
  Assets/ServiceSim/Experiment/serving-robot-params.json の unity_conditions

最初の夜：6条件（③ fam_p1・⑦ fam_p5 × ロボット 0・1・2 台）× 種 0〜9 ＝ 60 回
          ＋ ③2台・すれ違い「入」× 種 0〜9 ＝ 10 回  … 合わせて 70 回
スタッフ4人・42組/時・上げ膳（mode=serve）・1往復3卓・レジ1つは、
その JSON の条件がそのまま持っている値なので、ここでは選び直さない。

すれ違い（依頼 v13 の 依頼1）は条件ごとに持たせる（pass_by: on / off）。
標準は off（基準データ cs:7 の18回と同じ動き）で、
--pass-by-extra で「その条件だけ on の回」を足す。
実行ファイルには -ssPassBy on|off で渡す（依頼 v12 の 2 章に足す引数）。

使い方（プロジェクトの中で）
  # 最初の夜（70回）
  python3 tools/make_queue.py --out queue.json \
      --pass-by-extra fam_p1_s4_r2_a42
  # 60回だけ
  python3 tools/make_queue.py --out queue.json
  python3 tools/make_queue.py --conditions fam_p1_s4_r1_a42 --seeds 1 --out /tmp/q.json

標準ライブラリだけで動く。Unity も台帳も読み書きしない。
"""
import argparse
import json
import os
import sys

HERE = os.path.dirname(os.path.abspath(__file__))
PROJECT = os.path.dirname(HERE)
PARAMS = os.path.join(PROJECT, 'Assets', 'ServiceSim', 'Experiment',
                      'serving-robot-params.json')

# 条件の名前の付け方は Unity の SimParams.Condition.Id と同じにする。
#   {店}_s{スタッフ}_r{ロボット}_a{来客/時}   例: fam_p1_s4_r1_a42
# 実行ファイルに -ssCondition で渡す字（依頼 v12 の 2 章）
def condition_id(c):
    return '%s_s%d_r%d_a%d' % (c['store'], int(c['staff']), int(c['robots']),
                               int(c['arrivals_per_h']))


def parse_seeds(text):
    """'0-9' / '0,1,2' / '3' を種の一覧にする"""
    out = []
    for part in str(text).split(','):
        part = part.strip()
        if not part:
            continue
        if '-' in part[1:]:                      # 先頭の - は負の数のため避ける
            a, b = part.split('-', 1)
            lo, hi = int(a), int(b)
            if hi < lo:
                lo, hi = hi, lo
            out.extend(range(lo, hi + 1))
        else:
            out.append(int(part))
    seen, uniq = set(), []
    for s in out:
        if s not in seen:
            seen.add(s)
            uniq.append(s)
    return uniq


def load_conditions(path):
    with open(path, encoding='utf-8') as f:
        d = json.load(f)
    meta = d.get('meta') or {}
    conds = []
    for item in d.get('unity_conditions') or []:
        c = item.get('condition') or {}
        missing = [k for k in ('store', 'staff', 'robots', 'arrivals_per_h', 'mode')
                   if k not in c]
        if missing:
            sys.exit('unity_conditions に %s がありません: %r' % (missing, c))
        conds.append(c)
    return meta, conds


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument('--params', default=PARAMS, help='serving-robot-params.json')
    ap.add_argument('--seeds', default='0-9', help='種（例 0-9 / 0,1,2）')
    ap.add_argument('--conditions', default='',
                    help='条件の名前をカンマ区切りで絞る（既定は6条件ぜんぶ）')
    ap.add_argument('--label', default='night', help='出力フォルダに入る名札')
    ap.add_argument('--pass-by-extra', default='',
                    help='すれ違い「入」でも回す条件（カンマ区切り）。'
                         'その条件だけ、同じ種で on の回を足す（依頼 v13）')
    ap.add_argument('--pass-by', default='off', choices=('off', 'on'),
                    help='ふだんの回のすれ違い（既定 off＝基準データと同じ動き）')
    ap.add_argument('--out', default=os.path.join(PROJECT, 'queue.json'))
    a = ap.parse_args()

    if not os.path.exists(a.params):
        sys.exit('試算の値が見つかりません: %s' % a.params)

    meta, conds = load_conditions(a.params)
    seeds = parse_seeds(a.seeds)
    if not seeds:
        sys.exit('--seeds が空です')

    want = [s.strip() for s in a.conditions.split(',') if s.strip()]
    if want:
        known = {condition_id(c) for c in conds}
        bad = [w for w in want if w not in known]
        if bad:
            sys.exit('知らない条件です: %s\n  使える条件: %s'
                     % (', '.join(bad), ', '.join(sorted(known))))
        conds = [c for c in conds if condition_id(c) in want]

    extra = [s.strip() for s in a.pass_by_extra.split(',') if s.strip()]
    known = {condition_id(c) for c in conds}
    bad = [w for w in extra if w not in known]
    if bad:
        sys.exit('--pass-by-extra に知らない条件があります: %s\n  使える条件: %s'
                 % (', '.join(bad), ', '.join(sorted(known))))

    def make(c, seed, pass_by):
        return {
            'condition': condition_id(c),
            'seed': seed,
            'pass_by': pass_by,          # 'on' / 'off'（実行ファイルへ -ssPassBy で渡す）
            'store': c['store'],
            'staff': int(c['staff']),
            'robots': int(c['robots']),
            'arrivals_per_h': int(c['arrivals_per_h']),
            'mode': c.get('mode', 'serve'),
            'trays': int(c.get('trays', 1)),
            'fragile_by': c.get('fragile_by', 'any'),
            'registers': int(c.get('registers', 1)),
        }

    runs = []
    for c in conds:
        for s in seeds:
            runs.append(make(c, s, a.pass_by))
    # すれ違い「入」でも回す条件を、同じ種で足す（依頼 v13）
    for c in conds:
        if condition_id(c) not in extra:
            continue
        if int(c['robots']) < 2:
            print('  ※ %s はロボットが %d 台なので、すれ違いは起きません（足しますが参考）'
                  % (condition_id(c), int(c['robots'])))
        for s in seeds:
            runs.append(make(c, s, 'on'))

    q = {
        'schema': 'servicesim-night-queue/0.1',
        'label': a.label,
        'params_model': meta.get('model', ''),
        'layout_generation': meta.get('layout_generation', ''),
        'params_source': os.path.relpath(a.params, PROJECT),
        'seeds': seeds,
        'conditions': [condition_id(c) for c in conds],
        'pass_by': a.pass_by,
        'pass_by_extra': extra,
        'count': len(runs),
        'runs': runs,
    }

    out_dir = os.path.dirname(os.path.abspath(a.out))
    if out_dir and not os.path.isdir(out_dir):
        os.makedirs(out_dir)
    with open(a.out, 'w', encoding='utf-8') as f:
        json.dump(q, f, ensure_ascii=False, indent=1)
        f.write('\n')

    print('並べました: %s' % a.out)
    print('  試算の版 %s / 店の形の版 %s' % (q['params_model'], q['layout_generation']))
    n_on = sum(1 for r in runs if r['pass_by'] == 'on')
    print('  条件 %d / 種 %s / 合わせて %d 回（すれ違い 切 %d 回・入 %d 回）'
          % (len(conds), ','.join(str(s) for s in seeds), len(runs), len(runs) - n_on, n_on))
    for cid in q['conditions']:
        mark = '（＋すれ違い入も %d 回）' % len(seeds) if cid in extra else ''
        print('    %s%s' % (cid, mark))


if __name__ == '__main__':
    main()
