# -*- coding: utf-8 -*-
"""仮の病院 h1：h0b の形に、全部の部屋の中（小部屋・扉・家具）と4つの道を足す。

2026-11-10 エイイチと決めた：病院のフロアと道を全部作る。部屋の中まで全部、道は 3ルート＋裏口の道。
- 部屋・廊下・積む所と届け先の点は h0b と同じ（h0・h0b の JSON は変えない）
- 部屋の中の並びは、外来・検査部・薬剤部・外来化学療法室・救急室の典型をコードで起こした仮置き
- 道は 緑（救急室→検査部）・青 表（内科外来の処置室→検査部、大廊下を通る）・青 裏口（職員通路を通る）・赤（薬剤部→外来化学療法室）
- 高さは配膳のベースに合わせる：部屋・小部屋の壁 2.4m

使い方：python3 make_hospital_h1.py 出力先
出力：hospital_h1.json・hospital_h1.png（全体）・hospital_h1_rooms.png（部屋ごとの寄り）
"""
import json
import sys
from pathlib import Path

import make_hospital as mh

WALL_H = 2.4
H = {"ベンチ": 0.45, "いす": 0.45, "机": 0.72, "ベッド": 0.6, "カウンター": 1.0, "棚": 1.8,
     "分析装置": 1.3, "リクライニングチェア": 1.0, "台": 0.9, "機器": 1.2, "精算機": 1.5,
     "置き台": 0.9, "台車": 1.0, "島": 1.0, "車いすの場所": 0.0}

SUB = []    # 小部屋：部屋の中を壁で区切った所
FUR = []    # 家具
OPEN = []   # 部屋の壁のうち、廊下へ開いている所（壁を建てない）
DOORS = []  # 部屋の壁の扉（廊下や別の部屋へ出る）
INNER = []  # 部屋の中の通路（壁で囲まれていない、人やロボットが通る所）


def sub(room, name, rect, doors=(), what=""):
    SUB.append({"room": room, "name": name, "rect": [round(v, 2) for v in rect],
                "doors": [{"at": [round(d[0], 2), round(d[1], 2)], "w": d[2]} for d in doors], "what": what})


def fur(room, kind, rect, name=""):
    FUR.append({"room": room, "kind": kind, "name": name or kind, "rect": [round(v, 2) for v in rect], "h": H[kind]})


def door(room, x, y, w, to, name=""):
    DOORS.append({"room": room, "at": [round(x, 2), round(y, 2)], "w": w, "to": to, "name": name})


def opening(room, side, a, b, to):
    OPEN.append({"room": room, "side": side, "span": [a, b], "to": to})


def inner(room, name, rect, w):
    INNER.append({"room": room, "name": name, "rect": [round(v, 2) for v in rect], "w": w})


def benches(room, x0, x1, y, n_rows, pitch=1.5, length=4.0, gap=1.0):
    """x0〜x1 に長さ length のベンチを並べ、y から pitch ごとに n_rows 列"""
    for r in range(n_rows):
        x = x0
        while x + length <= x1 + 1e-6:
            fur(room, "ベンチ", (x, y + r * pitch, x + length, y + r * pitch + 0.45))
            x += length + gap


# ---------------- 内科外来（x0〜42, y31〜57.9）----------------
R = "内科外来"
door(R, 12.0, 31.0, 1.6, "外来の大廊下", "内科外来の処置室（積む所）")
door(R, 12.0, 57.9, 1.6, "外来の奥の職員通路", "内科外来の処置室（裏口）")
sub(R, "処置室", (0, 31, 22, 46), [(12.0, 31.0, 1.6), (12.0, 46.0, 1.6), (22.0, 42.05, 1.2)],
    "採血・点滴・検体を出す所。南の扉が積む所（表）")
fur(R, "置き台", (12.8, 38.4, 13.6, 40.4), "検体の置き台（ロボットに積む）")
for i in range(4):
    fur(R, "ベッド", (0.4, 33 + i * 2.4, 2.4, 33.9 + i * 2.4), "処置ベッド")
fur(R, "島", (3.5, 36.0, 10.5, 37.5), "看護師の作業台")
for x0, x1 in ((1.0, 10.5), (13.5, 21.0)):
    fur(R, "カウンター", (x0, 44.6, x1, 45.4), "採血台")
    n = int((x1 - x0) // 1.9)
    for k in range(n):
        fur(R, "いす", (x0 + 0.5 + k * 1.9, 43.6, x0 + 1.0 + k * 1.9, 44.1), "採血のいす")
inner(R, "処置室の裏の通路", (11.0, 46.0, 13.1, 57.9), 2.1)
sub(R, "器材室", (0, 46, 11, 57.9), [(11.0, 52.0, 1.0)])
for i in range(3):
    fur(R, "棚", (0.4, 47.5 + i * 3.2, 9.5, 48.1 + i * 3.2))
sub(R, "検体の準備室", (13.1, 46, 22, 57.9), [(13.1, 52.0, 1.0)])
fur(R, "台", (14.0, 47.0, 21.0, 47.8), "検体の仕分け台")
fur(R, "棚", (21.2, 48.5, 21.8, 56.5), "冷蔵庫")
opening(R, "S", 22.0, 42.0, "外来の大廊下")
inner(R, "内科の中待合", (22, 31, 42, 35.5), 4.5)
benches(R, 23.0, 41.5, 32.2, 2, pitch=1.4, length=4.5, gap=0.5)
for i in range(4):
    x0 = 22 + 5 * i
    sub(R, f"診察室{i + 1}", (x0, 35.5, x0 + 5, 41.0), [(x0 + 2.5, 35.5, 1.0), (x0 + 2.5, 41.0, 0.9)])
    fur(R, "机", (x0 + 0.3, 38.0, x0 + 1.5, 40.6), "診察机")
    fur(R, "ベッド", (x0 + 3.6, 37.0, x0 + 4.5, 39.0), "診察ベッド")
inner(R, "診察室の裏の通路", (22, 41.0, 42, 43.1), 2.1)
for i, nm in enumerate(("内視鏡室1", "内視鏡室2")):
    x0 = 22 + 10 * i
    sub(R, nm, (x0, 43.1, x0 + 10, 51.0), [(x0 + 5, 43.1, 1.4)])
    fur(R, "ベッド", (x0 + 3.0, 46.0, x0 + 5.0, 46.9), "検査ベッド")
    fur(R, "機器", (x0 + 6.0, 45.5, x0 + 7.2, 47.5), "内視鏡の機器")
sub(R, "回復室", (22, 51.0, 42, 57.9), [(32.0, 51.0, 1.4), (32.0, 57.9, 1.4)])
for i in range(6):
    fur(R, "ベッド", (23.0 + i * 3.0, 55.0, 23.9 + i * 3.0, 57.0), "回復ベッド")

# ---------------- ほかの外来（x42〜97.95, y31〜57.9）----------------
R = "ほかの外来"
X0, X1 = 42.0, 97.95
depts = ["外科・整形外科", "小児科", "眼科・耳鼻科"]
dw = (X1 - X0) / 3
opening(R, "S", X0, X1, "外来の大廊下")
for j, dn in enumerate(depts):
    a, b = X0 + j * dw, X0 + (j + 1) * dw
    inner(R, f"{dn}の中待合", (a, 31, b, 36), 5.0)
    fur(R, "カウンター", (a + 0.6, 31.5, a + 4.0, 32.3), f"{dn}の受付")
    benches(R, a + 5.0, b - 0.5, 32.3, 2, pitch=1.4, length=4.0, gap=0.6)
    cw = dw / 3
    for k in range(3):
        c0 = a + k * cw
        sub(R, f"{dn}の診察室{k + 1}", (c0, 36, c0 + cw, 41.5), [(c0 + cw / 2, 36, 1.0), (c0 + cw / 2, 41.5, 0.9)])
        fur(R, "机", (c0 + 0.3, 38.4, c0 + 1.5, 41.1), "診察机")
        fur(R, "ベッド", (c0 + cw - 1.3, 37.5, c0 + cw - 0.4, 39.5), "診察ベッド")
    inner(R, f"{dn}の裏の通路", (a, 41.5, b, 43.6), 2.1)
    sub(R, f"{dn}の処置・検査室", (a, 43.6, b, 57.9), [((a + b) / 2, 43.6, 1.4), ((a + b) / 2, 57.9, 1.4)])
    for k in range(4):
        fur(R, "ベッド", (a + 1.0 + k * 4.2, 54.5, a + 1.9 + k * 4.2, 56.5), "処置ベッド")
    fur(R, "島", (a + 5.0, 47.0, b - 5.0, 48.4), "作業台")
    fur(R, "棚", (a + 0.4, 44.5, a + 1.0, 51.5), "器材の棚")
    door(R, (a + b) / 2, 57.9, 1.4, "外来の奥の職員通路")

# ---------------- 外来化学療法室（x100.05〜120, y31〜60）----------------
R = "外来化学療法室"
door(R, 104.0, 31.0, 1.6, "外来の大廊下", "外来化学療法室の受付（届け先）")
fur(R, "カウンター", (105.2, 31.8, 109.0, 32.6), "受付（薬を受け取る）")
benches(R, 111.8, 119.5, 32.0, 2, pitch=1.4, length=3.5, gap=0.5)
fur(R, "島", (106.0, 40.0, 114.0, 43.0), "ナースステーション")
for i in range(9):
    fur(R, "リクライニングチェア", (100.6, 36.0 + i * 2.3, 102.2, 36.8 + i * 2.3), "点滴のいす")
for i in range(6):
    fur(R, "ベッド", (117.6, 35.5 + i * 2.6, 119.6, 36.4 + i * 2.6), "点滴のベッド")
sub(R, "準備室", (106.0, 52.0, 114.0, 60.0), [(110.0, 52.0, 1.2)])
fur(R, "台", (107.0, 58.6, 113.0, 59.4), "点滴の準備台")
sub(R, "トイレ", (114.5, 53.0, 120.0, 60.0), [(114.5, 55.0, 1.0)])

# ---------------- 薬剤部（x0〜23, y0〜13）----------------
R = "薬剤部"
door(R, 13.0, 13.0, 1.6, "職員の中廊下", "薬剤部の払い出し口（積む所）")
fur(R, "置き台", (14.2, 11.8, 16.5, 12.6), "払い出し台（ロボットに積む）")
sub(R, "無菌調製室", (0, 0, 7, 6), [(7.0, 3.0, 0.9)], "抗がん剤を調製する所")
fur(R, "機器", (0.5, 0.5, 2.0, 4.0), "安全キャビネット")
sub(R, "前室", (7, 0, 10, 6), [(8.5, 6.0, 0.9)])
fur(R, "台", (11.0, 2.0, 21.0, 3.0), "調剤台")
fur(R, "台", (11.0, 5.0, 21.0, 6.0), "調剤台")
fur(R, "機器", (19.5, 0.4, 22.6, 1.6), "錠剤の分包機")
fur(R, "棚", (0.4, 7.5, 9.0, 8.1), "薬品棚")
fur(R, "棚", (0.4, 9.8, 9.0, 10.4), "薬品棚")
fur(R, "机", (17.0, 8.0, 22.0, 9.5), "事務机")

# ---------------- お薬の窓口（x0〜23, y15.1〜27）----------------
R = "お薬の窓口"
opening(R, "N", 1.0, 17.0, "外来の大廊下")
door(R, 6.0, 15.1, 0.9, "職員の中廊下")
fur(R, "カウンター", (1.0, 18.5, 16.5, 19.3), "お薬の窓口")
benches(R, 2.0, 16.5, 21.0, 3, pitch=1.6, length=4.5, gap=0.5)
# 2026-11-11 トイレを足した（患者が待ち時間に行く所。大廊下に人がほとんど歩かなかったため）。大廊下から入る
sub(R, "トイレ（西）", (17.5, 15.1, 22.95, 27.0), [(20.2, 27.0, 1.6)], "男女・多目的。大廊下に面する")

# ---------------- 総合受付・会計・待合（x25.1〜70, y15.1〜27。玄関の通り x44〜52 をはさむ）----------------
R = "総合受付・会計・待合"
opening(R, "N", 25.1, 70.0, "外来の大廊下")
# 玄関の通り（x44〜52）は部屋の外枠の内側なので壁は建たない。x70 の東の辺は生理検査との壁なので開けない（2026-11-10 依頼143 で見つかった間違いを直した）
door(R, 32.5, 15.1, 0.9, "職員の中廊下")
# 2026-11-11 エレベーターを足した（ほかの階との行き来。病棟から検査に来る患者・見舞いの人・職員）。大廊下に面したホール
sub(R, "エレベーター（3基）", (25.1, 15.1, 31.0, 21.0), [(26.1, 21.0, 1.1), (28.05, 21.0, 1.1), (30.0, 21.0, 1.1)], "ほかの階との出入り口")
inner(R, "エレベーターホール", (25.1, 21.0, 31.0, 27.0), 6.0)
door(R, 58.0, 15.1, 0.9, "職員の中廊下")
fur(R, "カウンター", (32.0, 16.6, 43.6, 17.4), "総合受付")
fur(R, "カウンター", (52.4, 16.6, 63.0, 17.4), "会計")
for i in range(4):
    fur(R, "精算機", (64.0 + i * 1.4, 15.5, 65.0 + i * 1.4, 16.3), "自動精算機")
benches(R, 31.8, 43.0, 19.5, 4, pitch=1.6, length=3.5, gap=0.5)
benches(R, 53.0, 69.5, 19.5, 4, pitch=1.6, length=4.0, gap=0.6)

# ---------------- 倉庫（x25.1〜44, y0〜13）----------------
R = "倉庫"
door(R, 35.0, 13.0, 1.8, "職員の中廊下")
for i in range(4):
    fur(R, "棚", (26.0, 1.0 + i * 2.4, 42.0, 1.6 + i * 2.4))
fur(R, "台車", (26.0, 10.6, 32.0, 12.4), "台車置き場")

# ---------------- 階段・設備（h0 で部屋のなかった所）----------------
EXTRA_ROOMS = [("階段・設備（西）", 23.0, 0.0, 25.1, 13.0, "その他"),
               ("階段・設備（東）", 98.0, 0.0, 100.1, 13.0, "その他")]

# ---------------- 正面玄関（x44〜52, y0〜13）----------------
R = "正面玄関"
opening(R, "N", 44.0, 52.0, "職員の中廊下・玄関の通り")
sub(R, "風除室", (44, 0, 52, 4), [(48.0, 0.0, 3.0), (48.0, 4.0, 3.0)], "自動扉が2つ")
fur(R, "台", (44.3, 5.0, 45.3, 10.0), "車いす置き場")
fur(R, "カウンター", (50.2, 6.0, 51.7, 9.0), "案内")

# ---------------- 医事・事務（x52〜70, y0〜13）----------------
R = "医事・事務"
door(R, 55.0, 13.0, 0.9, "職員の中廊下")
door(R, 67.0, 13.0, 0.9, "職員の中廊下")
fur(R, "机", (54.0, 3.0, 66.0, 4.6), "事務机の島")
fur(R, "机", (54.0, 6.5, 66.0, 8.1), "事務机の島")
fur(R, "机", (54.0, 9.6, 66.0, 11.2), "事務机の島")
fur(R, "棚", (68.4, 1.0, 69.6, 10.0), "書庫")

# ---------------- 中央検査室（x70〜98, y0〜13）----------------
R = "中央検査室"
door(R, 80.0, 13.0, 1.6, "職員の中廊下", "検査部の検体受付（届け先）")
door(R, 94.0, 13.0, 0.9, "職員の中廊下")
fur(R, "置き台", (81.3, 11.6, 85.0, 12.4), "検体受付の台（ロボットから受け取る）")
fur(R, "台", (78.0, 8.5, 84.0, 9.5), "検体の仕分け台")
fur(R, "分析装置", (72.0, 1.0, 96.0, 2.2), "分析装置（生化学・免疫）")
fur(R, "分析装置", (86.0, 5.0, 96.0, 6.4), "分析装置（血液）")
fur(R, "分析装置", (86.0, 8.5, 96.0, 9.9), "検体の搬送ライン")
fur(R, "棚", (70.4, 8.0, 71.2, 12.0), "冷蔵庫")
fur(R, "机", (72.0, 5.0, 78.0, 6.2), "検査技師の机")

# ---------------- 生理検査（x70〜98, y15.1〜27）----------------
R = "生理検査"
opening(R, "N", 70.0, 98.0, "外来の大廊下")
inner(R, "生理検査の待合", (70, 21.9, 98, 27), 5.1)
fur(R, "カウンター", (70.3, 22.8, 71.1, 25.8), "生理検査の受付")
benches(R, 76.0, 97.5, 24.2, 2, pitch=1.4, length=4.0, gap=0.6)
names = ["心電図", "心エコー", "腹部エコー", "肺機能", "脳波"]
cw = 28.0 / 6
# 2026-11-11 東の端（もとは血圧脈波の検査室）をトイレ（東）にした。生理検査の待合から入る
sub(R, "トイレ（東）", (70 + 5 * cw, 15.1, 98.0, 21.9), [(70 + 5.5 * cw, 21.9, 1.6)], "男女・多目的")
for k, nm in enumerate(names):
    c0 = 70 + k * cw
    sub(R, f"{nm}の検査室", (c0, 15.1, c0 + cw, 21.9), [(c0 + cw / 2, 21.9, 1.0)])
    fur(R, "ベッド", (c0 + 1.0, 16.0, c0 + 3.0, 16.9), "検査ベッド")
    fur(R, "機器", (c0 + 3.4, 16.0, c0 + 4.3, 17.2), "検査の機器")

# ---------------- 救急室（x100.1〜120, y0〜27）----------------
R = "救急室"
door(R, 100.1, mh.STAFF_C, 1.6, "職員の中廊下", "救急室の処置室（積む所）")
door(R, 110.0, 27.0, 1.6, "外来の大廊下", "歩いて来た人の入口")
door(R, 120.0, 5.0, 2.4, "外（救急車）", "救急車の入口")
fur(R, "置き台", (100.6, 15.4, 102.6, 16.2), "検体の置き台（ロボットに積む）")
sub(R, "初療室", (110, 0, 120, 6), [(115.0, 6.0, 1.8)], "重い人を最初に診る所")
fur(R, "ベッド", (113.5, 2.5, 116.5, 3.5), "初療のベッド")
sub(R, "X線室", (100.1, 0, 108, 6), [(104.0, 6.0, 1.4)])
fur(R, "機器", (102.0, 1.5, 106.0, 3.5), "X線の機器")
for i in range(6):
    fur(R, "ベッド", (117.5, 8.0 + i * 2.4, 119.5, 8.9 + i * 2.4), "処置ベッド")
fur(R, "島", (106.0, 11.0, 111.0, 15.0), "ナースステーション")
benches(R, 101.0, 109.5, 22.0, 2, pitch=1.4, length=4.0, gap=0.5)


# 車いすの場所（2026-11-10 エイイチと決めた）：待合ごとに、通路に面したベンチの列の端に 1.5m 四方を足す。
# 指針（東京都 UD チェックリスト：待合に車椅子使用者が利用しやすいスペースの確保に配慮）にならう。列の端に置く形は仮置き。
# 「ない」条件（車いすの人がいすの間・通路で待つ）は試算・Unity の条件で選ぶ。床の印だけで高さはない
WC = 1.5
WAITING = [  # 待合の名前, 部屋, 待合の範囲, 通路（開いている側）の向き, 数
    ("内科の中待合", "内科外来", (22, 31, 42, 35.5), "S", 1),
    ("外科・整形外科の中待合", "ほかの外来", (42, 31, 60.65, 36), "S", 1),
    ("小児科の中待合", "ほかの外来", (60.65, 31, 79.3, 36), "S", 1),
    ("眼科・耳鼻科の中待合", "ほかの外来", (79.3, 31, 97.95, 36), "S", 1),
    ("化学療法の受付・待合", "外来化学療法室", (109.5, 31, 120, 35), "S", 1),
    ("お薬の窓口の待合", "お薬の窓口", (0, 19.3, 17.5, 27), "N", 1),
    ("総合受付の待合", "総合受付・会計・待合", (31.0, 17.4, 44, 27), "N", 2),
    ("会計の待合", "総合受付・会計・待合", (52, 17.4, 70, 27), "N", 2),
    ("生理検査の待合", "生理検査", (70, 21.9, 98, 27), "N", 1),
    ("救急の待合", "救急室", (100.1, 20, 120, 27), "N", 1),
]


def place_wheelchair_spaces():
    placed = []
    for wname, room, (zx0, zy0, zx1, zy1), side, n in WAITING:
        benches_in = [f for f in FUR if f["kind"] == "ベンチ" and f["room"] == room
                      and zx0 <= f["rect"][0] and f["rect"][2] <= zx1 and zy0 <= f["rect"][1] and f["rect"][3] <= zy1]
        cands = []
        for b in benches_in:
            bx0, by0, bx1, by1 = b["rect"]
            for x0 in (bx1 + 0.15, bx0 - 0.15 - WC):
                for y0 in (by0 - (WC - (by1 - by0)) / 2,):
                    cands.append((x0, y0, x0 + WC, y0 + WC))
        doors = [d["at"] for d in DOORS] + [dd["at"] for sb in SUB for dd in sb["doors"]]
        stops = [p for pts in INSIDE.values() for p in pts]
        ok = []
        for c in cands:
            if c[0] < zx0 or c[1] < zy0 or c[2] > zx1 or c[3] > zy1:
                continue
            if any(c[0] < f["rect"][2] + 0.05 and c[2] > f["rect"][0] - 0.05 and c[1] < f["rect"][3] + 0.05
                   and c[3] > f["rect"][1] - 0.05 for f in FUR):
                continue
            if any(c[0] - 1.0 < x < c[2] + 1.0 and c[1] - 1.0 < y < c[3] + 1.0 for x, y in doors + stops):
                continue
            if any(c[0] < q[2] and c[2] > q[0] and c[1] < q[3] and c[3] > q[1] for q in placed):
                continue
            # 通路（開いている側）に近いほどよい。同じなら外側の端
            edge = (c[1] - zy0) if side == "S" else (zy1 - c[3])
            ok.append((round(edge, 2), -abs((c[0] + c[2]) / 2 - (zx0 + zx1) / 2), c))
        ok.sort(key=lambda t: (t[0], t[1]))
        got = 0
        for _, _, c in ok:
            if got >= n:
                break
            if any(c[0] < q[2] and c[2] > q[0] and c[1] < q[3] and c[3] > q[1] for q in placed):
                continue
            fur(room, "車いすの場所", c, f"車いすの場所（{wname}）")
            placed.append(c)
            got += 1
        if got < n:
            print("車いすの場所が置けない：", wname, got, "/", n)


# ロボットが部屋に入って止まる所（2026-11-10 エイイチ：搬送ロボットは部屋の中にも入っていく前提）
# 扉の点（廊下の側）から、部屋の中の台の前で止まる点までの道。縦横だけ
INSIDE = {
    "内科外来の処置室": [(12.0, 31.0), (12.0, 39.4)],
    "内科外来の処置室（裏口）": [(12.0, 57.9), (12.0, 46.0), (12.0, 39.4)],
    "薬剤部の払い出し口": [(13.0, 13.0), (13.0, 11.0), (15.35, 11.0)],
    "外来化学療法室の受付": [(104.0, 31.0), (104.0, 33.4), (107.1, 33.4)],
    "検査部の検体受付": [(80.0, 13.0), (80.0, 10.55), (83.15, 10.55)],
    "救急室の処置室": [(100.1, 14.05), (101.6, 14.05), (101.6, 14.75)],
}
ROBOT_R = 0.35  # ロボットの半径の目安（W3 の幅 0.7m の半分）


def check_inside():
    """部屋の中の道が家具に当たらないか（ロボットの半径＋0.1m の余白）"""
    probs = []
    for name, pts in INSIDE.items():
        for (ax, ay), (bx, by) in zip(pts, pts[1:]):
            lo_x, hi_x, lo_y, hi_y = min(ax, bx), max(ax, bx), min(ay, by), max(ay, by)
            for f in FUR:
                x0, y0, x1, y1 = f["rect"]
                m = ROBOT_R + (0.0 if f["kind"] == "置き台" else 0.1)
                if lo_x - m < x1 and hi_x + m > x0 and lo_y - m < y1 and hi_y + m > y0:
                    probs.append(f"部屋の中の道が家具に当たる：{name} {f['name']}")
    return probs


# だれがどこに入るか（2026-11-10 エイイチ：患者・看護師・医師などの入る所・入らない所・座る所を分ける）
# 役割ごとに「座る」（座る・寝る・腰を落ち着ける所）／「入る」（通る・立ち寄る）。書いていない役割は入らない
ROLES = ["外来の患者", "化学療法の患者", "救急の患者", "看護師", "医師", "薬剤師", "検査技師", "事務", "ロボット"]
S_, E_ = "座る", "入る"
ACCESS = [  # 名前, 部屋, [rect...], {役割: 座る|入る}, 補足
    ("内科の処置室", "内科外来", [(0, 31, 22, 46)], {"外来の患者": S_, "看護師": E_, "医師": E_, "検査技師": E_, "ロボット": E_},
     "患者は採血のいす・処置ベッド。ロボットは台の前まで"),
    ("内科の処置室の裏の通路", "内科外来", [(11, 46, 13.1, 57.9)], {"看護師": E_, "医師": E_, "検査技師": E_, "ロボット": E_}, ""),
    ("内科の器材室", "内科外来", [(0, 46, 11, 57.9)], {"看護師": E_}, ""),
    ("内科の検体の準備室", "内科外来", [(13.1, 46, 22, 57.9)], {"看護師": E_, "検査技師": E_}, ""),
    ("内科の中待合", "内科外来", [(22, 31, 42, 35.5)], {"外来の患者": S_, "看護師": E_}, "看護師が呼び入れる"),
    ("内科の診察室", "内科外来", [(22, 35.5, 42, 41)], {"外来の患者": S_, "看護師": E_, "医師": S_}, ""),
    ("内科の診察室の裏の通路", "内科外来", [(22, 41, 42, 43.1)], {"外来の患者": E_, "看護師": E_, "医師": E_},
     "患者は内視鏡室へ行く人だけ、看護師が付き添って通る（2026-11-11 案A）"),
    ("内視鏡室", "内科外来", [(22, 43.1, 42, 51)], {"外来の患者": E_, "看護師": E_, "医師": E_}, "検査を受ける患者だけ"),
    ("回復室", "内科外来", [(22, 51, 42, 57.9)], {"外来の患者": S_, "看護師": E_}, "検査を受けた患者だけ。ベッドで休む"),
    ("ほかの外来の中待合", "ほかの外来", [(42, 31, 97.95, 36)], {"外来の患者": S_, "看護師": E_, "事務": E_}, "各科の受付は事務"),
    ("ほかの外来の診察室", "ほかの外来", [(42, 36, 97.95, 41.5)], {"外来の患者": S_, "看護師": E_, "医師": S_}, ""),
    ("ほかの外来の裏の通路", "ほかの外来", [(42, 41.5, 97.95, 43.6)], {"外来の患者": E_, "看護師": E_, "医師": E_},
     "患者は処置・検査室へ行く人だけ、看護師が付き添って通る（2026-11-11 案A）"),
    ("ほかの外来の処置・検査室", "ほかの外来", [(42, 43.6, 97.95, 57.9)], {"外来の患者": E_, "看護師": E_, "医師": E_}, "処置を受ける患者だけ"),
    ("化学療法の受付・待合", "外来化学療法室", [(100.05, 31, 120, 35)], {"化学療法の患者": S_, "看護師": E_, "薬剤師": E_, "ロボット": E_},
     "ロボットは受付の台の前まで"),
    ("化学療法の点滴の場所", "外来化学療法室", [(100.05, 35, 120, 52), (100.05, 52, 106, 60), (114, 52, 120, 53), (114, 53, 114.5, 60)],
     {"化学療法の患者": S_, "看護師": E_, "医師": E_, "薬剤師": E_}, "患者は点滴のいす・ベッドで数時間"),
    ("化学療法の準備室", "外来化学療法室", [(106, 52, 114, 60)], {"看護師": E_, "薬剤師": E_}, ""),
    ("化学療法のトイレ", "外来化学療法室", [(114.5, 53, 120, 60)], {"化学療法の患者": E_}, "点滴をつけたまま行く"),
    ("薬剤部", "薬剤部", [(0, 0, 23, 13)], {"薬剤師": S_, "ロボット": E_}, "ロボットは払い出し台の前まで"),
    ("お薬の窓口の内側", "お薬の窓口", [(0, 15.1, 17.5, 19.3)], {"薬剤師": S_}, ""),
    ("お薬の窓口の待合", "お薬の窓口", [(0, 19.3, 17.5, 27)], {"外来の患者": S_}, ""),
    ("トイレ（西）", "お薬の窓口", [(17.5, 15.1, 22.95, 27)], {"外来の患者": E_, "化学療法の患者": E_, "救急の患者": E_}, "付き添いも入る"),
    ("エレベーター", "総合受付・会計・待合", [(25.1, 15.1, 31, 21)], {r: E_ for r in ROLES if r != "ロボット"}, "ほかの階との出入り口（ここで現れ・消える）"),
    ("エレベーターホール", "総合受付・会計・待合", [(25.1, 21, 31, 27)], {r: E_ for r in ROLES if r != "ロボット"}, ""),
    ("総合受付・会計の内側", "総合受付・会計・待合", [(31, 15.1, 44, 17.4), (52, 15.1, 70, 17.4)], {"事務": S_}, ""),
    ("総合受付・会計の待合", "総合受付・会計・待合", [(31, 17.4, 44, 27), (52, 17.4, 70, 27)],
     {"外来の患者": S_, "化学療法の患者": S_}, ""),
    ("倉庫", "倉庫", [(25.1, 0, 44, 13)], {"看護師": E_, "事務": E_}, ""),
    ("正面玄関", "正面玄関", [(44, 0, 52, 13)], {"外来の患者": E_, "化学療法の患者": E_, "救急の患者": E_, "事務": E_}, ""),
    ("医事・事務", "医事・事務", [(52, 0, 70, 13)], {"事務": S_}, ""),
    ("中央検査室", "中央検査室", [(70, 0, 98, 13)], {"検査技師": S_, "ロボット": E_}, "ロボットは検体受付の台の前まで"),
    ("生理検査の待合", "生理検査", [(70, 21.9, 98, 27)], {"外来の患者": S_, "検査技師": E_}, ""),
    ("トイレ（東）", "生理検査", [(70 + 5 * 28.0 / 6, 15.1, 98, 21.9)], {"外来の患者": E_, "化学療法の患者": E_, "救急の患者": E_}, "付き添いも入る"),
    ("生理検査の検査室", "生理検査", [(70, 15.1, 70 + 5 * 28.0 / 6, 21.9)], {"外来の患者": E_, "検査技師": S_, "医師": E_}, "患者は検査ベッドに寝る"),
    ("救急の待合", "救急室", [(100.1, 20, 120, 27)], {"救急の患者": S_, "看護師": E_}, "歩いて来た人"),
    ("救急の処置の場所", "救急室", [(100.1, 6, 120, 20), (108, 0, 110, 6)],
     {"救急の患者": S_, "看護師": E_, "医師": E_, "ロボット": E_}, "患者は処置ベッド。ロボットは検体の台の前まで"),
    ("初療室", "救急室", [(110, 0, 120, 6)], {"救急の患者": E_, "看護師": E_, "医師": E_}, "救急車で来た人"),
    ("X線室", "救急室", [(100.1, 0, 108, 6)], {"救急の患者": E_, "医師": E_, "検査技師": E_}, ""),
    ("外来の大廊下", "廊下", [(0, 27, 120, 31)], {r: E_ for r in ROLES}, ""),
    ("玄関の通り", "廊下", [(44, 15.1, 52, 27)], {r: E_ for r in ROLES if r != "ロボット"}, ""),
    ("職員の中廊下", "廊下", [(0, 13, 100.1, 15.1)], {r: E_ for r in ["看護師", "医師", "薬剤師", "検査技師", "事務", "ロボット"]},
     "患者は正面玄関の前（x44〜52）を横切るだけ"),
    ("西の縦廊下", "廊下", [(22.95, 15.1, 25.05, 27)], {r: E_ for r in ["看護師", "医師", "薬剤師", "検査技師", "事務", "ロボット"]}, ""),
    ("東の縦廊下", "廊下", [(97.95, 15.1, 100.05, 27)], {r: E_ for r in ["看護師", "医師", "薬剤師", "検査技師", "事務", "ロボット"]}, ""),
    ("外来の奥の職員通路", "廊下", [(0, 57.9, 100.05, 60), (97.95, 31, 100.05, 57.9)],
     {r: E_ for r in ["看護師", "医師", "薬剤師", "検査技師", "事務", "ロボット"]}, "東の縦廊下（北）を含む"),
]
CROSS_ONLY = {"職員の中廊下": {"外来の患者": (44, 13, 52, 15.1), "化学療法の患者": (44, 13, 52, 15.1)}}


def check(data):
    """家具が部屋からはみ出していないか・道や扉の前をふさいでいないかを確かめる"""
    probs = []
    rooms = {r["name"]: r["rect"] for r in data["layout"]["rooms"]}
    for f in FUR:
        x0, y0, x1, y1 = rooms[f["room"]]
        a = f["rect"]
        if a[0] < x0 - 1e-6 or a[1] < y0 - 1e-6 or a[2] > x1 + 1e-6 or a[3] > y1 + 1e-6:
            probs.append(f"はみ出し：{f['room']} {f['name']} {a}")
    for d in DOORS + [{"room": s["room"], "at": dd["at"], "w": dd["w"], "name": s["name"]} for s in SUB for dd in s["doors"]]:
        x, y = d["at"]
        for f in FUR:
            a = f["rect"]
            if a[0] - 0.6 < x < a[2] + 0.6 and a[1] - 0.6 < y < a[3] + 0.6 and f["kind"] != "置き台":
                probs.append(f"扉の前に家具：{d['room']} {d.get('name','')} {d['at']} ← {f['name']}")
    return probs


def main(out_dir):
    place_wheelchair_spaces()
    mh.apply_variant("h0b")
    edges = mh.build_graph("h0b")
    out = Path(out_dir)
    out.mkdir(parents=True, exist_ok=True)
    rooms = [{"name": n, "rect": [x0, y0, x1, y1], "kind": k} for n, x0, y0, x1, y1, k in mh.ROOMS + EXTRA_ROOMS]
    routes = {}
    plan = {"green": ("救急室の処置室", "検査部の検体受付", "検体（救急・病棟）"),
            "blue": ("内科外来の処置室", "検査部の検体受付", "採血管・PCR/抗原・尿"),
            "blue_back": ("内科外来の処置室（裏口）", "検査部の検体受付", "採血管・PCR/抗原・尿"),
            "red": ("薬剤部の払い出し口", "外来化学療法室の受付", "抗がん剤・前投薬")}
    for key, (a, b, carries) in plan.items():
        d, path = mh.shortest(edges, mh.POINTS[a][:2], mh.POINTS[b][:2])
        hall_m = sum(mh.crowded_len(p[0], q[0]) for p, q in zip(path, path[1:]) if mh.on_hall(p, q))
        ia, ib = INSIDE[a][::-1], INSIDE[b]
        full = ia[:-1] + path + ib[1:]
        routes[key] = {"from": a, "to": b, "carries": carries, "distance_m": round(mh.true_len(full), 2),
                       "corridor_m": round(mh.true_len(path), 2),
                       "in_room_m": {"from": round(mh.true_len(INSIDE[a]), 2), "to": round(mh.true_len(INSIDE[b]), 2)},
                       "crowded_hall_m": round(hall_m, 2), "path": [[round(v, 2) for v in p] for p in full]}
    base = json.loads(Path(__file__).with_name("hospital_h0b.json").read_text(encoding="utf-8"))
    data = {
        "meta": dict(base["meta"], name="仮の病院 h1（外来の階・部屋の中まで）", variant="h1",
                     schema_version="hospital-layout/0.2",
                     based_on="hospital_h0b.json（部屋・廊下・点は同じ）",
                     interior_basis="仮置き（典型の並びをコードで起こした。実在の病院ではない）",
                     wall_h=WALL_H),
        "layout": dict(base["layout"], rooms=rooms),
        "interior": {"subrooms": SUB, "furniture": FUR, "openings": OPEN, "doors": DOORS, "inner_passages": INNER},
        "routes": routes,
        "distances": {k: round(v + mh.true_len(INSIDE[k.split("|")[0]]) + mh.true_len(INSIDE[k.split("|")[1]]), 2)
                      for k, v in base["distances"].items()},
        "stops": {n: [list(p) for p in pts] for n, pts in INSIDE.items()},
        "access": {"roles": ROLES, "levels": {"座る": "座る・寝る・腰を落ち着ける", "入る": "通る・立ち寄る"},
                   "zones": [{"name": n, "room": r, "rects": [list(x) for x in rs], "who": w, "note": nt}
                             for n, r, rs, w, nt in ACCESS],
                   "cross_only": {k: {r: list(v) for r, v in d.items()} for k, d in CROSS_ONLY.items()},
                   "basis": "仮置き（典型の動き方。病院ごとに違う）"},
    }
    data["meta"]["distance"] = "部屋の中の台の前で止まる点から、扉 → 廊下の中心線 → 扉 → 台の前まで（縦横だけ）。h0・h0b は扉から扉まで"
    probs = check(data) + check_inside()
    (out / "hospital_h1.json").write_text(json.dumps(data, ensure_ascii=False, indent=1), encoding="utf-8")
    draw(data, out / "hospital_h1.png")
    for k, r in routes.items():
        print(k, r["from"], "→", r["to"], r["distance_m"], "m（廊下", r["corridor_m"], "＋部屋の中", r["in_room_m"], "）")
    print("小部屋", len(SUB), "家具", len(FUR), "扉", len(DOORS) + sum(len(s["doors"]) for s in SUB))
    print("確かめ：", "問題なし" if not probs else "\n".join(probs))
    views = [("内科外来とお薬の窓口・総合受付の西", (-0.5, 12.5, 44.5, 60.5)),
             ("ほかの外来", (41.5, 26.0, 98.5, 60.5)),
             ("外来化学療法室と救急室", (96.0, -0.5, 120.5, 60.5)),
             ("薬剤部・倉庫・正面玄関（職員の中廊下の西）", (-0.5, -0.5, 53.0, 28.0)),
             ("医事・中央検査室・生理検査（職員の中廊下の東）", (51.5, -0.5, 101.0, 28.0))]
    files = []
    for i, (t, v) in enumerate(views):
        f = out / f"_view{i}.png"
        draw(data, f, view=v, title=t)
        files.append(f)
    try:
        from PIL import Image
        ims = [Image.open(f) for f in files]
        wmax = max(im.width for im in ims)
        canvas = Image.new("RGB", (wmax, sum(im.height for im in ims) + 40 * len(ims)), "white")
        y = 0
        for im in ims:
            canvas.paste(im, (0, y))
            y += im.height + 40
        canvas.save(out / "hospital_h1_rooms.png")
        for f in files:
            f.unlink()
    except ImportError:
        pass


def draw(data, path, view=None, title=None):
    import matplotlib
    matplotlib.use("Agg")
    import matplotlib.pyplot as plt
    from matplotlib import font_manager
    from matplotlib.patches import Rectangle
    for f in font_manager.findSystemFonts():
        if "NotoSansCJK-Regular" in f or "NotoSansJP" in f:
            font_manager.fontManager.addfont(f)
            plt.rcParams["font.family"] = font_manager.FontProperties(fname=f).get_name()
            break
    col = {"外来": "#f3f5f8", "届け先": "#e6effc", "出発": "#e8f5eb", "混む所": "#fff4e3", "その他": "#f5f5f5"}
    fcol = {"ベンチ": "#c9a46a", "いす": "#c9a46a", "机": "#9aa6b2", "ベッド": "#8fc7b8", "カウンター": "#7f8c99",
            "棚": "#6b7480", "分析装置": "#5d6b7a", "リクライニングチェア": "#8fc7b8", "台": "#9aa6b2",
            "機器": "#5d6b7a", "精算機": "#5d6b7a", "置き台": "#e0472f", "台車": "#9aa6b2", "島": "#7f8c99", "車いすの場所": "#f2b705"}
    rc = {"green": "#2e9d4f", "blue": "#2f6fd6", "blue_back": "#6c4fd6", "red": "#d24b3e"}
    big = view is None
    fig, ax = plt.subplots(figsize=(17, 9.4) if big else (10, 6.6), dpi=150)
    for r in data["layout"]["rooms"]:
        x0, y0, x1, y1 = r["rect"]
        ax.add_patch(Rectangle((x0, y0), x1 - x0, y1 - y0, fc=col[r["kind"]], ec="#3c4650", lw=1.6))
    for c in data["layout"]["corridors"]:
        x0, y0, x1, y1 = c["rect"]
        ax.add_patch(Rectangle((x0, y0), x1 - x0, y1 - y0, fc="#ffffff", ec="none"))
    for p in data["interior"]["inner_passages"]:
        x0, y0, x1, y1 = p["rect"]
        ax.add_patch(Rectangle((x0, y0), x1 - x0, y1 - y0, fc="#fbfbfb", ec="none"))
    for s in data["interior"]["subrooms"]:
        x0, y0, x1, y1 = s["rect"]
        ax.add_patch(Rectangle((x0, y0), x1 - x0, y1 - y0, fc="none", ec="#3c4650", lw=1.0))
    for f in data["interior"]["furniture"]:
        x0, y0, x1, y1 = f["rect"]
        ax.add_patch(Rectangle((x0, y0), x1 - x0, y1 - y0, fc=fcol[f["kind"]], ec="none"))
    # 開いている壁と扉は、壁の線の上に白い線を引いて見せる
    rooms = {r["name"]: r["rect"] for r in data["layout"]["rooms"]}
    for o in data["interior"]["openings"]:
        x0, y0, x1, y1 = rooms[o["room"]]
        a, b = o["span"]
        if o["side"] in "NS":
            y = y1 if o["side"] == "N" else y0
            ax.plot([a, b], [y, y], color="#ffffff", lw=2.6, solid_capstyle="butt")
        else:
            x = x1 if o["side"] == "E" else x0
            ax.plot([x, x], [a, b], color="#ffffff", lw=2.6, solid_capstyle="butt")
    alld = [(d["at"], d["w"]) for d in data["interior"]["doors"]] + \
           [(dd["at"], dd["w"]) for s in data["interior"]["subrooms"] for dd in s["doors"]]
    for (x, y), w in alld:
        # 扉がどちら向きの壁にあるか：近くの縦の壁か横の壁かで決める
        horiz = any(abs(y - r[1]) < 1e-3 or abs(y - r[3]) < 1e-3 for r in list(rooms.values()) +
                    [s["rect"] for s in data["interior"]["subrooms"]])
        vert = any(abs(x - r[0]) < 1e-3 or abs(x - r[2]) < 1e-3 for r in list(rooms.values()) +
                   [s["rect"] for s in data["interior"]["subrooms"]])
        if horiz and not (vert and not horiz):
            ax.plot([x - w / 2, x + w / 2], [y, y], color="#ffffff", lw=3.0, solid_capstyle="butt")
        else:
            ax.plot([x, x], [y - w / 2, y + w / 2], color="#ffffff", lw=3.0, solid_capstyle="butt")
    off = {"green": 0.35, "blue": 0.0, "blue_back": 0.0, "red": -0.35}
    for k, r in data["routes"].items():
        xs = [p[0] for p in r["path"]]
        ys = [p[1] + off[k] for p in r["path"]]
        ax.plot(xs, ys, color=rc[k], lw=2.4 if big else 3.0, solid_capstyle="round", alpha=0.95)
        ax.plot(xs[0], ys[0], "o", color=rc[k], ms=8)
        ax.plot(xs[-1], ys[-1], "s", color=rc[k], ms=8)
    fs = 10 if big else 9
    for r in data["layout"]["rooms"]:
        x0, y0, x1, y1 = r["rect"]
        if view and not (view[0] <= (x0 + x1) / 2 <= view[2] and view[1] <= (y0 + y1) / 2 <= view[3]):
            continue
        if x1 - x0 < 3:  # 階段・設備の細い所は斜線だけで名前は出さない
            ax.add_patch(Rectangle((x0, y0), x1 - x0, y1 - y0, fc="none", ec="#b9c0c7", hatch="///", lw=0))
            continue
        ax.text(x0 + 0.5, y1 - 0.5, r["name"], ha="left", va="top", fontsize=fs, color="#1f2933", weight="bold",
                bbox=dict(fc="white", ec="none", alpha=0.8, pad=1))
    if not big:
        for s in data["interior"]["subrooms"]:
            x0, y0, x1, y1 = s["rect"]
            if view[0] <= (x0 + x1) / 2 <= view[2] and view[1] <= (y0 + y1) / 2 <= view[3]:
                nm = s["name"]
                if nm.endswith("の検査室"):
                    nm = nm[:-4]
                elif "の" in nm and len(nm) > 6:
                    nm = nm.split("の")[-1]
                ax.text((x0 + x1) / 2, (y0 + y1) / 2, nm, ha="center", va="center",
                        fontsize=7.5 if x1 - x0 > 5.5 else 6.5, color="#3c4650",
                        bbox=dict(fc="white", ec="none", alpha=0.7, pad=0.5))
        for f in data["interior"]["furniture"]:
            if f["kind"] == "置き台":
                x0, y0, x1, y1 = f["rect"]
                if view[0] <= x0 <= view[2] and view[1] <= y0 <= view[3]:
                    ax.annotate(f["name"], ((x0 + x1) / 2, (y0 + y1) / 2), xytext=(8, 10), textcoords="offset points",
                                fontsize=8, color="#b8321e")
    ax.add_patch(Rectangle((0, 0), mh.W, mh.D, fc="none", ec="#1f2933", lw=2.2))
    if big:
        nm = {"green": "緑", "blue": "青（表）", "blue_back": "青（裏口）", "red": "赤"}
        for i, (k, r) in enumerate(data["routes"].items()):
            ax.text(1 + (i % 2) * 52, -3 - (i // 2) * 2.3, "●", color=rc[k], fontsize=12, va="center")
            ax.text(3 + (i % 2) * 52, -3 - (i // 2) * 2.3, f"{nm[k]}  {r['from']} → {r['to']}  {r['distance_m']:.0f}m",
                    fontsize=10.5, va="center")
        ax.text(1, -8.2, "■ 赤い小さな台＝ロボットに積む所・受け取る所　茶＝待合のベンチ　黄＝車いすの場所　緑＝ベッド・点滴のいす　灰＝机・台・機器・棚",
                fontsize=9.5, color="#555", va="center")
        ax.set_xlim(-1, mh.W + 1)
        ax.set_ylim(-9.5, mh.D + 1)
    else:
        ax.set_xlim(view[0], view[2])
        ax.set_ylim(view[1], view[3])
    ax.set_aspect("equal")
    ax.axis("off")
    ax.set_title(title or f"{data['meta']['name']}（120m×60m）と搬送の4つの道", fontsize=15 if big else 12, loc="left")
    fig.savefig(path, bbox_inches="tight", facecolor="white")
    plt.close(fig)


if __name__ == "__main__":
    main(sys.argv[1] if len(sys.argv) > 1 else ".")
