"""Prepare and locally serve a browser-based Three.js viewer for the AC28 PLY."""

from __future__ import annotations

import argparse
import os
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path

import numpy as np

from moltex_experiments.matching_data import read_ply_header


WORKSPACE = Path("/workspace")
SOURCE = WORKSPACE / (
    "moltex-sample/PLY File (측정 데이터)/"
    "AC28_DT RR UPR STD 1_20260827 18시01분 생산_OK/all_merge_pointcloud_full.ply"
)
SAMPLE = WORKSPACE / "moltex-experiments/artifacts/ac28_ok_10k_sample.ply"
HIGH_DETAIL_SAMPLE = WORKSPACE / "moltex-experiments/artifacts/ac28_ok_100k_sample.ply"
POINT_DTYPE = np.dtype(
    [("x", "<f4"), ("y", "<f4"), ("z", "<f4"), ("r", "u1"), ("g", "u1"), ("b", "u1")]
)


def prepare_sample(point_count: int = 10_000) -> Path:
    """Write an evenly sampled binary PLY suitable for interactive WebGL."""
    if not SOURCE.is_file():
        raise FileNotFoundError(SOURCE)
    header = read_ply_header(SOURCE)
    count = min(point_count, header["vertex_count"])
    output = SAMPLE if point_count <= 10_000 else HIGH_DETAIL_SAMPLE
    output.parent.mkdir(parents=True, exist_ok=True)

    source_points = np.memmap(
        SOURCE,
        dtype=POINT_DTYPE,
        mode="r",
        offset=header["header_bytes"],
        shape=(header["vertex_count"],),
    )
    indices = np.linspace(0, header["vertex_count"] - 1, count, dtype=np.int64)
    sample = source_points[indices].copy()
    del source_points

    target_header = (
        "ply\n"
        "format binary_little_endian 1.0\n"
        f"element vertex {count}\n"
        "property float x\nproperty float y\nproperty float z\n"
        "property uchar red\nproperty uchar green\nproperty uchar blue\n"
        "end_header\n"
    ).encode("ascii")
    with output.open("wb") as handle:
        handle.write(target_header)
        handle.write(sample.tobytes())
    return output


def serve(port: int, high_detail_sample: Path) -> None:
    """Serve /workspace on loopback, making the viewer and NAS copy same-origin."""
    handler = lambda *args, **kwargs: SimpleHTTPRequestHandler(*args, directory=str(WORKSPACE), **kwargs)
    server = ThreadingHTTPServer(("127.0.0.1", port), handler)
    print(f"Three.js PLY viewer: http://127.0.0.1:{port}/moltex-experiments/viewer/")
    print(f"Default sample: {SAMPLE}")
    print(f"High-detail sample: {high_detail_sample}")
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        pass
    finally:
        server.server_close()


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--points", type=int, default=10_000)
    parser.add_argument("--high-detail-points", type=int, default=100_000)
    parser.add_argument("--port", type=int, default=8000)
    parser.add_argument("--prepare-only", action="store_true")
    args = parser.parse_args()
    sample = prepare_sample(args.points)
    print(f"Prepared {sample} ({sample.stat().st_size / 1024 / 1024:.1f} MiB)")
    high_detail_sample = prepare_sample(args.high_detail_points)
    print(f"Prepared {high_detail_sample} ({high_detail_sample.stat().st_size / 1024 / 1024:.1f} MiB)")
    if not args.prepare_only:
        serve(args.port, high_detail_sample)


if __name__ == "__main__":
    main()
