#!/usr/bin/env python3
"""依頼155：確かめの回のまとめ（依頼143・147・149・154 の check と並べる）。
  python3 agg155.py <回のフォルダ> [<回のフォルダ> ...]"""
import csv, json, os, sys, statistics
from collections import Counter, defaultdict

HOSP = "/Volumes/KIOXIA/r2r2r-data/service_sim/unity/hospital"
R143 = {"hall": HOSP + "/20261110_floor143/runs/check143a_v06_hall_s0", "switch": HOSP + "/20261110_floor143/runs/check143c_v06_switch_s0"}
R147 = {"hall": HOSP + "/20261110_people147/runs/check147a_v06_hall_s0", "switch": HOSP + "/20261110_people147/runs/check147c_v06_switch_s0"}
R149 = {"hall": HOSP + "/20261111_flow149/runs/check149a_v06_hall_s0", "switch": HOSP + "/20261111_flow149/runs/check149c_v06_switch_s0"}
R154 = {"hall": HOSP + "/20261111_flow154/runs/check154a_v06_hall_s0", "switch": HOSP + "/20261111_flow154/runs/check154c_v06_switch_s0"}
EST = {"busy": (2, 8), "quiet": (1, 3)}   # assumptions.json crowd_people.flow_estimate
H1 = "/Users/shimazueiichi/Robotics_UX/ServiceSim/Assets/ServiceSim/Hospital/Data/hospital_h1-155.json"
TN = {1: "急に止まった", 2: "ひやり", 3: "人がよけさせられた", 4: "立ち往生", 5: "横切り", 6: "止まりきれなかった"}
LAY = json.load(open(H1, encoding="utf-8"))
COR = {c["name"]: c["rect"] for c in LAY["layout"]["corridors"]}
STAFF_COR = ["職員の中廊下", "西の縦廊下", "東の縦廊下", "外来の奥の職員通路", "東の縦廊下（北）"]
WAIT = ["総合受付・会計の待合", "お薬の窓口の待合", "生理検査の待合", "化学療法の受付・待合", "救急の待合"]
MIDW = ["内科の中待合", "ほかの外来の中待合"]
SCOR = ["職員の中廊下", "西の縦廊下", "東の縦廊下", "外来の奥の職員通路"]
ENT = ["正面玄関", "玄関の通り"]
ELV = ["エレベーターホール", "エレベーター"]
TOI = ["トイレ（西）", "トイレ（東）", "化学療法のトイレ"]
ROLES = ("外来の患者", "化学療法の患者", "救急の患者", "看護師", "医師", "薬剤師", "検査技師", "事務")


def inr(r, x, z, e=1e-4):
    return r[0] - e <= x <= r[2] + e and r[1] - e <= z <= r[3] + e


def area3(x, z):
    if inr(COR["外来の大廊下"], x, z): return "外来の大廊下"
    if any(inr(COR[n], x, z) for n in STAFF_COR): return "職員の廊下"
    if inr(COR["玄関の通り"], x, z): return "玄関の通り"
    return "部屋の中"


def rd(run, f):
    return list(csv.DictReader(open(os.path.join(run, f), encoding="utf-8")))


def one(run):
    s = json.load(open(os.path.join(run, "summary.json"), encoding="utf-8"))
    ev = rd(run, "events.csv")
    legs = {l["leg_id"]: l for l in rd(run, "legs.csv")}
    run_c, wait_c, ar = Counter(), Counter(), defaultdict(Counter)
    fast = 0
    kinds = defaultdict(Counter)
    for e in ev:
        k = int(e["type"])
        d = legs[e["leg_id"]]["dir"] if e["leg_id"] in legs else ""
        if d == "待ち":
            wait_c[k] += 1
            continue
        run_c[k] += 1
        kinds[k][e["partner_kind"] or "なし"] += 1
        if k == 2 and e["note"] == "速いまま": fast += 1
        ar[area3(float(e["robot_x"]), float(e["robot_z"]))][k] += 1
    return dict(s=s, ev=ev, legs=legs, run=run_c, wait=wait_c, area=ar, fast=fast, kinds=kinds,
                trips=rd(run, "trips.csv"), moves=rd(run, "moves.csv"), resc=rd(run, "rescues.csv"))


def line(c, ks=range(1, 7)):
    return "／".join("%d" % c[k] for k in ks)


def hm(t):
    t = int(round(t)) + 7 * 3600 + 1800
    return "%d:%02d" % (t // 3600, (t % 3600) // 60)


def halls(run):
    return {float(r["t_s"]): int(r["hall_people"]) for r in rd(run, "crowd.csv")}


def zones_table(run, s, run149):   # run149 ＝ 比べる相手（依頼154）
    zl = rd(run, "zones147.csv")
    byt = defaultdict(lambda: Counter())
    for r in zl:
        t = float(r["t_s"]); n = int(r["patients"]) + int(r["staff"])
        z = r["zone"]
        if z in WAIT: c = "待合"
        elif z in MIDW: c = "中待合"
        elif z in SCOR: c = "職員の廊下"
        elif z == "外来の大廊下": c = None
        elif z in ENT: c = "玄関"
        elif z in ELV: c = "エレベーターホール"     # ホールと中（かご）を合わせる
        elif z in TOI: c = "トイレ"
        else: c = "部屋の中"
        if c: byt[t][c] += n
        byt[t]["all"] += n
    crowd = rd(run, "crowd.csv")
    hall = {float(r["t_s"]): (int(r["hall_people"]), int(r["hall_target"]), int(r["east_people"])) for r in crowd}
    h147 = halls(run149)
    e149 = {float(r["t_s"]): int(r["east_people"]) for r in rd(run149, "crowd.csv")}
    end = s["sim_s"]
    print("時刻ごと（30分ごとの平均。1分ごとに数えた人数。患者・付き添い・職員・エレベーターから来た人の全部）")
    print("時刻          大廊下x0〜70  依頼154  目安(flow_estimate)  大廊下x70〜120  依頼154  待合   中待合  部屋の中  職員の廊下  玄関・玄関の通り  エレベーターホール  トイレ   全部")
    t0 = 0.0
    while t0 < end - 1:
        ts = [t for t in hall if t0 <= t < t0 + 1800]
        if not ts: break
        avg = lambda f: sum(f(t) for t in ts) / len(ts)
        hh = avg(lambda t: hall[t][0]); ee = avg(lambda t: hall[t][2])
        tg = "%d〜%d" % (EST["busy"] if 1800 <= t0 < 12600 else EST["quiet"])
        x49 = [e149[t] for t in e149 if t0 <= t < t0 + 1800]
        e49 = sum(x49) / len(x49) if x49 else float("nan")
        t47 = [h147[t] for t in h147 if t0 <= t < t0 + 1800]
        h47 = sum(t47) / len(t47) if t47 else float("nan")
        cs = {c: avg(lambda t: byt[t][c]) for c in ("待合", "中待合", "部屋の中", "職員の廊下", "玄関", "エレベーターホール", "トイレ", "all")}
        print("%s〜%s  %6.1f     %6.1f     %8s            %6.1f     %6.1f    %6.1f %6.1f  %7.1f   %7.1f       %7.1f          %7.1f       %6.1f  %6.1f" % (
            hm(t0), hm(t0 + 1800), hh, h47, tg, ee, e49, cs["待合"], cs["中待合"], cs["部屋の中"], cs["職員の廊下"], cs["玄関"], cs["エレベーターホール"], cs["トイレ"], cs["all"]))
        t0 += 1800
    busy = [hall[t][0] for t in hall if 1800 <= t < 12600]
    quiet = [hall[t][0] for t in hall if t < 1800 or t >= 12600]
    b47 = [h147[t] for t in h147 if 1800 <= t < 12600]; q47 = [h147[t] for t in h147 if t < 1800 or t >= 12600]
    allh = list(hall.values())
    print("外来の大廊下 x0〜70：混んでいる時間帯（8:00〜11:00）平均 %.1f 人・最大 %d 人（依頼154 %.1f・%d／目安 2〜8）／空いている時間帯 平均 %.1f 人・最大 %d 人（依頼154 %.1f・%d／目安 1〜3）" % (
        sum(busy) / len(busy), max(busy), sum(b47) / len(b47), max(b47), sum(quiet) / len(quiet), max(quiet), sum(q47) / len(q47), max(q47)))
    print("1日の平均 %.2f 人（依頼154 %.2f 人）" % (sum(h[0] for h in allh) / len(allh), sum(h147.values()) / len(h147)))
    pm = [hall[t][0] for t in hall if t >= 19800]          # 13:00 から（午後）
    pm49 = [h147[t] for t in h147 if t >= 19800]
    print("午後（13:00〜）平均 %.1f 人・最大 %d 人（依頼154 %.1f・%d／目安 1〜3）" % (sum(pm) / len(pm), max(pm), sum(pm49) / len(pm49), max(pm49)))
    ea = [hall[t][2] for t in hall]; e49a = list(e149.values())
    print("大廊下 x70〜120：1日の平均 %.2f 人・最大 %d 人（依頼154 %.2f 人・%d 人）" % (sum(ea) / len(ea), max(ea), sum(e49a) / len(e49a), max(e49a)))
    sc = [byt[t]["職員の廊下"] for t in byt]
    print("職員の廊下の人数：平均 %.2f 人・最大 %d 人" % (sum(sc) / len(sc), max(sc)))


def people(run, s):
    rows = rd(run, "people147.csv")
    for what in ("外来の患者", "付き添い", "化学療法の患者", "救急の患者", "病棟の患者", "病棟の看護師", "見舞いの人", "ほかの階の職員"):
        rs = [r for r in rows if r["what"] == what]
        st = sorted(float(r["stay_s"]) / 60 for r in rs if r["stay_s"])
        inside = sum(1 for r in rs if r["spawn_s"] and not r["exit_s"])
        never = sum(1 for r in rs if not r["spawn_s"])
        if len(st) >= 2:
            q = statistics.quantiles(st, n=10)
            print("%s %d 人：出た %d・回の終わりに中にいた %d・入らなかった %d／いた時間 中央値 %.1f 分・最短 %.1f・最長 %.1f（10〜90%%：%.1f〜%.1f）" % (
                what, len(rs), len(st), inside, never, statistics.median(st), st[0], st[-1], q[0], q[-1]))
        else:
            print("%s %d 人：出た %d・回の終わりに中にいた %d・入らなかった %d" % (what, len(rs), len(st), inside, never))
    pat = [r for r in rows if r["what"] in ("外来の患者",)]
    sk = Counter()
    for r in rows:
        for x in r["skipped"].split("|"):
            if x: sk[r["what"] + " " + x] += 1
    print("飛ばした行き先（人）：", "・".join("%s %d 人" % kv for kv in sk.most_common()) or "なし")
    print("初診・紹介 %d 人（外来の患者 %d 人）" % (sum(1 for r in pat if r["first_visit"] == "1"), len(pat)))
    # トイレ
    tw = Counter(r["what"] for r in rows if r["toilet_want"] == "1")
    td = Counter((r["what"], r["toilet"]) for r in rows if r["toilet"])
    tl = rd(run, "toilet149.csv")
    print("トイレ：行くと決めた人 %s／行った人 %s" % ("・".join("%s %d" % kv for kv in tw.items()),
          "・".join("%s→%s %d" % (k[0], k[1], v) for k, v in sorted(td.items()))))
    if tl:
        dur = [float(r["back_s"]) - float(r["leave_seat_s"]) for r in tl]
        walk = [float(r["arrive_s"]) - float(r["leave_seat_s"]) for r in tl]
        print("   席を立ってから戻るまで 中央値 %.1f 分（最短 %.1f・最長 %.1f）／席からトイレまで歩いた時間 中央値 %.0f 秒・最長 %.0f 秒" % (
            statistics.median(dur) / 60, min(dur) / 60, max(dur) / 60, statistics.median(walk), max(walk)))
        st = Counter(r["step"] for r in tl)
        print("   どこで待っている間に行ったか：", "・".join("%s %d" % kv for kv in st.most_common()))
    print("   行く時が来ないまま回が終わった %d・行けるトイレがない %d・戻るのを待って次へ行くのが遅れた %d 回（計 %.0f 秒）" % (
        s["flow_toilet_not_due"], s["flow_toilet_no_way"], s["flow_toilet_extend_n"], s["flow_toilet_extend_s"]))
    # エレベーター
    el = rd(run, "elevator149.csv")
    ec = Counter((r["what"], r["dir"]) for r in el)
    print("エレベーター（種類ごと）：", "・".join("%s %s %d" % (k[0], k[1], v) for k, v in sorted(ec.items())))
    wk = Counter(r["kind"] for r in el if r["what"] == "病棟の患者" and r["dir"] == "エレベーターから出る")
    print("   病棟の患者の形：", "・".join("%s %d" % kv for kv in wk.items()))
    print("出さなかった人（行き先へ歩いて行けない）：", s.get("flow_not_spawned"), "／救急の患者 %d 人（正面玄関から行ける %d）" % (s["flow_er_not_spawned"], s["flow_er_reach_from_main"]))
    xl = rd(run, "er_cross154.csv")
    for z in ("救急の待合", "救急の処置の場所"):
        rs = [r for r in xl if r["zone"] == z]
        byw = Counter(r["what"] for r in rs)
        sec = sorted(float(r["seconds"]) for r in rs)
        if sec:
            print("%s を通り抜けた外来の患者の役割の人 %d 人（%s）／そこにいた時間 合計 %.0f 秒・1人 中央値 %.1f 秒・最長 %.1f 秒" % (
                z, len(rs), "・".join("%s %d" % kv for kv in byw.items()), sum(sec), statistics.median(sec), sec[-1]))
        else: print("%s を通り抜けた外来の患者の役割の人 0 人" % z)
    print("病棟の看護師：検査の所の中のそばに立った %d・所の中の点 %d・大廊下の端で待った %d／化学療法のトイレへ行けず西・東にした %d" % (
        s.get("flow_ward_nurse_inside", -1), s.get("flow_ward_nurse_inside_random", -1), s["flow_ward_nurse_hall_waits"], s.get("flow_chemo_toilet_fallback", -1)))
    print("案A：付き添いを呼んだ %d・付き添った %d・間に合わない %d・時間切れ %d／付き添いなしで裏の通路にいた患者 %.1f 秒" % (
        s["flow_escort_calls"], s["flow_escorts"], s["flow_escort_late"], s["flow_escort_timeouts"], s["flow_back_no_escort_s"]))
    pr = [r for r in pat if "処置・検査室" in r["steps"]]
    went = sum(1 for r in pr if any(x.startswith("処置・検査室:") and x.split(":")[3] for x in r["steps"].split("|")))
    print("   処置・検査室・内視鏡室の行き先がある外来の患者 %d 人・着いた %d 人" % (len(pr), went))


def east_end(a):
    return sum(1 for e in a["ev"] if (a["legs"][e["leg_id"]]["dir"] if e["leg_id"] in a["legs"] else "") != "待ち" and float(e["robot_x"]) >= 95 and float(e["robot_z"]) >= 27)


def prob(a, b143, b147, b149, b154):
    L = [a, b154, b149, b147, b143]
    f = lambda g: " ／ ".join(g(x) for x in L)
    print("問題の数（1 急に止まった／2 ひやり／3 人がよけさせられた／4 立ち往生／5 横切り／止まりきれなかった）  依頼155 ／ 依頼154 ／ 依頼149 ／ 依頼147 ／ 依頼143")
    print("   走っている間 全部: " + f(lambda x: line(x["run"])) + "   （合計 " + f(lambda x: "%d" % sum(x["run"].values())) + "）")
    for ar in ("外来の大廊下", "職員の廊下", "部屋の中", "玄関の通り"):
        print("     %s: " % ar + f(lambda x: line(x["area"][ar])))
    print("   台の前で待っている間: " + f(lambda x: line(x["wait"])))
    print("   速いままのひやり: " + f(lambda x: "%d" % x["fast"]))
    print("   助け: " + f(lambda x: "%d" % len(x["resc"])))
    print("   大廊下の東の端（走っている間・ロボットの中心 x95 以上・z27 以上）: " + f(lambda x: "%d" % east_end(x)))


def chemo155(run, s):
    rows = rd(run, "people147.csv")
    ch = [r for r in rows if r["what"] == "化学療法の患者"]
    want = [r for r in ch if r["toilet_want"] == "1"]
    td = Counter(r["toilet"] or "（まだ・行く時が来ない）" for r in want)
    print("化学療法の患者 %d 人のうちトイレに行くと決まった %d 人：行き先 %s／化学療法のトイレへ行けず西・東にした %d 人" % (
        len(ch), len(want), "・".join("%s %d" % (k, td.get(k, 0)) for k in ("化学療法のトイレ", "トイレ（西）", "トイレ（東）", "行けない", "（まだ・行く時が来ない）")), s.get("flow_chemo_toilet_fallback", -1)))
    ids = set(r["id"] for r in ch)
    tl = [r for r in rd(run, "toilet149.csv") if r["what"] == "化学療法の患者"]
    for r in tl:
        print("   id %s %s 席を立つ %s 秒・着く %s 秒（歩き %.1f 秒）・戻る %s 秒（帰り %.1f 秒）" % (r["id"], r["toilet"], r["leave_seat_s"], r["arrive_s"],
              float(r["arrive_s"]) - float(r["leave_seat_s"]), r["back_s"], float(r["back_s"]) - float(r["stay_end_s"])))
    wy = rd(run, "chemo_way155.csv")
    bw = defaultdict(list)
    for r in wy: bw[r["what"]].append(r)
    print("トイレの前の通り道（x114〜115.5・z53〜60）にいた人 %d 人：" % len(wy))
    for w, rs in bw.items():
        print("   %s %d 人・いた時間 合計 %.0f 秒（1人 最長 %.1f 秒）・歩いている途中で止まっていた 合計 %.2f 秒・続けて止まった最長 %.2f 秒" % (
            w, len(rs), sum(float(r["seconds"]) for r in rs), max(float(r["seconds"]) for r in rs), sum(float(r["stopped_s"]) for r in rs), max(float(r["longest_stop_s"]) for r in rs)))
    print("   続けて止まった最長（全員）%.2f 秒" % s.get("flow_chemo_way_longest_stop_s", -1))


def staff155(run):
    st = rd(run, "staff147.csv")
    posts = defaultdict(set); own = {}
    for r in st: posts[r["role"]].add(r["zone"]); own[r["staff_id"] if "staff_id" in r else r["id"]] = r["zone"]
    mv = rd(run, "staff_moves147.csv")
    w = Counter(m["what"] for m in mv)
    print("職員の動き（終わったもの）：", "・".join("%s %d" % kv for kv in w.items()))
    c = Counter(); non = 0; ownn = 0
    for m in mv:
        if m["what"] != "持ち場から動く": continue
        c[(m["role"], m["dest"])] += 1
        if m["dest"] not in posts[m["role"]]: non += 1
        if m["dest"] == own[m["staff_id"]]: ownn += 1
    print("ふだんの行き来（持ち場から動く）の行き先：役割ごと・所ごとの回数（＊＝その役割の持ち場がある所）")
    for role in ("医師", "看護師", "薬剤師", "検査技師", "事務"):
        ks = sorted(k for k in c if k[0] == role)
        print("   %s 計 %d：%s" % (role, sum(c[k] for k in ks), "・".join("%s%s %d" % (k[1], "＊" if k[1] in posts[role] else "（持ち場でない）", c[k]) for k in ks)))
        print("      持ち場がある所：" + "・".join(sorted(posts[role])))
    print("   持ち場でない所へ行った回数 %d／自分の持ち場の所が行き先 %d" % (non, ownn))


def main():
    b143 = {k: one(v) for k, v in R143.items()}
    b147 = {k: one(v) for k, v in R147.items()}
    b149 = {k: one(v) for k, v in R149.items()}
    b154 = {k: one(v) for k, v in R154.items()}
    for run in sys.argv[1:]:
        a = one(run)
        s = a["s"]
        key = "switch" if s["plan_key"] == "switch" else "hall"
        print()
        print("== %s（%s）status %s／回の終わり %.1f 秒（%s）／最後の便が戻った %.1f 秒／実時間 %.1f 分" % (
            os.path.basename(run), s["plan"], s["status"], s["sim_s"], hm(s["sim_s"]), s["last_trip_end_s"], s["wall_s"] / 60))
        print("便 %d（走った %d・途中 %d・走れなかった %d）／移る %d（終えた %d）／出るのが遅れた 平均 %.1f 秒・最大 %.1f 秒" % (
            s["trips_scheduled"], s["trips_done"], s["trips_unfinished_at_end"], s["trips_not_run_after_end"] + s["trips_not_run_other"],
            s["moves"], s["moves_done"], s["delay_mean_s"], s["delay_max_s"]))
        print("瞬間移動 ロボット %d（1コマ最大 %.3f m）・人 %d（最大 %.3f m）／人の引き直し（180秒動けない）%d／行き先の近くで止まって待った %d" % (
            s["robot_jumps"], s["robot_max_step_m"], s["person_jumps"], s["person_max_step_m"], s.get("flow_stuck_regoes", 0), s.get("flow_near_stops", 0)))
        st = [e for e in a["ev"] if e["type"] == "4"]
        print("立ち往生 %d: %s" % (len(st), "／".join("%s〜%s秒 %s秒 (%s,%s) %s" % (e["t_s"], e["end_s"], e["value"], e["robot_x"], e["robot_z"], e["note"]) for e in st)))
        print("助け %d: %s" % (len(a["resc"]), "／".join("%s秒 (%s,%s) %s・%s" % (r["call_s"], r["x"], r["z"], r["partner"], r["outcome"]) for r in a["resc"])))
        print("ロボットの入らない所に中心が入った時間 %.2f 秒／どの所にも入っていない時間 %.2f 秒" % (s["robot_forbidden_s"], s["robot_no_zone_s"]))
        if s.get("people_mode") == "flow":
            print("人が access で入れない所に入った時間（役割ごと）：", "・".join("%s %.2f 秒" % (r, s["flow_forbidden_s_" + r]) for r in ROLES))
            print("どの所にも入っていない時間（役割ごと）：", "・".join("%s %.2f 秒" % (r, s["flow_no_zone_s_" + r]) for r in ROLES))
            print("職員 %d 人／持ち場から動いた %d 回・看護師が呼びに出た %d 回（看護師なし %d・間に合わない %d）" % (
                s["flow_staff_total"], s["flow_staff_moves"], s["flow_nurse_calls"], s["flow_nurse_calls_no_nurse"], s["flow_nurse_late"]))
            print("席 %d（使えない %d）・机などの横の点 %d・診察室 %d／所 %d 枚・区画 %d／壁の箱 %d・家具の箱 %d" % (
                s["flow_seats"], s["flow_seats_dropped"], s["flow_spots"], s["flow_consult_rooms"], s["flow_pieces"], s["flow_areas"], s["flow_wall_boxes"], s["flow_furniture_boxes"]))
            print("飛ばした行き先（理由）：", s["flow_skips"])
            people(run, s)
            chemo155(run, s)
            staff155(run)
            zones_table(run, s, R154[key])
            prob(a, b143[key], b147[key], b149[key], b154[key])
            lc = Counter()
            for e in a["ev"]:
                d = a["legs"][e["leg_id"]]["dir"] if e["leg_id"] in a["legs"] else ""
                lc[d] += 1
            print("   区間ごとの数（全部の種類）：", "・".join("%s %d" % kv for kv in lc.items()))
            for k in range(1, 7):
                if a["kinds"][k]: print("   相手 %d %s: %s" % (k, TN[k], "・".join("%s %d" % kv for kv in a["kinds"][k].most_common())))


if __name__ == "__main__":
    main()
