"""Synthetic analytic teaching study; requires Python 3 and matplotlib."""
from pathlib import Path
import csv, json, math, hashlib
from decimal import Decimal, localcontext
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from pathlib import Path as _Path
import sys as _sys
_sys.path.insert(0, str(_Path(__file__).resolve().parents[1]))
from svg_accessibility import describe_svg

OUT = Path(__file__).resolve().parent
rows = []
checks = 0
for i in range(101):
    r = i / 100
    power = 1 - r
    speed = math.sqrt(power)
    rows.append((r, power, speed, speed))
    # Independent high-precision reconstruction from dimensional balances.
    with localcontext() as ctx:
        ctx.prec = 50
        rd = Decimal(i) / Decimal(100)
        mdot, v0 = Decimal('2.7'), Decimal('730')
        initial = mdot * v0 * v0 / 2
        extracted = rd * initial
        v1 = (2 * (initial - extracted) / mdot).sqrt()
        assert abs(speed - float(v1 / v0)) < 2e-15
        assert abs(power - float((initial - extracted) / initial)) < 2e-15
        assert abs(speed - float((mdot * v1) / (mdot * v0))) < 2e-15
        checks += 3
    assert abs(speed * speed + r - 1) < 3e-16
    checks += 1
assert rows[0] == (0., 1., 1., 1.)
assert rows[-1] == (1., 0., 0., 0.)
checks += 2
for a, b in zip(rows, rows[1:]):
    assert b[0] > a[0] and b[1] < a[1] and b[2] < a[2]
    checks += 1
with (OUT / 'jet-recovery.csv').open('w', newline='', encoding='utf-8') as f:
    writer = csv.writer(f)
    writer.writerow(['extracted_kinetic_power_fraction', 'remaining_kinetic_power_fraction', 'speed_ratio', 'momentum_flux_ratio'])
    writer.writerows(rows)

plt.rcParams.update({'font.family':'DejaVu Sans', 'font.size':11, 'svg.fonttype':'none', 'pdf.fonttype':42})
for mobile in (False, True):
    if mobile:
        # 225 pt SVG at 300 CSS px: 11 pt text renders as 14.67 px.
        fig, ax = plt.subplots(figsize=(3.125, 4.7))
        fig.subplots_adjust(left=.22, right=.94, bottom=.20, top=.72)
        r, p, v, _ = zip(*rows)
        ax.plot(r, p, color='#6941a5', lw=2.3)
        ax.plot(r, v, color='#007e91', lw=2.3, linestyle='--')
        ax.set(xlim=(0,1), ylim=(0,1.03))
        ax.set_xlabel('Extracted power\nfraction, r', fontsize=12)
        ax.set_ylabel('Remaining fraction', fontsize=12)
        ax.set_xticks([0,.5,1], ['0','0.5','1'])
        ax.set_yticks([0,.5,1], ['0','0.5','1'])
        ax.tick_params(labelsize=11)
        ax.grid(alpha=.2)
        ax.spines[['top','right']].set_visible(False)
        fig.suptitle('Ideal stream: power removal', y=.98, fontsize=11, weight='bold')
        fig.legend(ax.lines, ['Power: 1 − r', 'Speed / momentum flux:\n√(1 − r)'], loc='upper left', bbox_to_anchor=(.08,.94), frameon=False, fontsize=11, handlelength=1.5)
        fig.savefig(OUT / 'jet-recovery-mobile.svg')
        describe_svg(OUT / 'jet-recovery-mobile.svg','Removing kinetic power from one ideal stream','Two curves show the remaining kinetic-power fraction and speed or momentum-flux fraction as extraction increases. This is a synthetic analytical illustration under fixed-flow assumptions, not a net-thrust, engine-performance or scramjet prediction.')
        plt.close(fig)
        continue
    fig, ax = plt.subplots(figsize=(5.4, 6.8) if mobile else (9.4, 6.3))
    fig.patch.set_facecolor('#ffffff')
    fig.subplots_adjust(left=.17 if mobile else .12, right=.96, bottom=.33 if mobile else .25, top=.81)
    r, p, v, _ = zip(*rows)
    ax.plot(r, p, color='#6941a5', lw=2.7, label='Kinetic power: 1 − r')
    ax.plot(r, v, color='#007e91', lw=2.7, linestyle='--', label='Speed / momentum flux: √(1 − r)')
    ax.set(xlim=(0,1), ylim=(0,1.03), ylabel='Remaining fraction')
    ax.set_xlabel('Extracted fraction of initial\nkinetic power, r' if mobile else 'Extracted fraction of initial kinetic power, r')
    ax.set_xticks([0,.2,.4,.6,.8,1]); ax.set_yticks([0,.2,.4,.6,.8,1])
    ax.grid(alpha=.2)
    ax.spines[['top','right']].set_visible(False)
    ax.legend(loc='lower left', fontsize=9 if mobile else 10, frameon=True)
    fig.suptitle('Removing kinetic power\nfrom one ideal stream' if mobile else 'Removing kinetic power from one ideal stream', x=.5, y=.97, fontsize=16, weight='bold')
    fig.text(.5,.855 if mobile else .89,'Synthetic analytic study · 101 points · No measurements',ha='center',fontsize=9 if mobile else 11)
    note = ('Fixed mass flow and pressure condition;\nno later heat addition or reacceleration.\n\nNot net thrust, engine performance,\nor a scramjet prediction.\nr = 1 is a formal zero-speed limit,\nnot a real finite-area outlet.') if mobile else ('Fixed mass flow and pressure condition; no later heat addition or reacceleration.\nNot net thrust, engine performance, or a scramjet prediction.\nr = 1 is a formal zero-speed limit, not a real finite-area outlet.')
    fig.text(.17 if mobile else .12,.035,note,ha='left',va='bottom',fontsize=9,color='#333333',linespacing=1.5)
    name='jet-recovery-mobile' if mobile else 'jet-recovery'
    fig.savefig(OUT / (name+'.svg'))
    describe_svg(OUT / (name+'.svg'),'Removing kinetic power from one ideal stream','Two curves show the remaining kinetic-power fraction and speed or momentum-flux fraction as extraction increases. This is a synthetic analytical illustration under fixed-flow assumptions, not a net-thrust, engine-performance or scramjet prediction.')
    if not mobile: fig.savefig(OUT / (name+'.pdf'))
    plt.close(fig)

methods = {
 'classification':'synthetic analytic teaching study; no measurements; no confidence bands',
 'grid':{'r_min':0,'r_max':1,'step':0.01,'rows':101},
 'relations':{'remaining_kinetic_power_fraction':'1-r','speed_ratio':'sqrt(1-r)','momentum_flux_ratio':'sqrt(1-r)'},
 'derivation':'P=0.5*mass_flow*v^2; momentum_flux=mass_flow*v. Remove r*P_initial and solve for the remaining speed with the same mass flow.',
 'assumptions':['One ideal uniform one-dimensional stream','Fixed positive mass flow','Before and after speeds and kinetic powers use the same fixed inertial frame, for example a nonaccelerating vehicle frame; kinetic power and the extracted fraction r depend on the chosen frame','Fixed pressure condition; no change in pressure-thrust contribution is modeled','Nonnegative extracted fraction of initial kinetic power','No later heat addition or reacceleration'],
 'limitations':['Not net thrust, engine performance, or a scramjet prediction','No inlet momentum subtraction, pressure-force calculation, compressibility, shocks, combustion, extraction hardware, drag, thermal balance, or losses','Extracted kinetic power is not necessarily usable electrical power; conversion efficiency is unspecified','The r=1 endpoint is a formal zero-speed limit and cannot represent positive fixed mass flow through a finite-area outlet at finite density','The dimensional check values are arbitrary verification inputs, not vehicle specifications'],
 'verification':{'passed':True,'checks':checks,'independent_method':'50-digit Decimal dimensional energy balance with arbitrary positive mass flow and initial speed, then independent momentum-flux reconstruction; endpoints and strict monotonicity'},
 'caption':'Removing a fraction r of the initial kinetic power of one ideal stream leaves power fraction 1-r and speed and momentum-flux fractions sqrt(1-r), under the stated fixed-flow assumptions. This analytic illustration does not predict net thrust or engine performance.'
}
(OUT/'methods.json').write_text(json.dumps(methods,indent=2)+'\n',encoding='utf-8')
manifest = {p.name:hashlib.sha256(p.read_bytes()).hexdigest() for p in sorted(OUT.iterdir()) if p.is_file() and p.name != 'sha256.json'}
(OUT/'sha256.json').write_text(json.dumps(manifest,indent=2)+'\n',encoding='utf-8')
print(json.dumps({'rows':len(rows),'checks_passed':checks,'sha256':manifest},indent=2))
