"""
Supplementary Figure 44 — converged structural picture: nested radial pleats at three
distinct length scales (T_1, T_1/3, T_1/5) with mechanism-distinct fillers.

REGEN 2026-05-02: replaces the v44 schematic that had two flaws Wolf identified:
  (i) panels a + b drew the three nested levels at superficially similar wavelengths
      (the level-2 amplitude was too small relative to level-1 to make the cycle-count
      difference visually unambiguous); and
  (ii) panel a depicted the chromosome wrapping the entire nuclear envelope, suggesting
      that chromosomes span the full nuclear cross-section. They do not — chromosomes
      occupy territories that span only part of the nucleus.

This regen fixes both:
  - panel a now shows ONLY a chromosome territory occupying part of the nuclear
    cross-section (a wedge of the nucleus); the chromosome path traces five radial
    cycles back and forth between the speckle-proximal interior and the lamina-proximal
    periphery, and the three nested levels are drawn as three separate paths inside
    the same wedge so the cycle-count differences (5, 15, 75) are visually distinct.
  - panel b shows three separate traces at the same horizontal extent, each scaled
    to show its actual cycle count (5, 15, 75) at the correct relative amplitude.

This is a SCHEMATIC. Per CLAUDE.md, schematics are allowed when clearly distinct from
data figures and not labeled as a measurement. The wave shapes here are pedagogical
cartoons of the converged structural picture; the underlying data appears in
Supplementary Figs. S37–S43, S45.
"""
from __future__ import annotations
import sys, os
from pathlib import Path
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
from matplotlib.patches import Circle, Wedge, FancyBboxPatch, Rectangle, Arc
from matplotlib.lines import Line2D

sys.path.insert(0, str(Path(__file__).resolve().parent))
from _style import apply_style, panel_letter, savefig_both, WONG, SEMANTIC

apply_style()

BUNDLE_ROOT = Path(__file__).resolve().parents[2]
OUT_BASENAMES = [
    BUNDLE_ROOT / '02_figures' / 'supplementary' / 'supplementary_figure_44_structural_picture',
    BUNDLE_ROOT / '03_figures_to_edit' / 'supplementary' / 'supplementary_figure_44_structural_picture',
]
DRAFT_BASENAME = BUNDLE_ROOT / '05_code' / 'figure_regen' / '_drafts' / 'supp44_structural_v2'

# Level colours (hierarchical)
L1_C = WONG['gold']
L2_C = WONG['green']
L3_C = WONG['sky']

fig = plt.figure(figsize=(15.0, 10.5))
gs = gridspec.GridSpec(2, 2, height_ratios=[1.2, 1.0], width_ratios=[1.0, 1.4],
                       hspace=0.34, wspace=0.22,
                       left=0.05, right=0.985, top=0.94, bottom=0.06)

# =============================================================================
# Panel a — chromosome TERRITORY inside a portion of the nucleus
# Linear (rectangular) wedge depiction so the wave shapes read cleanly: the
# chromosome is a meandering snake between the speckle-side (left) and the
# lamina-side (right) of a slice of the nucleus. The full nucleus extends off
# panel — we are showing only a chromosome territory.
# =============================================================================
ax_a = fig.add_subplot(gs[0, 0])
ax_a.set_aspect('auto'); ax_a.axis('off')
ax_a.set_xlim(-0.05, 1.10); ax_a.set_ylim(-0.05, 1.05)

# Nucleus boundary (just one curved edge representing where the lamina begins,
# at the right). The reader infers that the rest of the nucleus extends off-panel.
xs_lam = np.array([1.00, 1.00])
ys_lam = np.array([0.0, 1.0])
ax_a.plot(xs_lam, ys_lam, color='black', lw=1.6, zorder=2)
# Lamina band
ax_a.fill_betweenx([0, 1], 1.00, 1.05, color=WONG['blue'], alpha=0.18, zorder=1)
ax_a.text(1.07, 0.5, 'Lamina\n(B-side,\nperiphery)', fontsize=9, ha='left', va='center',
          color=WONG['blue'], fontweight='bold', rotation=0)
# Speckle cluster on the interior (left) side
np.random.seed(7)
for _ in range(7):
    cx = 0.02 + 0.10*np.random.random()
    cy = 0.20 + 0.60*np.random.random()
    ax_a.add_patch(Circle((cx, cy), 0.025, fc=WONG['vermil'], ec='black',
                          linewidth=0.3, zorder=4))
ax_a.text(-0.04, 0.5, 'Speckles\n(A-side,\ninterior)', fontsize=9, ha='right', va='center',
          color=WONG['vermil'], fontweight='bold')
# Subtle dashed boundaries above/below indicating "rest of nucleus continues"
for y_edge, lab in [(1.01, 'rest of nucleus →'), (-0.01, 'rest of nucleus →')]:
    ax_a.axhline(y_edge, color='0.7', lw=0.5, ls=':', alpha=0.6)
ax_a.text(0.50, 1.01, '↕ rest of nucleus continues above and below', ha='center', va='bottom',
          fontsize=7.5, color='0.45', style='italic')

# Chromosome path: y = position along chromosome (0 at top, 1 at bottom)
# x = radial coordinate (0 at speckle interior, 1 at lamina periphery)
# A real chromosome follows the long axis of the chain; we plot the chromosome
# as a meandering trace where x oscillates between speckle and lamina sides as
# we walk along the chain (y).
n = 1500
t = np.linspace(0, 1, n)            # position along chromosome chain
cyc_1 = 5
cyc_2 = 15      # = 5 × 3
cyc_3 = 75      # = 5 × 3 × 5
A1 = 0.40
A2 = 0.13
A3 = 0.04

# Three traces stacked side-by-side (each in its own vertical strip) so the cycle
# counts read unambiguously.
strip_l1_x_centre = 0.27
strip_l2_x_centre = 0.50
strip_l3_x_centre = 0.73
strip_half_width = 0.10

# Level 1
x1 = strip_l1_x_centre + A1 * np.cos(2*np.pi*cyc_1*t) * (strip_half_width / A1)
y1 = t
ax_a.plot(x1, y1, color=L1_C, lw=2.2, zorder=5)

# Level 2 (same range as level 1 strip but with 15 cycles, smaller amp on top of
# the level-1 envelope; shown here as level 2 ALONE for clarity)
x2 = strip_l2_x_centre + (A1 * np.cos(2*np.pi*cyc_1*t) + A2 * np.cos(2*np.pi*cyc_2*t)) * (strip_half_width / (A1 + A2))
ax_a.plot(x2, y1, color=L2_C, lw=1.4, zorder=5)

# Level 3
x3 = strip_l3_x_centre + (A1 * np.cos(2*np.pi*cyc_1*t)
                          + A2 * np.cos(2*np.pi*cyc_2*t)
                          + A3 * np.cos(2*np.pi*cyc_3*t)) * (strip_half_width / (A1 + A2 + A3))
ax_a.plot(x3, y1, color=L3_C, lw=0.9, zorder=5)

# Per-strip header annotations
for xc, lab, col, n_cyc in [
    (strip_l1_x_centre, f'Level 1 only\n{cyc_1} radial cycles', L1_C, cyc_1),
    (strip_l2_x_centre, f'Level 1 + 2\n{cyc_2} sub-stops', L2_C, cyc_2),
    (strip_l3_x_centre, f'Level 1 + 2 + 3\n{cyc_3} fine cycles', L3_C, cyc_3),
]:
    ax_a.text(xc, -0.04, lab, ha='center', va='top', fontsize=8, color=col, fontweight='bold')

# Mark the 5 A-crests and 5 B-troughs of level-1 (gold strip)
for k in range(cyc_1):
    # Crest: cos = -1 → x is at left (speckle side)
    t_crest = (k + 0.5) / cyc_1
    ax_a.add_patch(Circle((strip_l1_x_centre - strip_half_width, t_crest),
                          0.018, fc=WONG['vermil'], ec='black', lw=0.3, zorder=6))
    # Trough: cos = +1 → x is at right (lamina side)
    t_trough = k / cyc_1
    if 0 < t_trough < 1:
        ax_a.add_patch(Circle((strip_l1_x_centre + strip_half_width, t_trough),
                              0.018, fc=WONG['blue'], ec='black', lw=0.3, zorder=6))

# Indicator: chromosome territory takes up only a fraction of the nucleus
ax_a.text(0.50, -0.13,
          'a chromosome territory occupies only part of the nucleus;\n'
          'the chromosome chain meanders between speckle and lamina sides',
          ha='center', va='top', fontsize=9, color='0.3', style='italic')

ax_a.set_title('Chromosome territory: 3 nested radial pleats with distinct cycle counts',
               loc='left', pad=10, fontsize=10.5)
panel_letter(fig, ax_a, 'a')

# =============================================================================
# Panel b — Unwound view: 3 traces at distinct cycle counts (5, 15, 75)
# =============================================================================
ax_b = fig.add_subplot(gs[0, 1])
t = np.linspace(0, 1, 4000)
y_l1   = A1 * np.cos(2*np.pi*cyc_1*t)
y_l2   = A2 * np.cos(2*np.pi*cyc_2*t)
y_l3   = A3 * np.cos(2*np.pi*cyc_3*t)

# Three stacked traces, each at the correct amplitude scaled relative to its level.
# To make the cycle-count differences visually unambiguous, normalise each trace
# to unit amplitude in this panel (so the reader sees 5 vs 15 vs 75 cycles, not the
# amplitude collapse — that is the point of panel b in the harmonics figure).
y_l1_n = y_l1 / A1
y_l2_n = y_l2 / A2
y_l3_n = y_l3 / A3

offset_top = 4.5
offset_mid = 0.0
offset_bot = -4.5

ax_b.plot(t, y_l1_n + offset_top, color=L1_C, lw=2.0,
          label=fr'Level 1 only ({cyc_1} radial cycles)')
ax_b.plot(t, y_l2_n + offset_mid, color=L2_C, lw=1.5,
          label=fr'Level 2 only ({cyc_2} sub-radial cycles)')
ax_b.plot(t, y_l3_n + offset_bot, color=L3_C, lw=1.0,
          label=fr'Level 3 only ({cyc_3} fine cycles)')

# Annotate the cycle counts
for off, n, c in [(offset_top, cyc_1, L1_C), (offset_mid, cyc_2, L2_C), (offset_bot, cyc_3, L3_C)]:
    ax_b.text(1.04, off, f'{n} cycles', va='center', ha='left',
              fontsize=10, color=c, fontweight='bold')

# Mark the speckle/lamina extremes on the axis
for off in [offset_top, offset_mid, offset_bot]:
    ax_b.axhline(off, color='0.7', lw=0.4, alpha=0.5)
    ax_b.text(-0.018, off + 0.95, 'speckle', ha='right', va='center',
              fontsize=7, color=WONG['vermil'])
    ax_b.text(-0.018, off - 0.95, 'lamina',  ha='right', va='center',
              fontsize=7, color=WONG['blue'])

ax_b.set_xlim(-0.08, 1.18)
ax_b.set_ylim(offset_bot - 1.5, offset_top + 1.5)
ax_b.set_xticks([0, 0.5, 1.0]); ax_b.set_xticklabels(['0','L/2','L'], fontsize=10)
ax_b.set_yticks([])
ax_b.set_xlabel('Position along chromosome', fontsize=10.5)
for s in ['left', 'top', 'right']:
    ax_b.spines[s].set_visible(False)
ax_b.set_title('Unwound view: same scaling rule $T \\propto L^{0.83}$ at three nested scales',
               fontsize=10.5, loc='left', pad=8)
panel_letter(fig, ax_b, 'b')

# =============================================================================
# Panel c — Three-level mapping table (preserved from v44)
# =============================================================================
ax_c = fig.add_subplot(gs[1, :])
ax_c.set_xlim(0, 12); ax_c.set_ylim(0, 7)
ax_c.axis('off')
ax_c.text(0.6, 6.5, 'Three nested levels of supra-compartment geometry',
          fontsize=11.5, fontweight='bold')

levels = ['Level 1', 'Level 2', 'Level 3']
colors = [L1_C, L2_C, L3_C]
xstart = [3.0, 6.1, 9.2]
xwidth = 3.0

for i, (lv, c, xs_) in enumerate(zip(levels, colors, xstart)):
    head = FancyBboxPatch((xs_, 5.5), xwidth, 0.8,
                           boxstyle='round,pad=0.05', fc=c, ec='black',
                           linewidth=0.6, alpha=0.85)
    ax_c.add_patch(head)
    ax_c.text(xs_ + xwidth/2, 5.9, lv, fontsize=11, fontweight='bold',
              ha='center', va='center', color='white')

rows = [
    'Wavelength',
    'Cycles per chromosome',
    'Mechanism',
    'Biology refined',
    'Breakpoint vulnerability',
]
yrows = [4.7, 3.9, 3.1, 2.3, 1.5]
for r_, y_ in zip(rows, yrows):
    ax_c.text(0.4, y_, r_, fontsize=9.5, fontweight='bold', va='center', ha='left')

content = [
    [r'$T_1 \sim 25$–$50$ Mbp',
     r'$\sim 5$ radial excursions',
     'RIF1 / replication-timing\ncoupled coherence',
     'A/B compartment identity;\nradial position $R^2 = 0.93$',
     'Sets the supra-compartment\nframe (no breakpoint signal\nbeyond mean)'],
    [r'$T_1/3 \sim 8$–$15$ Mbp',
     r'$\sim 15$ sub-stops total',
     'Block-copolymer\nphase separation',
     r'Radial $\Delta R^2 = +0.19$ ($p<10^{-16}$);''\nReplisome, transcription,\nmutation density',
     r'$F_\mathrm{CNV} = 27.4$, $F_\mathrm{SV} = 16.5$,' '\n' r'$F_\mathrm{TRA} = 13.0$ — captures all''\nSV breakpoint signal'],
    [r'$T_1/5 \sim 5$–$10$ Mbp',
     r'$\sim 75$ fine cycles total',
     'CTCF + cohesin\nloop extrusion',
     'Continuous TAD-scale\nrefinement of TSA-seq, RT,\ngene/mut. density',
     'Saturates for sparse SVs\n(CNV $p=0.10$, TRA $p=0.99$)\nbut continuous biology refines'],
]
for col, (xs_, c, items) in enumerate(zip(xstart, colors, content)):
    for row, (y_, txt) in enumerate(zip(yrows, items)):
        ax_c.text(xs_ + xwidth/2, y_, txt,
                  ha='center', va='center', fontsize=8.5, color='black',
                  linespacing=1.25)
    if col < 2:
        ax_c.plot([xs_+xwidth+0.1, xs_+xwidth+0.1], [1.0, 5.4],
                  color='grey', lw=0.4, alpha=0.5)

panel_letter(fig, ax_c, 'c')

# Save
DRAFT_BASENAME.parent.mkdir(parents=True, exist_ok=True)
savefig_both(fig, DRAFT_BASENAME)
print(f"[wrote draft] {DRAFT_BASENAME}.png + .svg")
if os.environ.get('PROMOTE') == '1':
    for base in OUT_BASENAMES:
        savefig_both(fig, base)
        print(f"[promoted] {base}.png + .svg")
plt.close(fig)
