#!/usr/bin/env python3
"""Reduce the real adr-example outputs and plot final rate versus delivery."""
import csv
import math
from pathlib import Path
import sys

from matplotlib import pyplot as plt


def reference_airtime_ms(sf):
    # Module LoraPhy::GetTimeOnAir formula for a 10-byte reference PHY frame.
    symbol_s = 2**sf / 125000
    low_rate_opt = int(sf >= 11)
    symbols = 8 + max(math.ceil((80 - 4 * sf + 28 + 16) / (4 * (sf - 2 * low_rate_opt))) * 5, 0)
    return round(((8 + 4.25) + symbols) * symbol_s * 1000, 2)


def read_case(case_dir):
    statuses = [line.split() for line in (case_dir / "nodeData.txt").read_text().splitlines()]
    last_time = statuses[-1][0]
    final = [line for line in statuses if line[0] == last_time]
    drs = [int(line[4]) for line in final]
    perf = [line.split() for line in (case_dir / "globalPerformance.txt").read_text().splitlines()]
    sent = sum(int(float(line[1])) for line in perf)
    received = sum(int(float(line[2])) for line in perf)
    sf = [12 - dr for dr in drs]
    return sent, received, round(sum(sf) / len(sf), 2), round(sum(reference_airtime_ms(s) for s in sf) / len(sf), 2), min(sf), max(sf)


def main(result_dir):
    rows = []
    for loss in (0, 10, 20):
        for mode in ("adr", "fixed"):
            sent, delivered, mean_sf, airtime, min_sf, max_sf = read_case(result_dir / f"{mode}-loss{loss}")
            rows.append(dict(loss_db=loss, mode=mode, sent=sent, delivered=delivered,
                             delivery_pct=round(100 * delivered / sent, 1),
                             mean_final_sf=mean_sf, min_final_sf=min_sf, max_final_sf=max_sf,
                             reference_airtime_ms=airtime))
    with (result_dir / "summary.csv").open("w", newline="") as out:
        writer = csv.DictWriter(out, fieldnames=rows[0].keys(), lineterminator="\n")
        writer.writeheader()
        writer.writerows(rows)
    for row in rows:
        print(f"loss={row['loss_db']:2} mode={row['mode']:5} delivered={row['delivered']:3}/{row['sent']:3} ({row['delivery_pct']:5.1f}%) final_SF={row['mean_final_sf']:5.2f} [{row['min_final_sf']}-{row['max_final_sf']}] reference_airtime={row['reference_airtime_ms']:7.2f} ms")
    fig, ax = plt.subplots(figsize=(8, 4.5))
    for mode, marker in (("adr", "o"), ("fixed", "s")):
        subset = [row for row in rows if row["mode"] == mode]
        ax.plot([r["loss_db"] for r in subset], [r["delivery_pct"] for r in subset], marker=marker, label=mode.upper())
    ax.set(xlabel="Maximum synthetic random loss (dB)", ylabel="Delivered uplinks (%)", ylim=(0, 105), xticks=[0, 10, 20])
    ax.grid(alpha=0.3)
    ax.legend()
    fig.tight_layout()
    fig.savefig(result_dir / "delivery.png", dpi=150)


if __name__ == "__main__":
    main(Path(sys.argv[1] if len(sys.argv) > 1 else "results"))
