#!/usr/bin/env python3
"""依頼153（pan149.py の写し：読む表を camspath153_*.csv に、静止画は still153_*.png で 10時の4つだけ）。もとの説明：依頼149（pan147.py の写しを直した：カメラは cams149、静止画は6つ×2つの時刻、エレベーター・トイレ）。もとの説明：依頼147（pan143.py の写しを直した：カメラは cams147、静止画7枚、題に人の動き）。もとの説明：依頼143：h1 の静止画（8枚）と1日の早回しの動画に、輪・パネル・凡例・上の帯を付ける（1920×1080）。依頼141 の pan141.py と同じ作り
（輪・凡例・パネルの形は依頼140 の pan140.py）。違うのは：病院 h1、ロボットの状態（台の前で待っている・積む所へ移る）、
行き先の名前を短く、道（緑・青（表）・青（裏口）・赤）、引きは西半分と東半分、問題を 廊下／部屋の中 に分けて帯に出す。

  python3 pan143.py stills <回のフォルダ> <出力フォルダ>
  python3 pan143.py video <回のフォルダ> flowday <出力.mp4> [並べる数]
  python3 pan143.py frame <回のフォルダ> flowday <コマ番号> <出力.png>
"""
import csv, json, math, os, subprocess, sys, tempfile
from multiprocessing import Pool
from PIL import Image, ImageDraw
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import pan140 as P
from cams149 import cams as cams143, still_cams

W, H, SW = P.W, P.H, P.SW
SHORT = {"救急室の処置室": "救急室", "内科外来の処置室": "内科（表）", "内科外来の処置室（裏口）": "内科（裏口）", "薬剤部の払い出し口": "薬剤部",
         "検査部の検体受付": "検体受付", "外来化学療法室の受付": "化学療法室"}
ROUTE_JA = {"green": "緑", "blue": "青（表）", "blue_back": "青（裏口）", "red": "赤"}
STATE = [("待ち", "台の前で待っている"), ("行き", "行き"), ("帰り", "帰り"), ("移る", "積む所へ移る")]
CAMJA = {c[0]: c[6] + ("" if c[0] == "top_all" else "・75°見下ろし・左30°") for c in cams143()}
CAMJA.update({n: CAMJA[c[0]] for n, c, tk in still_cams()})
RUN = None
DATA = {}


class PathCam:
    def __init__(self, proj, row, w, h, name):
        self.name = name
        self.w, self.h = w, h
        self.p = proj
        self.v = [[float(row["v%d%d" % (r, k)]) for k in range(4)] for r in range(4)]
        self.sx, self.sy = SW / w, H / h

    px = P.Cam.px
    ppm = P.Cam.ppm


def counts(ev, t0, t):
    c = {k: 0 for k in range(1, 7)}
    c["fast"] = c["wait2"] = c["room"] = 0
    for e in ev:
        te = float(e["t_s"])
        if te < t0 - 1e-6 or te > t + 1e-6: continue
        k = int(e["type"])
        if e["dir"] == "待ち":
            if k == 2: c["wait2"] += 1
            continue
        c[k] += 1
        if e.get("area") == "部屋の中" and k <= 5: c["room"] += 1
        if k == 2 and e.get("note") == "速いまま": c["fast"] += 1
    return c


def panel(im, row, ev, t, t0, title0, camja, summ, wide):
    d = ImageDraw.Draw(im)
    d.rectangle([SW, 0, W, H], fill=P.PANEL_BG)
    x0, xr = SW + 40, W - 40
    f_s, f_m, f_mb, f_big, f_num = P.font(False, 24), P.font(False, 27), P.font(True, 29), P.font(True, 54), P.font(True, 46)
    d.text((x0, 24), "病院 h1・人の動き・%s・%.1fm/秒・種%d" % (summ["plan"], summ["speed_mps"], summ["seed"]), font=f_s, fill=P.GRAY)
    d.text((x0, 56), "色 %s・車いすの場所 %s" % (P.SCHEME_JA["A"], "あり" if summ.get("wheelchair_marks") == "on" else "なし"), font=f_s, fill=P.GRAY)
    d.text((x0, 88), "カメラ %s" % camja, font=P.font(False, 22 if len(camja) > 18 else 24), fill=P.GRAY)
    d.text((x0, 122), P.clock(t), font=f_big, fill=P.INK)
    y = 206
    d.text((x0, y), "ロボット・スタッフがしていること", font=f_mb, fill=P.INK)
    y += 52
    d.text((x0, y), "ロボット 1台", font=f_m, fill=P.INK)
    y += 44
    cur = row["leg_dir"]
    for key, ja in STATE:
        on = key == cur
        d.text((x0 + 8, y), ja, font=f_m, fill=P.INK if on else (150, 150, 148))
        if on:
            d.ellipse([xr - 40, y + 2, xr - 4, y + 38], fill=P.WHITE, outline=(80, 80, 80), width=3)
            if key in ("行き", "帰り", "移る") and row["leg_from"]:
                d.text((xr - 52, y + 20), "%s→%s" % (SHORT.get(row["leg_from"], row["leg_from"]), SHORT.get(row["leg_to"], row["leg_to"])), font=f_s, fill=P.GRAY, anchor="rm")
            elif key == "待ち" and row["leg_from"]:
                d.text((xr - 52, y + 20), SHORT.get(row["leg_from"], row["leg_from"]), font=f_s, fill=P.GRAY, anchor="rm")
        y += 40
    y += 10
    d.text((x0, y), "スタッフ", font=f_m, fill=P.INK)
    y += 44
    rs = int(row["rescue_staff"])
    d.text((x0 + 8, y), "ロボットの助け", font=f_m, fill=P.INK if rs else (150, 150, 148))
    for i in range(rs):
        d.rectangle([xr - 36 - i * 44, y + 3, xr - 4 - i * 44, y + 35], fill=P.BLUE)
    y += 52
    d.line([x0, y, xr, y], fill=P.LINE, width=2)
    y += 14
    d.text((x0, y + 8), "大廊下の人（x0〜70）", font=f_m, fill=P.INK)
    d.text((xr, y + 28), row["hall_people"], font=f_num, fill=P.INK, anchor="rm")
    y += 66
    d.line([x0, y, xr, y], fill=P.LINE, width=2)
    y += 14
    d.text((x0, y), title0, font=f_mb, fill=P.INK)
    y += 46
    c = counts(ev, t0, t)
    for k in range(1, 6):
        d.ellipse([x0 + 6, y + 6, x0 + 30, y + 30], outline=P.COLORS[k], width=4)
        d.text((x0 + 44, y + 18), "%d %s" % (k, P.TYPES[k]), font=f_m, fill=P.INK, anchor="lm")
        d.text((xr, y + 18), str(c[k]), font=P.font(True, 34), fill=P.INK, anchor="rm")
        y += 42
    d.ellipse([x0 + 6, y + 6, x0 + 30, y + 30], outline=P.COLORS[2], width=4)
    d.text((x0 + 44, y + 18), "台の前で待っている間のひやり", font=f_s, fill=P.INK, anchor="lm")
    d.text((xr, y + 18), str(c["wait2"]), font=P.font(True, 34), fill=P.INK, anchor="rm")
    y += 48
    d.rectangle([x0 + 9, y + 5, x0 + 27, y + 23], outline=P.FAST_COL, width=3)
    d.rectangle([x0 + 4, y, x0 + 32, y + 28], outline=P.FAST_COL, width=2)
    d.text((x0 + 44, y + 14), "うち速いままのひやり（走っている間） %d" % c["fast"], font=f_s, fill=P.INK, anchor="lm")
    y += 36
    d.rectangle([x0 + 5, y + 1, x0 + 31, y + 27], outline=(255, 210, 0), width=4)
    d.line([x0 + 9, y + 5, x0 + 27, y + 23], fill=P.OVER_COL, width=5)
    d.line([x0 + 9, y + 23, x0 + 27, y + 5], fill=P.OVER_COL, width=5)
    d.text((x0 + 44, y + 14), "止まりきれなかった %d" % c[6], font=f_s, fill=P.INK, anchor="lm")
    y += 38
    note = "輪＝問題の起きた所（10秒）" + ("・白い輪＝ロボット" if wide else "")
    d.text((x0, min(y + 2, H - 34)), note, font=P.font(False, 21), fill=P.GRAY)
    return c


def legend_bottom(im):
    box = im.crop((0, H - 104, SW, H))
    P.legend(box)
    im.paste(box, (0, H - 104))


def load(run, cam):
    rows = [r for r in csv.DictReader(open(os.path.join(run, "look139", "look_frames.csv"), encoding="utf-8")) if r["kind"] == "path" and r["cam"] == cam]
    paths = [r for r in csv.DictReader(open(os.path.join(run, "look139", "look_path.csv"), encoding="utf-8")) if r["cam"] == cam]
    base = os.path.dirname(os.path.dirname(run))
    meta = [r for r in csv.DictReader(open(os.path.join(base, "camspath153_meta.csv"), encoding="utf-8")) if r["cam"] == cam]
    assert len(rows) == len(paths), (len(rows), len(paths))
    if cam == "flowday":
        assert len(rows) == len(meta), (len(rows), len(meta))
        for a, b in zip(rows, meta):
            assert abs(float(a["t_s"]) - float(b["t_s"])) < 0.051, (a["t_s"], b["t_s"])
    j = json.load(open(os.path.join(run, "look139", "cam_%s.json" % cam), encoding="utf-8"))
    ev = list(csv.DictReader(open(os.path.join(run, "events.csv"), encoding="utf-8")))
    summ = json.load(open(os.path.join(run, "summary.json"), encoding="utf-8"))
    trips = {r["trip_no"]: r for r in csv.DictReader(open(os.path.join(run, "trips.csv"), encoding="utf-8"))}
    return dict(rows=rows, paths=paths, meta=meta, proj=j["projectionMatrix"], w=j["width"], h=j["height"], ev=ev, summ=summ, cam=cam, trips=trips)


def compose(i):
    D = DATA
    r, pr = D["rows"][i], D["paths"][i]
    t = float(r["t_s"])
    src = Image.open(os.path.join(RUN, "look139", r["file"])).convert("RGB").resize((SW, H), Image.LANCZOS)
    im = Image.new("RGB", (W, H), P.PANEL_BG)
    im.paste(src, (0, 0))
    m = D["meta"][i]
    a = float(m["near_a"])
    wide = a < 0.5
    cam = PathCam(D["proj"], pr, D["w"], D["h"], "w_" if wide else "n_")
    P.WIDE.add("w_")
    P.overlay(im, cam, D["ev"], t, P.font(True, 22), (float(r["robot_x"]), float(r["robot_z"])))
    legend_bottom(im)
    sp = m["speed"]
    trip = r["trip_no"] if r["trip_no"] not in ("", "0") else ""
    sd = "西半分" if m["side"] == "west" else "東半分"
    camja = ("引き・%s・75°見下ろし・左30°" % sd) if a <= 0 else ("寄り・75°見下ろし・左30°・付いて行く" if a >= 1 else "引き⇄寄りに切り替え中")
    c = panel(im, r, D["ev"], t, 0.0, "7時30分からの1日の問題", camja, D["summ"], wide)
    route = ""
    if trip and trip in D["trips"]:
        route = "　%s便目・%s（%s→%s）" % (trip, ROUTE_JA.get(D["trips"][trip]["route"], ""), SHORT.get(D["trips"][trip]["from"], ""), SHORT.get(D["trips"][trip]["to"], ""))
    elif r["leg_dir"] == "移る":
        route = "　積む所へ移る（%s→%s）" % (SHORT.get(r["leg_from"], r["leg_from"]), SHORT.get(r["leg_to"], r["leg_to"]))
    t1 = "%s　店の時刻の%s%s" % (P.clock(t), sp, route if sp == "5倍" else "")
    t2 = "1日の問題（走っている間）　1 急に止まった %d・2 ひやり %d・3 人がよけさせられた %d・4 立ち往生 %d・5 横切り %d（うち部屋の中 %d）" % (
        c[1], c[2], c[3], c[4], c[5], c["room"])
    P_banner(im, t1, t2, "台の前で待っている間のひやり %d" % c["wait2"])
    return im


def P_banner(im, text1, text2, text3=""):
    lay = Image.new("RGBA", (SW, 140 if text3 else 104), (249, 247, 244, 225))
    d = ImageDraw.Draw(lay)
    d.text((24, 31), text1, font=P.font(True, 34 if len(text1) < 40 else 30), fill=P.INK, anchor="lm")
    d.text((24, 80), text2, font=P.font(False, 22), fill=P.INK, anchor="lm")
    if text3:
        d.text((24, 116), text3, font=P.font(False, 22), fill=P.INK, anchor="lm")
    im.paste(lay, (0, 0), lay)


def still(run, cam, out, what):
    D = load(run, cam)
    assert len(D["rows"]) == 1, (cam, len(D["rows"]))
    r, pr = D["rows"][0], D["paths"][0]
    t = float(r["t_s"])
    src = Image.open(os.path.join(run, "look139", r["file"])).convert("RGB").resize((SW, H), Image.LANCZOS)
    im = Image.new("RGB", (W, H), P.PANEL_BG)
    im.paste(src, (0, 0))
    wide = not cam.startswith("n_")
    pc = PathCam(D["proj"], pr, D["w"], D["h"], "w_" if wide else "n_")
    P.WIDE.add("w_")
    P.overlay(im, pc, D["ev"], t, P.font(True, 22), (float(r["robot_x"]), float(r["robot_z"])))
    legend_bottom(im)
    panel(im, r, D["ev"], t, 0.0, "7時30分からの1日の問題", CAMJA[cam], D["summ"], wide)
    P_banner(im, "%s　%s" % (P.clock(t), CAMJA[cam].split("・")[0] + "・" + CAMJA[cam].split("・")[1] if "・" in CAMJA[cam] else CAMJA[cam]), what)
    f = os.path.join(out, "still153_%s.png" % cam)
    im.save(f)
    return f, t


def init(run, cam):
    global RUN, DATA
    RUN = run
    DATA = load(run, cam)


def seg(args):
    run, cam, lo, hi, dst = args
    init(run, cam)
    p = subprocess.Popen([P.FFMPEG, "-y", "-loglevel", "error", "-f", "rawvideo", "-pix_fmt", "rgb24", "-s", "%dx%d" % (W, H), "-framerate", "25",
                          "-i", "-", "-c:v", "libx264", "-pix_fmt", "yuv420p", "-crf", "18", "-g", "50", dst], stdin=subprocess.PIPE)
    for i in range(lo, hi):
        p.stdin.write(compose(i).tobytes())
    p.stdin.close()
    p.wait()
    return dst


def main():
    mode, run = sys.argv[1], sys.argv[2]
    if mode == "stills":
        out = sys.argv[3]
        os.makedirs(out, exist_ok=True)
        base = os.path.dirname(os.path.dirname(run))
        whats = {r["cam"]: r["what"] for r in csv.DictReader(open(os.path.join(base, "camspath153_stills.csv"), encoding="utf-8"))}
        for c, _, _ in still_cams():
            if c not in whats: continue
            print(*still(run, c, out, whats[c]))
        return
    cam = sys.argv[3]
    if mode == "frame":
        init(run, cam)
        compose(int(sys.argv[4])).save(sys.argv[5])
        print(sys.argv[5], DATA["rows"][int(sys.argv[4])]["t_s"])
        return
    dst = sys.argv[4]
    nproc = int(sys.argv[5]) if len(sys.argv) > 5 else 8
    n = len(load(run, cam)["rows"])
    tmp = tempfile.mkdtemp(prefix="pan149_", dir=os.path.dirname(os.path.abspath(dst)))
    k = max(1, math.ceil(n / (nproc * 4)))
    jobs = [(run, cam, lo, min(n, lo + k), os.path.join(tmp, "s%04d.mp4" % (lo // k))) for lo in range(0, n, k)]
    with Pool(nproc) as pool:
        parts = pool.map(seg, jobs, chunksize=1)
    lst = os.path.join(tmp, "list.txt")
    open(lst, "w").write("".join("file '%s'\n" % q for q in parts))
    subprocess.check_call([P.FFMPEG, "-y", "-loglevel", "error", "-f", "concat", "-safe", "0", "-i", lst, "-c", "copy", "-movflags", "+faststart", dst])
    for q in parts: os.remove(q)
    os.remove(lst); os.rmdir(tmp)
    print(dst, n, "コマ", n / 25.0, "秒")


if __name__ == "__main__":
    main()
