#!/usr/bin/env python3
"""Rebuild the descriptive range charts from NASA's 2026 SmallSat survey table.

These are separate minimum-to-maximum envelopes for surveyed small-spacecraft
devices. Thrust and specific-impulse endpoints are not paired observations.
"""
from pathlib import Path
import csv
import json

ROOT = Path(__file__).resolve().parent
METHODS = json.loads((ROOT / "small-spacecraft-electric-ranges-methods.json").read_text(encoding="utf-8"))
DATA = [
    (item["name"], *item["thrust_mN"], *item["specific_impulse_s"])
    for item in METHODS["technologies"]
]

with (ROOT / "small-spacecraft-electric-ranges.csv").open("w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerow(["technology", "thrust_min_mN", "thrust_max_mN", "specific_impulse_min_s", "specific_impulse_max_s"])
    writer.writerows(DATA)

COLORS = ["#d652ff", "#ff79bd", "#51d4e8", "#91aaff", "#f3b560", "#77d9ac"]

def xlog(value, low, high, left, width):
    import math
    return left + (math.log10(value) - math.log10(low)) / (math.log10(high) - math.log10(low)) * width

def esc(value):
    return value.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")

def make_svg(mobile=False):
    if mobile:
        width, height, left, right = 430, 1100, 172, 20
        top1, top2, row_h, axis_h = 145, 610, 48, 300
        font, tick_font, title_font = 17, 16, 22
    else:
        width, height, left, right = 1420, 780, 355, 52
        top1, top2, row_h, axis_h = 154, 495, 44, 254
        font, tick_font, title_font = 20, 17, 28
    plot_w = width - left - right
    titles = [("Thrust range", "mN", 0.001, 1000, top1),
              ("Specific impulse range", "seconds", 10, 10000, top2)]
    parts = [f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {width} {height}" role="img" aria-labelledby="title desc">',
             '<title id="title">NASA small-spacecraft electric propulsion performance ranges</title>',
             '<desc id="desc">Two separate logarithmic range plots show thrust and specific-impulse ranges for six electric propulsion types in NASA’s 2026 small-spacecraft survey. Range endpoints are not paired measurements and are not universal technology limits. The electrospray 20 micronewton value is the lowest nominal survey entry, not a minimum operating capability: NASA’s 2017 ST7 flight-performance paper reports CMNT heads individually throttleable from 5 to 30 micronewtons. Read with the 2026 PDF Table 4-8 row “4 × 20” and its four-head label, this supports 20 micronewtons per head; 80 micronewtons is only the arithmetic sum if all four operate together, not a measured flight condition. NASA’s live HTML says millinewtons where the PDF and flight paper say micronewtons, apparently a factor-1000 label/version error. Hall-effect lower values are rounded survey upper bounds: NASA Table 4-10 lists MUSIC-SI with strict inequalities &lt;0.25 millinewtons and &lt;200 seconds; the exact values are unknown.</desc>',
             '<rect width="100%" height="100%" rx="22" fill="#08172d"/>',
             (f'<text x="24" y="38" fill="#f2f6ff" font-family="Arial,sans-serif" font-size="{title_font}" font-weight="700">NASA survey: small-spacecraft</text><text x="24" y="66" fill="#f2f6ff" font-family="Arial,sans-serif" font-size="{title_font}" font-weight="700">electric propulsion ranges</text>' if mobile else f'<text x="{left}" y="48" fill="#f2f6ff" font-family="Arial,sans-serif" font-size="{title_font}" font-weight="700">Surveyed device ranges · small spacecraft</text>')]
    for title, unit, low, high, top in titles:
        panel_title = "Specific impulse (s)" if mobile and high == 10000 else f"{title} ({unit})"
        panel_x = 24 if mobile else left
        parts.append(f'<text x="{panel_x}" y="{top-28}" fill="#eaf1ff" font-family="Arial,sans-serif" font-size="{title_font}" font-weight="700">{panel_title}</text>')
        ticks = [0.001,0.01,0.1,1,10,100,1000] if high == 1000 else [10,100,1000,10000]
        for tick in ticks:
            if low <= tick <= high:
                x = xlog(tick, low, high, left, plot_w)
                parts.append(f'<line x1="{x:.1f}" y1="{top-8}" x2="{x:.1f}" y2="{top+row_h*6-10}" stroke="#425777" stroke-width="1"/>')
                label = f"{tick:g}"
                parts.append(f'<text x="{x:.1f}" y="{top+row_h*6+18}" fill="#c6d3e9" text-anchor="middle" font-family="Arial,sans-serif" font-size="{tick_font}">{label}</text>')
        for i, row in enumerate(DATA):
            name, tmin, tmax, imin, imax = row
            y = top + i*row_h + row_h/2
            if mobile and name == "Pulsed plasma / vacuum arc":
                parts.append(f'<text x="{left-12}" y="{y-3:.1f}" fill="#f2f6ff" text-anchor="end" font-family="Arial,sans-serif" font-size="{font}"><tspan x="{left-12}">Pulsed plasma /</tspan><tspan x="{left-12}" dy="19">vacuum arc</tspan></text>')
            else:
                parts.append(f'<text x="{left-12}" y="{y+6:.1f}" fill="#f2f6ff" text-anchor="end" font-family="Arial,sans-serif" font-size="{font}">{esc(name)}</text>')
            a,b = (tmin,tmax) if high == 1000 else (imin,imax)
            xa,xb = xlog(a,low,high,left,plot_w),xlog(b,low,high,left,plot_w)
            color=COLORS[i]
            parts.append(f'<line x1="{xa:.1f}" y1="{y:.1f}" x2="{xb:.1f}" y2="{y:.1f}" stroke="{color}" stroke-width="12" stroke-linecap="round"/>')
            parts.append(f'<circle cx="{xa:.1f}" cy="{y:.1f}" r="9" fill="#08172d" stroke="{color}" stroke-width="4"/><circle cx="{xb:.1f}" cy="{y:.1f}" r="9" fill="{color}" stroke="#08172d" stroke-width="2"/>')
            if name == "Hall effect":
                # The summary-table minima are rounded upper bounds from strict
                # inequalities in NASA Table 4-10, not exact device minima.
                parts.append(f'<path d="M {xa-22:.1f} {y:.1f} L {xa-8:.1f} {y:.1f} M {xa-22:.1f} {y:.1f} l 7 -6 M {xa-22:.1f} {y:.1f} l 7 6" fill="none" stroke="{color}" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>')
    note_y = height-64
    note_x = 24 if mobile else left
    parts += [f'<text x="{note_x}" y="{note_y-12}" fill="#c6d3e9" font-family="Arial,sans-serif" font-size="{tick_font}">Surveyed ranges; endpoints are not paired measurements</text>',
              f'<text x="{note_x}" y="{note_y+12}" fill="#c6d3e9" font-family="Arial,sans-serif" font-size="{tick_font}">or universal technology limits.</text>',
              f'<text x="{note_x}" y="{note_y+36}" fill="#f3b560" font-family="Arial,sans-serif" font-size="{tick_font}">Hall lower marks are rounded bounds: &lt;0.25 mN and &lt;200 s.</text>', '</svg>']
    return "\n".join(parts) + "\n"

(ROOT / "small-spacecraft-electric-ranges.svg").write_text(make_svg(), encoding="utf-8")
(ROOT / "small-spacecraft-electric-ranges-mobile.svg").write_text(make_svg(True), encoding="utf-8")
