#!/usr/bin/env python3
"""Redraw the published normalized-thrust curve from Kaminaga et al. (2005).

Values are approximate visual readings of Fig. 12, not a source data table.
The point at s_gen=-0.40 is deliberately omitted as a y-value: the authors
report choking/no useful thrust there, so the chart's cross is not digitized.
"""
from pathlib import Path
import csv
import html
import json
import xml.etree.ElementTree as ET

ROOT = Path(__file__).resolve().parent
POINTS = [
    (0.00, 1.000), (-0.05, 0.981), (-0.10, 0.977), (-0.15, 0.990),
    (-0.20, 1.022), (-0.25, 1.036), (-0.30, 1.026), (-0.35, 1.002),
]
READOUT_TOLERANCE = 0.002  # plot-reading tolerance only, not study uncertainty
INK, MUTED, GRID, BLUE, PINK = "#14253a", "#51677e", "#d9e3ed", "#2e79b7", "#d95498"


def esc(value):
    return html.escape(str(value), quote=True)


def label(x, y, value, size=16, weight=400, fill=INK, anchor="start"):
    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}">{esc(value)}</text>')


def make_svg(mobile=False):
    w, h = (760, 850) if mobile else (1200, 690)
    left, right = (115, w - 32) if mobile else (205, 1140)
    top, bottom = (220, 610) if mobile else (190, 520)
    ymin, ymax = 0.95, 1.05
    xmin, xmax = -0.4, 0.0

    def px(v):
        return left + (right - left) * (xmax - v) / (xmax - xmin)

    def py(v):
        return bottom - (bottom - top) * (v - ymin) / (ymax - ymin)

    bits = [f'<rect width="{w}" height="{h}" rx="24" fill="#f7faff"/>',
            label(38, 52, "A published MHD-bypass thrust comparison", 27 if mobile else 30, 700),
            label(38, 90, "Kaminaga, Tomioka & Yamasaki · Mach 8 model · Fig. 12", 16 if mobile else 18, 400, MUTED)]
    bits.append(label(38, 142, "Normalized net thrust (baseline = 1)", 17, 700))
    for tick in (0.95, 0.975, 1.0, 1.025, 1.05):
        y = py(tick)
        bits.append(f'<line x1="{left}" y1="{y:.2f}" x2="{right}" y2="{y:.2f}" stroke="{GRID}" stroke-width="2"/>')
        bits.append(label(left - 12, y + 6, f"{tick:.3f}".rstrip("0").rstrip("."), 14, 400, MUTED, "end"))
    for tick in (0.0, -0.1, -0.2, -0.3, -0.4):
        x = px(tick)
        bits.append(f'<line x1="{x:.2f}" y1="{top}" x2="{x:.2f}" y2="{bottom}" stroke="{GRID}" stroke-width="2"/>')
        bits.append(label(x, bottom + 28, f"{tick:.1f}" if tick else "0", 14, 400, MUTED, "middle"))

    coords = [(px(x), py(y)) for x, y in POINTS]
    path = " ".join(("M" if i == 0 else "L") + f"{x:.2f},{y:.2f}" for i, (x, y) in enumerate(coords))
    bits.append(f'<path d="{path}" fill="none" stroke="{BLUE}" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>')
    for (xv, yv), (x, y) in zip(POINTS, coords):
        dy = (bottom - top) * READOUT_TOLERANCE / (ymax - ymin)
        bits.append(f'<line x1="{x:.2f}" y1="{y-dy:.2f}" x2="{x:.2f}" y2="{y+dy:.2f}" stroke="{PINK}" stroke-width="3"/>')
        bits.append(f'<circle cx="{x:.2f}" cy="{y:.2f}" r="8" fill="{BLUE}" stroke="white" stroke-width="3"><title>s_gen {xv:.2f}; normalized thrust about {yv:.3f}</title></circle>')
    bits.append(f'<line x1="{left}" y1="{bottom}" x2="{right}" y2="{bottom}" stroke="{INK}" stroke-width="2"/>')
    bits.append(label((left+right)/2, bottom+62, "Generator interaction parameter, s_gen", 16, 700, INK, "middle"))

    if mobile:
        bits.append(f'<line x1="42" y1="696" x2="78" y2="696" stroke="{BLUE}" stroke-width="4"/>')
        bits.append(label(88, 702, "Approximate visual readings from Fig. 12", 14, 400, MUTED))
        bits.append(f'<line x1="42" y1="730" x2="42" y2="750" stroke="{PINK}" stroke-width="3"/>')
        bits.append(label(56, 746, "±0.002 plot-readout tolerance only", 14, 400, MUTED))
        bits.append(label(38, 790, "At s_gen = −0.40: fuel addition caused choking;", 14, 700, INK))
        bits.append(label(38, 814, "the paper reports no useful thrust; not plotted as a datum.", 13, 400, MUTED))
    else:
        bits.append(f'<line x1="220" y1="622" x2="256" y2="622" stroke="{BLUE}" stroke-width="4"/>')
        bits.append(label(268, 628, "Approximate visual readings from published Fig. 12", 14, 400, MUTED))
        bits.append(f'<line x1="660" y1="622" x2="660" y2="642" stroke="{PINK}" stroke-width="3"/>')
        bits.append(label(674, 628, "±0.002 plot-readout tolerance only", 14, 400, MUTED))
        bits.append(label(205, 674, "At s_gen = −0.40: fuel addition caused choking; the paper reports no useful thrust.", 14, 700, INK))
    title = "Approximate redraw of Kaminaga et al. 2005 Figure 12"
    desc = ("Normalized net thrust from a published quasi-one-dimensional Mach 8 MHD energy-bypass model. "
            "Blue markers are approximate visual readings as the magnitude of negative generator interaction increases from zero to 0.35 (s_gen moves from zero to minus 0.35). "
            "Pink bars show plot-readout tolerance only, not scientific uncertainty. At minus 0.40 the paper reports flow choking after fuel addition and no useful thrust; no normalized value is digitized.")
    return f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {w} {h}" role="img" aria-labelledby="title desc"><title id="title">{esc(title)}</title><desc id="desc">{esc(desc)}</desc>{"".join(bits)}</svg>\n'


def main():
    for mobile in (False, True):
        suffix = "-mobile" if mobile else ""
        (ROOT / f"kaminaga-2005-normalized-thrust{suffix}.svg").write_text(make_svg(mobile), encoding="utf-8")
    with (ROOT / "kaminaga-2005-normalized-thrust.csv").open("w", newline="", encoding="utf-8") as f:
        w = csv.writer(f)
        w.writerow(["generator_interaction_parameter_s_gen", "normalized_net_thrust_approx", "visual_readout_tolerance_only", "status"])
        for x, y in POINTS:
            w.writerow([x, y, READOUT_TOLERANCE, "approximate visual digitization of Fig. 12; not raw data"])
        w.writerow([-0.40, "", "", "flow choked after fuel addition; paper reports no useful thrust; no y-value digitized"])
    methods = {
        "citation": "Kaminaga, S.; Tomioka, S.; Yamasaki, H. Performance of Scramjet Engine with MHD Energy Bypass System. Journal of the Japan Society for Aeronautical and Space Sciences 53(623), 554–561 (2005). DOI 10.2322/jjsass.53.554.",
        "source_pdf": "https://www.jstage.jst.go.jp/article/jjsass/53/623/53_623_554/_pdf",
        "source_pdf_sha256": "91B18FBDA89B1308BD7397B36243BEB565EEF70715FF891A771568EE65B496BA",
        "figure": "Figure 12, printed p. 560 / PDF page 7; points were visually digitized from a locally rendered source PDF.",
        "digitization": {"interaction_parameter_step": 0.05, "normalized_thrust_y_axis": [0.95, 1.05], "visual_readout_tolerance": 0.002, "tolerance_meaning": "estimated image-reading tolerance only; not model uncertainty, experimental error, or confidence interval", "interpolation": "line segments are visual guides only; no interpolation or fit is claimed"},
        "conditions": {"flight_mach": 8, "altitude_context_km": 30, "inlet_static_pressure_kPa": 1.13, "inlet_static_temperature_K": 227, "inlet_contraction_ratio": 2.86, "inlet_capture_fraction": 1.0, "working_gas": "N2 78%, O2 21%, Ar 1%", "fuel": "H2", "equivalence_ratio": 1.0, "assumed_mhd_electrical_efficiency": 0.8, "assumed_accelerator_efficiency": 0.8, "combustor_minimum_mach": 1.1, "wall": "adiabatic", "combustion": "chemical equilibrium; mixing delay neglected", "power_routing": "all MHD generator electrical output is assigned to the downstream MHD accelerator"},
        "limits": ["steady quasi-one-dimensional numerical model, not a hardware test or flight", "oblique inlet shocks were not explicitly modeled; inlet capture was set to unity", "electrical/accelerator efficiencies are assumed model parameters, not measured efficiencies", "at s_gen=-0.40 the article reports choking after fuel addition and no useful thrust; its plotted cross is not assigned a digitized thrust value", "no external installation mass, complete auxiliary-power/vehicle balance, or mission comparison is established"],
        "result": "The authors' prose describes the change in normalized net thrust as about 3%. Separately, approximate visual readings from Fig. 12 range from 0.977 to 1.036 of baseline, a 5.9 percentage-point peak-to-trough span. The readings are not exact source data; the prose summary and the graph's approximate min-to-max span are different descriptions and should not be conflated.",
        "approximate_figure_readout": {"minimum_normalized_thrust": 0.977, "maximum_normalized_thrust": 1.036, "peak_to_trough_percentage_points": 5.9, "status": "visual readings from published graph, not source tabular data"}
    }
    (ROOT / "kaminaga-2005-methods.json").write_text(json.dumps(methods, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
    for name in ("kaminaga-2005-normalized-thrust.svg", "kaminaga-2005-normalized-thrust-mobile.svg"):
        ET.parse(ROOT / name)
    assert len(POINTS) == 8 and POINTS[0] == (0.0, 1.0) and max(y for _, y in POINTS) == 1.036
    print("Generated two responsive Fig. 12 redraws, approximate CSV, and methods/limits JSON.")


if __name__ == "__main__":
    main()
