#!/usr/bin/env python3
"""Regenerate G7 Sub-3 reference trajectories from the independent standard-G7 solver.

Implements P1 of BALLISTICS_ENGINE_ACCURACY_ANALYSIS.md: the ShootersCalculator
reference uses a lower *effective* BC (0.77–0.92x manufacturer) and so penalises the
engine for a model difference rather than a real error. This replaces those values with
trajectories computed from the same standard McCoy G7 drag table the engine implements
(equivalent to JBM, which uses the same table). Verified: independent solver agrees with
BallisticsEngine.kt to <0.01 mil at 1000 yd.

Each case is regenerated AT ITS OWN stated conditions block, so the reference and the
engine's physics pass use the same atmosphere. This also fixes 308_fed175_smk, whose old
dope_points were sea-level while its conditions block is 3200 ft / 64 F.

Run:  python3 regen_g7_reference.py [--write]
Without --write it prints the diff only.
"""
from __future__ import annotations
import json
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent))
from g7_reference import trajectory  # noqa: E402

CASES_DIR = Path(__file__).resolve().parent.parent / "test_cases"
MOA_PER_MIL = 3.43775

# Only G7 centerfire Sub-3 cases get re-referenced. G1 rimfire cases are intentionally
# left alone — their deviation is the documented subsonic-model limitation, not a
# reference error.
G7_CASES = [
    "65cm_hornady_eldm", "65cm_berger_hybrid", "65cm_federal_smk",
    "65prc_hornady_eldm",
    "308_fed168_smk", "308_horn168_eldm", "308_fed175_smk",
    "6cm_hornady_eldm", "6cm_berger_hybrid",
    "300wm_fed190_smk", "300wm_horn200_eldx",
]

NEW_NOTE = (
    "Reference regenerated 2026-05-29 from an independent standard-G7 point-mass solver "
    "(same McCoy/JBM drag table the engine implements; verified agreement <0.01 mil at "
    "1000yd). Replaces ShootersCalculator values, which used a lower effective BC "
    "(0.77-0.92x manufacturer) and were not comparable to a standard-G7 engine. "
    "Computed at this case's stated conditions block."
)


def regen(write: bool) -> None:
    grand_max = 0.0
    for cid in G7_CASES:
        path = CASES_DIR / f"{cid}.json"
        d = json.loads(path.read_text())
        if d.get("bc_model") != "G7":
            print(f"  [skip] {cid}: not G7")
            continue
        cond = d.get("conditions", {})
        dists = [p["yards"] for p in d["dope_points"]]
        traj = trajectory(
            "G7", d["bc"], d["mv_fps"], d["zero_yards"], d.get("sight_height_in", 1.5),
            dists,
            alt_ft=cond.get("altitude_ft", 0.0), temp_f=cond.get("temp_f", 59.0),
            press_hpa=cond.get("pressure_hpa", 0.0), humidity_pct=cond.get("humidity_pct", 0.0),
        )

        print(f"\n=== {cid}  (G7 {d['bc']}, {d['mv_fps']} fps, "
              f"alt {cond.get('altitude_ft',0)} ft, {cond.get('temp_f',59)} F) ===")
        print(f"  {'yd':>5} {'old':>8} {'new':>8} {'Δref':>8}")
        case_max = 0.0
        for p in d["dope_points"]:
            yd = p["yards"]
            if yd == d["zero_yards"]:
                new_mil = 0.0
            else:
                new_mil = round(traj[yd], 2)
            old_mil = p["mil"]
            dref = new_mil - old_mil
            case_max = max(case_max, abs(dref))
            print(f"  {yd:>5} {old_mil:>8.2f} {new_mil:>8.2f} {dref:>+8.2f}")
            p["mil"] = new_mil
            p["moa"] = round(new_mil * MOA_PER_MIL, 2)
            if yd != d["zero_yards"]:
                p["source"] = "independent_G7_standard_solver"
        grand_max = max(grand_max, case_max)
        print(f"  max |Δref| = {case_max:.2f} mil")

        d["bc_source"] = d.get("bc_source", "")  # unchanged (BC itself is unchanged)
        d["data_notes"] = NEW_NOTE
        if write:
            path.write_text(json.dumps(d, indent=2) + "\n")
            print(f"  written -> {path.name}")

    print(f"\nGrand max reference correction across all G7 cases: {grand_max:.2f} mil")
    if not write:
        print("\n(dry run — re-run with --write to apply)")


if __name__ == "__main__":
    regen(write="--write" in sys.argv)
