#!/usr/bin/env python3
"""Minimal standalone viser viewer for a single PLY point cloud."""
from __future__ import annotations
import argparse, sys, time
import numpy as np


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("ply", help="PLY file to visualize")
    ap.add_argument("--port", type=int, default=8200)
    ap.add_argument("--point-size", type=float, default=0.01)
    ap.add_argument("--downsample", type=float, default=0.0)
    ap.add_argument("--max-points", type=int, default=2_000_000)
    ap.add_argument("--no-filter", action="store_true", help="Disable outlier removal")
    args = ap.parse_args()

    try:
        import open3d as o3d
        import viser
    except ImportError as e:
        sys.exit(f"missing dep: {e}")

    pcd = o3d.io.read_point_cloud(args.ply)
    n_raw = len(pcd.points)
    print(f"loaded {n_raw:,} pts from {args.ply}", flush=True)

    if args.downsample > 0:
        pcd = pcd.voxel_down_sample(args.downsample)
        print(f"after voxel downsample: {len(pcd.points):,} pts", flush=True)

    if not args.no_filter and len(pcd.points) > 100:
        # Remove statistical outliers (blobs / noise from low-confidence frames)
        pcd, _ = pcd.remove_statistical_outlier(nb_neighbors=20, std_ratio=2.0)
        print(f"after outlier removal: {len(pcd.points):,} pts", flush=True)
        # Second pass tighter for dense clouds
        if len(pcd.points) > 200_000:
            pcd, _ = pcd.remove_statistical_outlier(nb_neighbors=30, std_ratio=1.5)
            print(f"after 2nd pass: {len(pcd.points):,} pts", flush=True)

    pts = np.asarray(pcd.points, dtype=np.float32)
    cols = np.asarray(pcd.colors, dtype=np.float32) if pcd.has_colors() else np.ones_like(pts) * 0.7

    if len(pts) > args.max_points:
        idx = np.random.choice(len(pts), args.max_points, replace=False)
        pts, cols = pts[idx], cols[idx]

    print(f"displaying {len(pts):,} pts", flush=True)
    server = viser.ViserServer(host="0.0.0.0", port=args.port)
    server.scene.add_point_cloud(
        "/cloud", points=pts, colors=(cols * 255).astype(np.uint8), point_size=args.point_size
    )
    print(f"viser listening on *:{args.port}")
    while True:
        time.sleep(60)


if __name__ == "__main__":
    main()
