"""Why an encoding has the parts it has: the tracks (parts="tracked", topology only) and the cuts split_by_fit adds,
with the worst ring miss of one height fit over each track (RMS distance along the ring, canonical units).

    env -u PYTHONPATH PYTHONWARNINGS=ignore .venv/bin/python ~/code/data/meshes/fcp_2710_tests/scripts/why_parts.py dino [--points]
"""
import sys

import numpy as np
import trimesh

sys.path.insert(0, "/home/joao/code/fcp_devel/api")
sys.path.insert(0, str(__import__("pathlib").Path(__file__).parent))
import fcp
from fcp import _parts
from parts_report import OBJ, UP

LOG = []
_orig = _parts.split_by_fit


def logged(parts, coeffs, areas, M, tol=None):
    """split_by_fit, recording per track: its layers, the worst single-fit ring miss, and the pieces it was cut into."""
    tol = _parts.FIT_TOL if tol is None else tol
    for p, C, A in zip(parts, coeffs, areas):
        C = np.asarray(C, float)
        K = C.shape[2]
        w = np.full(K, 0.5); w[0] = 1.0
        Cw = (_parts.continuous_start(C, A) * np.sqrt(w)).reshape(len(C), -1)
        Q, _ = _parts.orth_basis(len(C), M)
        miss = np.sqrt(((Cw - Q @ (Q.T @ Cw)) ** 2).sum(1))
        pp, _, _ = _orig([p], [C], [A], M, tol)
        LOG.append(dict(sign=p["sign"], h0=p["h0"], h1=p["h1"], miss=float(miss.max()), at=p["h0"] + int(miss.argmax()),
                        pieces=[(q["h0"], q["h1"]) for q in pp], tol=tol))
    return _orig(parts, coeffs, areas, M, tol)


_parts.split_by_fit = logged

name = sys.argv[1] if len(sys.argv) > 1 else "dino"
points = "--points" in sys.argv  # encode 200k points sampled on the mesh even when it is watertight
g = trimesh.load(OBJ[name], force="mesh")
geom = g if g.is_watertight and not points else np.asarray(g.sample(200_000, seed=0))
up = UP.get(name, (0, 0, 1))
dt = fcp.encode(geom, device="cuda", up=up, parts="tracked")
d = fcp.encode(geom, device="cuda", up=up)
print(f"{name} ({'mesh' if geom is g else 'points'}): {len(dt.parts)} tracks -> {len(d.parts)} parts after fit cuts "
      f"(fit_tol {LOG[0]['tol']:.4f})")
print(f"{'track':>5s} {'sign':>4s} {'layers':>9s} {'n':>4s} {'worst miss':>10s} {'at':>4s}  pieces")
for i, r in enumerate(LOG):
    cut = len(r["pieces"]) > 1
    print(f"{i:5d} {'+' if r['sign'] > 0 else '-':>4s} {r['h0']:4d}-{r['h1']:<4d} {r['h1'] - r['h0'] + 1:4d} "
          f"{r['miss']:10.4f} {r['at']:4d}  " + (" | ".join(f"{a}-{b}" for a, b in r["pieces"]) if cut else "kept whole"))
