#!/usr/bin/env python3
"""Reconstruct clean surfaces by volumetric resampling of the generated mesh."""

from __future__ import annotations

import json
import sys
from pathlib import Path

import bpy


CANDIDATES = {
    "fine": {"voxel": 0.0060, "smooth": 0.10, "iterations": 2, "target": 360_000},
    "balanced": {"voxel": 0.0075, "smooth": 0.12, "iterations": 3, "target": 260_000},
}


def clear() -> None:
    bpy.ops.object.select_all(action="SELECT")
    bpy.ops.object.delete(use_global=False)


def export(obj: bpy.types.Object, path: Path) -> None:
    bpy.ops.object.select_all(action="DESELECT")
    obj.select_set(True)
    bpy.context.view_layer.objects.active = obj
    bpy.ops.export_scene.gltf(
        filepath=str(path), export_format="GLB", use_selection=True, export_apply=True,
        export_yup=True, export_normals=True, export_tangents=False, export_texcoords=False,
        export_materials="EXPORT", export_animations=False, export_cameras=False,
        export_lights=False, export_draco_mesh_compression_enable=False,
    )


def main() -> None:
    args = sys.argv[sys.argv.index("--") + 1 :]
    source = Path(args[0]).resolve()
    output = Path(args[1]).resolve()
    output.mkdir(parents=True, exist_ok=True)
    results = {}
    for name, config in CANDIDATES.items():
        clear()
        bpy.ops.import_scene.gltf(filepath=str(source))
        obj = max((candidate for candidate in bpy.context.scene.objects if candidate.type == "MESH"), key=lambda item: len(item.data.vertices))
        bpy.context.view_layer.objects.active = obj
        obj.select_set(True)
        obj.data.remesh_voxel_size = config["voxel"]
        obj.data.remesh_voxel_adaptivity = 0.0
        obj.data.use_remesh_preserve_volume = True
        bpy.ops.object.voxel_remesh()
        for polygon in obj.data.polygons:
            polygon.use_smooth = True

        smooth = obj.modifiers.new(name="Surface fairing", type="LAPLACIANSMOOTH")
        smooth.lambda_factor = config["smooth"]
        smooth.iterations = config["iterations"]
        smooth.use_volume_preserve = True
        smooth.use_normalized = True
        bpy.ops.object.modifier_apply(modifier=smooth.name)

        obj.data.calc_loop_triangles()
        current = len(obj.data.loop_triangles)
        if current > config["target"]:
            decimate = obj.modifiers.new(name="Web optimization", type="DECIMATE")
            decimate.ratio = config["target"] / current
            decimate.use_collapse_triangulate = True
            bpy.ops.object.modifier_apply(modifier=decimate.name)
        obj.data.calc_loop_triangles()
        path = output / f"reconstructed-voxel-{name}.glb"
        export(obj, path)
        results[name] = {
            **config,
            "path": str(path),
            "vertices": len(obj.data.vertices),
            "triangles": len(obj.data.loop_triangles),
            "bytes": path.stat().st_size,
        }
    (output / "voxel-candidates.json").write_text(json.dumps(results, indent=2), encoding="utf-8")
    print("TN_VOXEL_CANDIDATES", json.dumps(results))


if __name__ == "__main__":
    main()
