#!/usr/bin/env python3
"""Independent NumPy pupil/focal-grid refinement, retaining absolute transmission differences."""
import argparse
import json
from pathlib import Path
import numpy as np
from synthetic_optics import pupil_fields, finite_band


def compare(first, second):
    return {"relative_l1": float(np.sum(np.abs(first - second)) / np.sum(second)),
            "absolute_l1_entrance_fraction": float(np.sum(np.abs(first - second))),
            "absolute_transmission_difference": float(abs(first.sum() - second.sum())),
            "first_camera_transmission": float(first.sum()), "reference_camera_transmission": float(second.sum())}


def full_corner_check(metadata, output):
    rows = []
    for separation in (2., 2.7, 5., 7.):
        for degrees in (0., 45., 90.):
            for wavelength in (1.14, 1.24, 1.34):
                for static in (0., 40.):
                    settings = {**metadata["settings"], "staticOpdNm": static, "evolvingOpdNm": 20}
                    source = (separation * np.cos(np.deg2rad(degrees)), separation * np.sin(np.deg2rad(degrees)))
                    for exposure in (0, 4, 9, 13):
                        three = finite_band(settings, wavelength, source, exposure, nodes=3)
                        five = finite_band(settings, wavelength, source, exposure, nodes=5)
                        coarse = pupil_fields(settings, wavelength, source, exposure, size=128, diameter=32)
                        pupil = pupil_fields(settings, wavelength, source, exposure, size=256, diameter=64)
                        focal = pupil_fields(settings, wavelength, source, exposure, size=256, diameter=32)
                        rows.append({"separation": separation, "angle_degrees": degrees, "wavelength": wavelength, "static_opd_nm": static, "exposure": exposure,
                                     "band3vs5": compare(three, five), "pupil32vs64": compare(coarse, pupil), "focalQuarterVsEighth": compare(coarse, focal)})
    result = {"implementation": "Independent NumPy FFT and split trigonometric Gauss circle/cell quadrature", "rows": rows, "cases": len(rows)}
    output.write_text(json.dumps(result, indent=2) + "\n")
    print("PASS independent full corner calculations", len(rows), flush=True)


def validate(materials, output):
    metadata = json.loads((materials / "synthetic-input.json").read_text())
    settings = metadata["settings"]
    rows = []
    for separation in (2., 2.7, 5., 7.):
        for wavelength in (1.14, 1.34):
            angle = np.pi / 4
            source = (separation * np.cos(angle), separation * np.sin(angle))
            coarse = pupil_fields(settings, wavelength, source, 9, size=128, diameter=32)
            medium = pupil_fields(settings, wavelength, source, 9, size=256, diameter=64)
            fine = pupil_fields(settings, wavelength, source, 9, size=512, diameter=64)
            joint = pupil_fields(settings, wavelength, source, 9, size=1024, diameter=128)
            rows.append({"separation": separation, "wavelength": wavelength, "pupil32_to64_same_quarter_sampling": compare(coarse, medium),
                         "quarter_to_eighth_same_pupil64": compare(medium, fine), "current_to_joint_fine": compare(coarse, joint),
                         "fine64_to_joint128_same_eighth_sampling": compare(fine, joint)})
    result = {"independent_implementation": "NumPy FFT plus split Gauss integration of circle/cell area; no TS primitive used",
              "complex_field_approximation": "constant inside each native focal cell; not exact continuous optical power integration",
              "cases": rows, "domain": "All positions support a sampled teaching-camera image. Subpercent statistical intervals are conditional on that discrete optical model. Low transmission below three reference angular units remains strongly grid-sensitive and is not a precision continuum-optics inference."}
    output.write_text(json.dumps(result, indent=2) + "\n")
    print("PASS independent fine-grid calculations", len(rows), flush=True)
    for row in rows:
        print(row["separation"], row["wavelength"], row["current_to_joint_fine"], flush=True)


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--full-corners", action="store_true")
    parser.add_argument("--materials", type=Path, default=Path("."))
    parser.add_argument("--output", type=Path, default=Path("optical-grid-reference.json"))
    args = parser.parse_args()
    if args.full_corners:
        full_corner_check(json.loads((args.materials / "synthetic-input.json").read_text()), args.output)
    else:
        validate(args.materials, args.output)
