"""Registered scorer for the movement model. For each run, five crowds against the real one: learned (movesim.py),
met (crowdsim.py, the crowd that met on 10 October), socialforce and orca (pedreplay via opp.py) and straight.
Measures as peds_score.py (peds_realism.py unchanged): 1-Wasserstein distance of step speeds, of nearest-neighbour
distances and of path straightness, and |share of frames with a contact - the real share|. Each measure ranks the
five crowds (ties share the mean rank); a crowd's score in a run is its rank sum. A run is won when the learned
crowd's rank sum is lower than both the met crowd's and social force's. A run where any crowd has no output is
not won.

  score_move.py RUNDIR SET...     (a set is a run-name prefix; RUNDIR/conv/<run>.txt real, RUNDIR/sim/<run>-<crowd>.txt)
"""
import json, os, sys

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from peds_realism import load, stats, w1  # noqa: E402

CROWDS = ["learned", "met", "socialforce", "orca", "straight"]


def ranks(vals):
    order = sorted(range(len(vals)), key=lambda i: vals[i]); r = [0.0] * len(vals); i = 0
    while i < len(order):
        j = i
        while j + 1 < len(order) and vals[order[j + 1]] == vals[order[i]]: j += 1
        for k in range(i, j + 1): r[order[k]] = (i + j) / 2 + 1
        i = j + 1
    return r


def score_run(R, r):
    real = stats(load(f"{R}/conv/{r}.txt")); m = {}
    for c in CROWDS:
        s = stats(load(f"{R}/sim/{r}-{c}.txt"))
        m[c] = {"speed": w1(real["speed"], s["speed"]), "nearest": w1(real["nearest"], s["nearest"]),
                "straight": w1(real["straight"], s["straight"]), "contact": abs(s["contact"] - real["contact"])}
    score = dict.fromkeys(CROWDS, 0.0)
    for key in ("speed", "nearest", "straight", "contact"):
        for c, rk in zip(CROWDS, ranks([round(m[c][key], 6) for c in CROWDS])): score[c] += rk
    return {"win": score["learned"] < score["met"] and score["learned"] < score["socialforce"], "rankSum": score,
            "measures": {c: {k: round(v, 4) for k, v in d.items()} for c, d in m.items()}, "contactReal": round(real["contact"], 4)}


if __name__ == "__main__":
    R, sets = sys.argv[1], sys.argv[2:]
    out = {}
    for e in sets:
        runs = sorted(f[:-4] for f in os.listdir(f"{R}/conv") if f.endswith(".txt") and f.startswith(e) and "__" in f)
        detail, failed = {}, []
        for r in runs:
            if all(os.path.exists(f"{R}/sim/{r}-{c}.txt") and os.path.getsize(f"{R}/sim/{r}-{c}.txt") > 0 for c in CROWDS):
                try:
                    detail[r] = score_run(R, r); continue
                except Exception as err:  # an unreadable output counts as no output
                    detail[r] = {"win": False, "error": str(err)[:200]}
            failed.append(r)
        won = sum(1 for v in detail.values() if v["win"])
        out[e] = {"runs": len(runs), "won": won, "failedRuns": failed, "atLeastTwoThirds": 3 * won >= 2 * len(runs) and len(runs) > 0,
                  "learnedBeatsStraight": sum(1 for v in detail.values() if "rankSum" in v and v["rankSum"]["learned"] < v["rankSum"]["straight"]),
                  "learnedBeatsOrca": sum(1 for v in detail.values() if "rankSum" in v and v["rankSum"]["learned"] < v["rankSum"]["orca"]),
                  "runsDetail": detail}
    out["primary_met"] = all(out[e]["atLeastTwoThirds"] for e in sets)
    print(json.dumps(out, indent=1))
