#!/usr/bin/env python3
"""Rebuild the source-bounded HVEPS Test 3 output comparison."""

from html import escape
from pathlib import Path
import csv

OUT = Path(__file__).resolve().parent
INK = "#14253a"
MUTED = "#51677e"
GRID = "#d9e3ed"
BLUE = "#2779a7"
PURPLE = "#7656a5"
PINK = "#c53f81"
VALUES = [
    ("Load-bank output", 6.52, "Measured at load bank", "Measured dissipated power at the external resistive load"),
    ("Active-channel output", 14.38, "Active channel; end losses excluded", "Post-test derived; excludes end-region losses"),
    ("Matched-load maximum", 14.80, "Matched-load model estimate", "Post-test load-line model estimate"),
]
SERIES = [
    {"test": 1, "field_T": 1.48, "combustion_efficiency_percent": 91.0, "load_current_A": 21.55, "load_bank_kW": 3.81, "active_channel_kW": 6.55, "matched_load_maximum_kW": 8.24},
    {"test": 2, "field_T": 1.48, "combustion_efficiency_percent": 85.0, "load_current_A": 10.85, "load_bank_kW": 3.20, "active_channel_kW": 4.00, "matched_load_maximum_kW": 7.76},
    {"test": 3, "field_T": 1.48, "combustion_efficiency_percent": 89.0, "combustion_efficiency_case": "mean; bounds omitted from this plot", "load_current_A": 57.82, "load_bank_kW": 6.52, "active_channel_kW": 14.38, "matched_load_maximum_kW": 14.80},
    {"test": 5, "field_T": 1.80, "combustion_efficiency_percent": 81.5, "load_current_A": 22.47, "load_bank_kW": 5.55, "active_channel_kW": 7.25, "matched_load_maximum_kW": 8.09},
    {"test": 6, "field_T": 1.80, "combustion_efficiency_percent": 77.0, "load_current_A": 28.36, "load_bank_kW": 5.83, "active_channel_kW": 8.46, "matched_load_maximum_kW": 8.48},
    {"test": 7, "field_T": 1.80, "combustion_efficiency_percent": 82.0, "load_current_A": 8.00, "load_bank_kW": 2.10, "active_channel_kW": 2.98, "matched_load_maximum_kW": 6.94},
]


def text(x, y, value, size=18, weight=400, fill=INK, anchor="start"):
    return f'<text x="{x}" y="{y}" font-family="Arial, sans-serif" font-size="{size}" font-weight="{weight}" fill="{fill}" text-anchor="{anchor}">{escape(value)}</text>'


def svg(mobile=False):
    w, h = (760, 720) if mobile else (1200, 590)
    left, right = (220, 690) if mobile else (320, 1120)
    ys = [270, 420, 570] if mobile else [220, 340, 460]
    max_kw = 16
    x = lambda value: left + (right - left) * value / max_kw
    bits = [f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {w} {h}" role="img" aria-labelledby="title desc">',
            '<title id="title">HVEPS Test 3 output values at different boundaries</title>',
            '<desc id="desc">For test 3, 6.52 kilowatts were dissipated in the measured external load bank. Post-test analysis estimated 14.38 kilowatts across the active channel after excluding end-region losses, and estimated a 14.80 kilowatt maximum at matched load. These are not equivalent direct measurements.</desc>',
            f'<rect width="{w}" height="{h}" rx="26" fill="#f7faff"/>',
            text(38, 54, "One test, three different power boundaries", 27 if mobile else 30, 700),
            text(38, 92, "HVEPS Test 3 · peak magnetic field 1.48 T · output in kW", 16 if mobile else 18, 400, MUTED)]
    for tick in range(0, max_kw + 1, 4):
        xx = x(tick)
        bits.append(f'<line x1="{xx:.1f}" y1="150" x2="{xx:.1f}" y2="{ys[-1]+24}" stroke="{GRID}" stroke-width="2"/>')
        bits.append(text(xx, ys[-1] + 52, str(tick), 14, 400, MUTED, "middle"))
    colors = [BLUE, PURPLE, PINK]
    for (label, value, note, _detail), yy, color in zip(VALUES, ys, colors):
        if mobile:
            bits.append(text(40, yy - 50, label, 18, 700))
            bits.append(text(40, yy - 25, note, 13, 400, MUTED))
        else:
            bits.append(text(left - 18, yy - 10, label, 17, 700, INK, "end"))
            bits.append(text(left - 18, yy + 15, note, 13, 400, MUTED, "end"))
        bits.append(f'<rect x="{left}" y="{yy-14}" width="{right-left}" height="28" rx="14" fill="#e7edf4"/>')
        bits.append(f'<rect x="{left}" y="{yy-14}" width="{(right-left)*value/max_kw:.2f}" height="28" rx="14" fill="{color}"/>')
        bits.append(text(min(x(value) + 10, right - 2), yy + 6, f"{value:.2f}", 16, 700, color))
    bits.append(text((left+right)/2, h-20, "Power (kW)", 14, 700, MUTED, "middle"))
    bits.append('</svg>')
    return '\n'.join(bits) + '\n'


def series_svg(mobile=False):
    w, h = (760, 1460) if mobile else (1200, 1045)
    left, right = (228, 690) if mobile else (300, 1100)
    scale_max = 18
    x = lambda value: left + (right - left) * value / scale_max
    top = 278 if mobile else 222
    group_h = 184 if mobile else 126
    row_offsets = [63, 93, 123] if mobile else [54, 78, 102]
    bar_h = 13 if mobile else 15
    colors = [BLUE, PURPLE, PINK]
    series = [
        ("Load-bank power (reported measured dissipation)", "load_bank_kW", BLUE),
        ("Active-channel output (end losses excluded)", "active_channel_kW", PURPLE),
        ("Maximum at matched load (post-test model)", "matched_load_maximum_kW", PINK),
    ]
    bits = [f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {w} {h}" role="img" aria-labelledby="series-title series-desc">',
            '<title id="series-title">HVEPS generator output across six analysed test points</title>',
            '<desc id="series-desc">Six tests are shown: 1, 2, 3, 5, 6 and 7. Each group compares load-bank dissipation reported in Table VI, active-channel power reported with end losses excluded, and maximum power at matched load from post-test analysis. Test 4 was not reduced because its scramjet pressure data were anomalous. Test 3 uses the mean combustion-efficiency case; its bounds are not plotted.</desc>',
            f'<rect width="{w}" height="{h}" rx="26" fill="#f7faff"/>',
            text(38, 52, "HVEPS: output depends on the boundary", 26 if mobile else 30, 700),
            text(38, 87, "Six analysed points from AFRL preprint ADA471429 · power in kW", 15 if mobile else 18, 400, MUTED)]
    legend_y = 132 if mobile else 132
    if mobile:
        for i, (label, _key, color) in enumerate(series):
            yy = legend_y + 28 * i
            bits.append(f'<rect x="40" y="{yy-12}" width="18" height="12" rx="5" fill="{color}"/>')
            bits.append(text(68, yy, label, 13, 400, MUTED))
    else:
        lx = [300, 565, 845]
        for xx, (label, _key, color) in zip(lx, series):
            bits.append(f'<rect x="{xx}" y="{legend_y-12}" width="18" height="14" rx="5" fill="{color}"/>')
            bits.append(text(xx+27, legend_y, label, 13, 400, MUTED))
    plot_top = top
    plot_bottom = top + group_h * (len(SERIES) - 1) + max(row_offsets) + bar_h
    for tick in [0, 5, 10, 15]:
        xx = x(tick)
        bits.append(f'<line x1="{xx:.1f}" y1="{plot_top}" x2="{xx:.1f}" y2="{plot_bottom}" stroke="{GRID}" stroke-width="2"/>')
        bits.append(text(xx, plot_bottom+27, str(tick), 13 if mobile else 14, 400, MUTED, "middle"))
    for idx, row in enumerate(SERIES):
        gy = top + idx * group_h
        comb = row['combustion_efficiency_percent']
        annotation = "mean case" if row['test'] == 3 else ""
        label = f"Test {row['test']}  ·  B = {row['field_T']:.2f} T  ·  combustion {comb:g}% {annotation}".strip()
        bits.append(text(40, gy+22, label, 15 if mobile else 16, 700))
        for off, (series_label, key, color) in zip(row_offsets, series):
            yy = gy + off
            value = row[key]
            bits.append(f'<rect x="{left}" y="{yy-bar_h}" width="{right-left}" height="{bar_h*2}" rx="{bar_h}" fill="#e7edf4"/>')
            bits.append(f'<rect x="{left}" y="{yy-bar_h}" width="{(right-left)*value/scale_max:.2f}" height="{bar_h*2}" rx="{bar_h}" fill="{color}"/>')
            bits.append(text(min(x(value)+8, right-2), yy+5, f"{value:.2f}", 13 if mobile else 14, 700, color))
    bits.append(text((left+right)/2, h-16, "Power (kW)", 14, 700, MUTED, "middle"))
    bits.append('</svg>')
    return '\n'.join(bits) + '\n'


def main():
    for mobile in (False, True):
        suffix = '-mobile' if mobile else ''
        (OUT / f'hveps-output-boundaries{suffix}.svg').write_text(svg(mobile), encoding='utf-8')
    with (OUT / 'hveps-output-boundaries.csv').open('w', newline='', encoding='utf-8') as stream:
        writer = csv.writer(stream)
        writer.writerow(['test', 'peak_field_T', 'power_kW', 'quantity', 'evidence_type', 'source_table'])
        for label, value, note, detail in VALUES:
            writer.writerow([3, 1.48, f'{value:.2f}', label, detail, 'Lineberry et al. 2007, Table VI and notes'])
    for mobile in (False, True):
        suffix = '-mobile' if mobile else ''
        (OUT / f'hveps-test-series{suffix}.svg').write_text(series_svg(mobile), encoding='utf-8')
    with (OUT / 'hveps-test-series.csv').open('w', newline='', encoding='utf-8') as stream:
        fields = ['test', 'peak_field_T', 'combustion_efficiency_percent', 'combustion_efficiency_case', 'load_current_A', 'load_bank_power_kW', 'active_channel_output_end_losses_excluded_kW', 'matched_load_maximum_model_kW']
        writer = csv.DictWriter(stream, fieldnames=fields)
        writer.writeheader()
        for row in SERIES:
            writer.writerow({
                'test': row['test'], 'peak_field_T': f"{row['field_T']:.2f}",
                'combustion_efficiency_percent': f"{row['combustion_efficiency_percent']:.1f}",
                'combustion_efficiency_case': row.get('combustion_efficiency_case', 'nominal'),
                'load_current_A': f"{row['load_current_A']:.2f}",
                'load_bank_power_kW': f"{row['load_bank_kW']:.2f}",
                'active_channel_output_end_losses_excluded_kW': f"{row['active_channel_kW']:.2f}",
                'matched_load_maximum_model_kW': f"{row['matched_load_maximum_kW']:.2f}",
            })


if __name__ == '__main__':
    main()
