"""Test suite esaustivo su Test/*.png con varie configurazioni.

Esegue matrix (immagine, ROI completa/parziale, config) e stampa tempi/match.
"""
from __future__ import annotations

import time
from pathlib import Path

import cv2
import numpy as np

from pm2d import LineShapeMatcher
from pm2d.gui import draw_matches


TEST_DIR = Path(__file__).parent.parent / "Test"
OUT_DIR = Path("/tmp/pm2d_suite"); OUT_DIR.mkdir(exist_ok=True)

# Casi: (nome, immagine, (y0,y1,x0,x1) roi completa, (y0,y1,x0,x1) roi parziale)
CASES = [
    ("clip",   "clip.png",            ( 60, 200,  90, 290), ( 60, 135,  90, 290)),
    ("ruota",  "rings_and_nuts.png",  ( 55, 175,  90, 215), ( 55, 115,  90, 215)),
    ("dado",   "rings_and_nuts.png",  (255, 375,  40, 170), (255, 315,  40, 170)),
    ("lama",   "razors2.png",         ( 90, 370, 120, 160), ( 90, 230, 120, 160)),
]

CONFIGS = [
    ("fast",    dict(angle_step_deg=10.0, scale_range=(1.0, 1.0),
                     pyramid_levels=3, num_features=64)),
    ("preciso", dict(angle_step_deg=5.0,  scale_range=(0.5, 1.1), scale_step=0.05,
                     pyramid_levels=3, num_features=96)),
]


def bench(case_name: str, img_path: str, roi_box: tuple, roi_kind: str,
          cfg_name: str, cfg: dict) -> dict:
    scene = cv2.imread(str(TEST_DIR / img_path))
    if scene is None:
        # cv2.imread ritorna None silenzioso: senza check il crash arriva
        # dopo, sullo slice, con un errore criptico.
        raise FileNotFoundError(
            f"Immagine di test non trovata o non leggibile: {TEST_DIR / img_path}")
    y0, y1, x0, x1 = roi_box
    roi = scene[y0:y1, x0:x1].copy()
    m = LineShapeMatcher(
        angle_range_deg=(0.0, 360.0),
        weak_grad=30, strong_grad=60,
        spread_radius=5, n_threads=4, **cfg,
    )
    t0 = time.time()
    n_var = m.train(roi)
    t_train = time.time() - t0
    # warmup (prima call è JIT compile)
    m.find(scene, min_score=0.55, max_matches=3, refine_angle=False)

    t0 = time.time()
    matches = m.find(
        scene, min_score=0.55, max_matches=25, nms_radius=None,
        refine_angle=True, subpixel=True, verify_threshold=0.4,
    )
    t_find = time.time() - t0

    tag = f"{case_name}_{roi_kind}_{cfg_name}"
    overlay = draw_matches(scene, matches,
                           template_gray=cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY))
    cv2.imwrite(str(OUT_DIR / f"{tag}.png"), overlay)

    return {
        "case": tag,
        "roi": f"{roi.shape[1]}x{roi.shape[0]}",
        "variants": n_var,
        "train_s": t_train,
        "find_s": t_find,
        "n_match": len(matches),
        "score_range": (
            f"{min(x.score for x in matches):.2f}..{max(x.score for x in matches):.2f}"
            if matches else "-"
        ),
    }


def main():
    print(f"{'case':30s} {'roi':>9s}  {'var':>4s}  "
          f"{'train':>6s}  {'find':>6s}  {'n':>3s}  score")
    print("-" * 85)
    total_find = 0.0
    for case_name, img, roi_full, roi_part in CASES:
        for roi_kind, roi_box in [("full", roi_full), ("part", roi_part)]:
            for cfg_name, cfg in CONFIGS:
                r = bench(case_name, img, roi_box, roi_kind, cfg_name, cfg)
                print(f"{r['case']:30s} {r['roi']:>9s}  {r['variants']:>4d}  "
                      f"{r['train_s']:>5.2f}s  {r['find_s']:>5.2f}s  "
                      f"{r['n_match']:>3d}  {r['score_range']}")
                total_find += r["find_s"]
    print("-" * 85)
    print(f"totale find: {total_find:.1f}s  overlay salvati in {OUT_DIR}/")


if __name__ == "__main__":
    main()
