"""Generate three standalone, large-type mobile chart variants.

Uses values transcribed from the accompanying scientific CSV files.
Writes the three mobile SVG layouts into the script directory; desktop
figures and underlying data files are not modified.
"""
from __future__ import annotations

from pathlib import Path
from xml.sax.saxutils import escape

OUT = Path(__file__).resolve().parent
BG = "#f7faff"
INK = "#14253a"
MUTED = "#51677e"
GRID = "#cfdae6"


def text(x, y, s, size=22, *, fill=INK, weight=400, anchor="start", extra=""):
    return (f'<text x="{x}" y="{y}" font-family="Arial,sans-serif" '
            f'font-size="{size}" font-weight="{weight}" fill="{fill}" '
            f'text-anchor="{anchor}" {extra}>{escape(str(s))}</text>')


def start(width, height, title, desc):
    return [f'<svg xmlns="http://www.w3.org/2000/svg" width="{width}" height="{height}" viewBox="0 0 {width} {height}" role="img" aria-labelledby="title desc">',
            f'<title id="title">{escape(title)}</title>',
            f'<desc id="desc">{escape(desc)}</desc>',
            f'<rect width="100%" height="100%" rx="18" fill="{BG}"/>']


def mach6():
    w, h = 360, 800
    s = start(w, h,
        "Mach 6 inlet pressure recovery by angle of attack",
        "A mobile redraw of approximate visual readings from NASA TN D-7998 Figure 12(c). The four area-averaged throat total-pressure recovery values are 0.75 at minus 5 degrees, 0.74 at zero degrees, 0.80 at 3 degrees, and 0.81 at 5 degrees. Vertical bars show about plus or minus 0.01 visual digitization tolerance only, not test uncertainty. The connecting line is a guide, not intermediate measurements.")
    s += [text(18, 34, "Mach 6 fixed-geometry inlet", 24, weight=700),
          text(18, 66, "NASA TN D-7998", 24, fill=MUTED),
          text(18, 96, "Wind-tunnel test", 24, fill=MUTED),
          text(18, 126, "One inlet configuration", 24, fill=MUTED)]
    x0, x1, y0, y1 = 66, 344, 168, 390
    # Y axis uses the source's recovery range; X axis retains its full -6 to +6 range.
    for t in range(5, 11):
        val = t / 10
        y = y1 - (val - .5) / .5 * (y1 - y0)
        s += [f'<line x1="{x0}" y1="{y:.1f}" x2="{x1}" y2="{y:.1f}" stroke="{GRID}" stroke-width="1.5"/>',
              text(x0 - 8, y + 7, f"{val:.1f}", 24, fill=MUTED, anchor="end")]
    for tick in range(-6, 7, 2):
        x = x0 + (tick + 6) / 12 * (x1 - x0)
        s += [f'<line x1="{x:.1f}" y1="{y0}" x2="{x:.1f}" y2="{y1}" stroke="{GRID}" stroke-width="1"/>',
              text(x, y1 + 28, tick, 24, fill=MUTED, anchor="middle")]
    points = [(-5, .75), (0, .74), (3, .80), (5, .81)]
    coords = [(x0 + (a + 6) / 12 * (x1 - x0), y1 - (v - .5) / .5 * (y1 - y0)) for a, v in points]
    s.append('<polyline points="' + ' '.join(f'{x:.1f},{y:.1f}' for x, y in coords) + '" fill="none" stroke="#2779a7" stroke-width="3"/>')
    for (a, v), (x, y) in zip(points, coords):
        dy = .01 / .5 * (y1 - y0)
        s += [f'<line x1="{x:.1f}" y1="{y-dy:.1f}" x2="{x:.1f}" y2="{y+dy:.1f}" stroke="#2779a7" stroke-width="3"/>',
              f'<line x1="{x-6:.1f}" y1="{y-dy:.1f}" x2="{x+6:.1f}" y2="{y-dy:.1f}" stroke="#2779a7" stroke-width="3"/>',
              f'<line x1="{x-6:.1f}" y1="{y+dy:.1f}" x2="{x+6:.1f}" y2="{y+dy:.1f}" stroke="#2779a7" stroke-width="3"/>',
              f'<circle cx="{x:.1f}" cy="{y:.1f}" r="6" fill="#2779a7" stroke="white" stroke-width="2"/>',
              text(x, y + 35 if a == 5 else y - 17, f"{v:.2f}", 24, weight=700, anchor="middle")]
    s += [text(180, 459, "Angle of attack (degrees)", 24, fill=INK, anchor="middle"),
          text(18, 500, "Area-averaged throat", 24, fill=INK),
          text(18, 530, "total-pressure recovery", 24, fill=INK),
          text(18, 566, "Read-off tolerance: ±0.01", 24, weight=700),
          text(18, 600, "Visual reading tolerance;", 24, fill=MUTED),
          text(18, 634, "not test uncertainty.", 24, fill=MUTED),
          text(18, 668, "Line is a guide only.", 24, fill=MUTED),
          text(18, 702, "No added measurements", 24, fill=MUTED),
          text(18, 736, "between the markers.", 24, fill=MUTED),
          '</svg>']
    (OUT / "nasa-mach6-inlet-fig12c-mobile.svg").write_text('\n'.join(s) + '\n', encoding='utf-8')


def hveps():
    w, h = 320, 2730
    s = start(w, h,
        "HVEPS generator output across six analysed test points",
        "Six groups compare load-bank power measured as external load dissipation, active-channel output with end-region losses excluded, and maximum power at matched load estimated by post-test load-line analysis. Tests 1, 2, 3, 5, 6 and 7 are shown; Test 4 was not reduced because its scramjet pressure data were anomalous. Test 3 shows the mean combustion-efficiency case; its bounds are omitted from this plot. Bars share a 0 to 15 kilowatt scale.")
    s += [text(18, 34, "HVEPS output depends", 24, weight=700),
          text(18, 64, "on the power boundary", 24, weight=700),
          text(18, 96, "Six tests · power in kW", 24, fill=MUTED),
          text(18, 126, "Test 4 was not reduced:", 24, fill=MUTED),
          text(18, 156, "scramjet pressure data", 24, fill=MUTED),
          text(18, 186, "were anomalous", 24, fill=MUTED)]
    legends = [("#2779a7", "Load bank: measured", "external dissipation"),
               ("#7656a5", "Active channel output", "end losses excluded"),
               ("#c53f81", "Matched-load maximum", "post-test model")]
    yy = 228
    for color, line1, line2 in legends:
        s += [f'<rect x="18" y="{yy-19}" width="18" height="18" rx="4" fill="{color}"/>',
              text(46, yy, line1, 24, fill=MUTED), text(46, yy + 28, line2, 24, fill=MUTED)]
        yy += 66
    x0, x1 = 20, 225
    tests = [
        (1, 1.48, 91, (3.81, 6.55, 8.24)),
        (2, 1.48, 85, (3.20, 4.00, 7.76)),
        (3, 1.48, 89, (6.52, 14.38, 14.80)),
        (5, 1.80, 81.5, (5.55, 7.25, 8.09)),
        (6, 1.80, 77, (5.83, 8.46, 8.48)),
        (7, 1.80, 82, (2.10, 2.98, 6.94)),
    ]
    axis_top = 435
    axis_bottom = 2290
    for tick in (0, 5, 10, 15):
        x = x0 + tick / 15 * (x1 - x0)
        s += [f'<line x1="{x:.1f}" y1="{axis_top}" x2="{x:.1f}" y2="{axis_bottom}" stroke="{GRID}" stroke-width="1"/>',
              text(x, axis_bottom + 28, tick, 24, fill=MUTED, anchor="middle")]
    colors = [item[0] for item in legends]
    panel_height = 315
    for i, (test, field, eta, values) in enumerate(tests):
        y = 430 + i * panel_height
        s += [f'<rect x="10" y="{y}" width="300" height="288" rx="14" fill="#ffffff" stroke="#d9e3ed"/>',
              text(20, y + 31, f"Test {test}", 24, weight=700),
              text(20, y + 61, f"Peak field {field:.2f} T", 24, fill=MUTED),
              text(20, y + 91, "Combustion efficiency:", 24, fill=MUTED),
              text(20, y + 121, f"{eta:g}%" + (" · mean case" if test == 3 else ""), 24, fill=MUTED)]
        for tick in (0, 5, 10, 15):
            gx = x0 + tick / 15 * (x1 - x0)
            s.append(f'<line x1="{gx:.1f}" y1="{y+131}" x2="{gx:.1f}" y2="{y+276}" stroke="{GRID}" stroke-width="1"/>')
        for row, (value, color) in enumerate(zip(values, colors)):
            by = y + 140 + row * 43
            bw = value / 15 * (x1 - x0)
            s += [f'<rect x="{x0}" y="{by}" width="{x1-x0}" height="28" rx="8" fill="#e7edf4"/>',
                  f'<rect x="{x0}" y="{by}" width="{bw:.2f}" height="28" rx="8" fill="{color}"/>',
                  text(min(x0 + bw + 7, 248), by + 24, f"{value:.2f}", 24, weight=700, fill=INK)]
    # Explain the omitted Test 3 bounds separately from test-data uncertainty.
    s += [text(18, 2380, "Power (kW)", 24, fill=INK),
          text(18, 2420, "Test 3: mean case;", 24, fill=MUTED),
          text(18, 2454, "combustion-efficiency", 24, fill=MUTED),
          text(18, 2488, "bounds omitted.", 24, fill=MUTED),
          text(18, 2522, "Bars compare different", 24, fill=MUTED),
          text(18, 2556, "power boundaries;", 24, fill=MUTED),
          text(18, 2590, "not net output.", 24, fill=MUTED),
          text(18, 2624, "No error intervals", 24, fill=MUTED),
          text(18, 2658, "are plotted here.", 24, fill=MUTED),
          '</svg>']
    (OUT / "hveps-test-series-mobile.svg").write_text('\n'.join(s) + '\n', encoding='utf-8')


def ionizer():
    w, h = 280, 740
    s = start(w, h,
        "A 2001 NASA model required more ionizer power than its MHD generator produced",
        "For one modeled case, calculated external ionizer demand was 827 megawatts and calculated magnetohydrodynamic generator output was 151 megawatts. Ionizer demand was about 5.48 times gross generation. These rounded model values are not hardware measurements or a complete vehicle balance. The authors assumed a 100 percent efficient ionizer.")
    s += [text(18, 34, "One modeled power", 24, weight=700),
          text(18, 64, "trade-off", 24, weight=700),
          text(18, 96, "NASA model · 2001", 22, fill=MUTED),
          text(18, 126, "Rounded values · MW", 22, fill=MUTED),
          text(18, 176, "External ionizer", 22, weight=700),
          text(18, 204, "demand (input power)", 22, fill=MUTED)]
    x0, scale = 18, 0.25
    s += [f'<rect x="{x0}" y="220" width="236" height="38" rx="9" fill="#e7edf4"/>',
          f'<rect x="{x0}" y="220" width="{827*scale:.1f}" height="38" rx="9" fill="#e454b7"/>',
          text(18, 290, "827 MW", 24, weight=700),
          text(18, 344, "MHD generator", 22, weight=700),
          text(18, 372, "output (gross power)", 22, fill=MUTED),
          f'<rect x="{x0}" y="389" width="236" height="38" rx="9" fill="#e7edf4"/>',
          f'<rect x="{x0}" y="389" width="{151*scale:.1f}" height="38" rx="9" fill="#46a8cb"/>',
          text(18, 459, "151 MW", 24, weight=700),
          '<line x1="18" y1="485" x2="262" y2="485" stroke="#9babc0" stroke-width="2"/>',
          text(18, 524, "Demand ≈ 5.48 ×", 22, weight=700),
          text(18, 552, "gross generation", 22, weight=700),
          text(18, 586, "Ideal 100%-efficient", 22, fill=MUTED),
          text(18, 614, "ionizer assumed.", 22, fill=MUTED),
          text(18, 648, "Not a vehicle power", 22, fill=MUTED),
          text(18, 676, "balance or a", 22, fill=MUTED),
          text(18, 704, "hardware test.", 22, fill=MUTED),
          '</svg>']
    (OUT / "nasa-ionizer-power-comparison-mobile.svg").write_text('\n'.join(s) + '\n', encoding='utf-8')


if __name__ == "__main__":
    mach6()
    hveps()
    ionizer()
    print("Wrote three large-type mobile SVG variants.")
