"""f33_hierarchy -- master diagram: a vertical ladder from ideal gas upward,
each rung labelled with the physics added and the EOS that introduced it,
with an accuracy/complexity axis on the right.

2026 revision: figure canvas resized to match its on-slide / in-notes display
size, and every font enlarged so that the rendered text is legible at that
size. Previously the figure was drawn at 10.5 x 12.5 in with 10.3 pt labels
and displayed at 5.25 in tall, giving an effective 4.3 pt on the slide.
"""
import os
import sys
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import FancyBboxPatch, FancyArrowPatch

INK      = "#1C242B"
GRAPHITE = "#333F4A"
PAPER    = "#FFFFFF"
NAVY     = "#1F4E79"
AMBER    = "#E29A2D"

FIGDIR = os.environ.get("EOS_FIGDIR", "/home/claude/eos_lecture/figs")
os.makedirs(FIGDIR, exist_ok=True)

plt.rcParams.update({
    "figure.facecolor": PAPER,
    "savefig.facecolor": PAPER,
    "text.color": INK,
    "font.family": "sans-serif",
    "font.sans-serif": ["DejaVu Sans"],
    "mathtext.fontset": "dejavusans",
})

# ---------------------------------------------------------------- geometry
# Canvas sized to the display box (slide 2 / slide 53 place the picture at
# about 5.7 x 5.34 in). Drawing at 6.4 x 6.0 in gives a display scale of
# 0.89, so the point sizes below are close to what the audience actually sees.
W, H = 6.4, 6.0

FS_BOX   = 13.0   # EOS name inside each rung
FS_SUB   = 10.6   # "+ physics added" caption under each rung
FS_TITLE = 15.0
FS_AXIS  = 12.5

# box outline geometry: the drawn rounded box extends BOX_PAD beyond the
# nominal rectangle on every side, so the clear air left for the connector
# captions is (rung spacing - box_h - 2*BOX_PAD).
BOX_PAD  = 0.006
BOX_ROUND = 0.012

fig, ax = plt.subplots(figsize=(W, H))
ax.set_xlim(0, 1)
ax.set_ylim(0, 1)
ax.axis("off")


def box(cx, cy, w, h, text, fc=PAPER, ec=GRAPHITE, tc=INK, fs=FS_BOX, lw=1.3, weight="bold"):
    b = FancyBboxPatch((cx - w / 2, cy - h / 2), w, h,
                       boxstyle=f"round,pad={BOX_PAD},rounding_size={BOX_ROUND}",
                       linewidth=lw, edgecolor=ec, facecolor=fc, zorder=3)
    ax.add_patch(b)
    ax.text(cx, cy, text, ha="center", va="center",
            fontsize=fs, color=tc, zorder=4, weight=weight, linespacing=1.25)
    return b


rungs = [
    ("Ideal gas", "point particles, no interactions"),
    ("van der Waals (1873)", "+ excluded volume $b$ + mean-field attraction $-a/V^2$"),
    ("Redlich-Kwong (1949)", "+ temperature-dependent attraction $a/\\sqrt{T}$"),
    ("Soave / SRK (1972)", "+ molecular shape via $\\alpha(T_r,\\omega)$"),
    ("Peng-Robinson (1976)", "+ improved volume dependence, better $Z_c$"),
    ("SAFT (1990) / PC-SAFT (2001)", "+ chain connectivity $a_{chain}$ + dispersion $a_{disp}$"),
    ("SAFT + association", "+ hydrogen bonding, $a_{assoc}$"),
    ("Electrolyte EOS (eSAFT / ePC-SAFT)", "+ electrostatics: $a_{Born}+a_{DH/MSA}$"),
]

n = len(rungs)
# The rungs are made shallow and the ladder pitch wide so that each connector
# caption sits in a band of genuinely clear paper between two boxes: the
# tallest caption (the one carrying a square root) is ~0.052 of the canvas
# height, and the clear band below is 0.068.
y0, y1 = 0.034, 0.916
ys = np.linspace(y0, y1, n)
box_w, box_h = 0.830, 0.046
cx = 0.427

pitch = (y1 - y0) / (n - 1)
half = box_h / 2 + BOX_PAD          # half-height of the *drawn* box
gap = pitch - 2 * half              # clear paper between two drawn boxes

for i, ((name, physics), y) in enumerate(zip(rungs, ys)):
    fc = NAVY if i == 0 else "#F4F6F7"
    tc0 = "white" if i == 0 else INK
    box(cx, y, box_w, box_h, name, fc=fc, tc=tc0)
    if i > 0:
        # caption is vertically centred in the clear band, so ascenders and
        # descenders keep equal air from both box borders
        ax.text(cx - box_w / 2 - BOX_PAD + 0.012, y - half - gap / 2, physics,
                ha="left", va="center", fontsize=FS_SUB, color=GRAPHITE,
                linespacing=1.2)
    if i < n - 1:
        # arrow sits at the right end of the gap, clear of both the
        # left-aligned "+ physics" caption and the rounded box corners
        arr_x = cx + box_w / 2 + BOX_PAD - 0.032
        arr = FancyArrowPatch((arr_x, y + half + 0.004),
                              (arr_x, ys[i + 1] - half - 0.006),
                              arrowstyle="-|>", mutation_scale=13,
                              linewidth=1.6, color=GRAPHITE, zorder=2)
        ax.add_patch(arr)

# ------------------------------------------- accuracy / complexity axis
ax_x = 0.885
ax.annotate("", xy=(ax_x, y1 + 0.026), xytext=(ax_x, y0 - 0.020),
            arrowprops=dict(arrowstyle="-|>", color=GRAPHITE, lw=1.7, mutation_scale=16))
ax.text(ax_x + 0.050, (y0 + y1) / 2,
        "increasing molecular realism,\naccuracy, and computational cost",
        ha="center", va="center", fontsize=FS_AXIS, color=GRAPHITE,
        rotation=90, linespacing=1.3)
for y in [ys[0], ys[3], ys[-1]]:
    ax.plot([ax_x - 0.014, ax_x + 0.014], [y, y], color=GRAPHITE, lw=1.3)

ax.text(cx, 0.995, "The hierarchy of physics in the equation of state",
        ha="center", va="top", fontsize=FS_TITLE, color=NAVY, weight="bold")

fig.tight_layout()
fig.savefig(f"{FIGDIR}/f33_hierarchy.pdf", bbox_inches="tight", pad_inches=0.03)
fig.savefig(f"{FIGDIR}/f33_hierarchy.png", dpi=600, bbox_inches="tight", pad_inches=0.03)
plt.close(fig)
print("wrote f33_hierarchy")
