#!/usr/bin/env python3
"""Render orthographic binary silhouettes for geometric QA."""

from __future__ import annotations

import argparse
import math
import sys
from pathlib import Path

import bpy
from mathutils import Vector


def parse_args() -> argparse.Namespace:
    argv = sys.argv[sys.argv.index("--") + 1 :] if "--" in sys.argv else []
    parser = argparse.ArgumentParser()
    parser.add_argument("--input", required=True)
    parser.add_argument("--outdir", required=True)
    parser.add_argument("--label", required=True)
    return parser.parse_args(argv)


def point_at(obj: bpy.types.Object, target: Vector) -> None:
    obj.rotation_euler = (target - obj.location).to_track_quat("-Z", "Y").to_euler()


def main() -> None:
    args = parse_args()
    source = Path(args.input).resolve()
    outdir = Path(args.outdir).resolve()
    outdir.mkdir(parents=True, exist_ok=True)
    bpy.ops.object.select_all(action="SELECT")
    bpy.ops.object.delete(use_global=False)
    bpy.ops.import_scene.gltf(filepath=str(source))
    objects = [obj for obj in bpy.context.scene.objects if obj.type == "MESH"]
    bpy.ops.object.select_all(action="DESELECT")
    for obj in objects:
        obj.select_set(True)
    bpy.context.view_layer.objects.active = objects[0]
    if len(objects) > 1:
        bpy.ops.object.join()
    obj = bpy.context.view_layer.objects.active
    bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
    points = [obj.matrix_world @ Vector(corner) for corner in obj.bound_box]
    minimum = Vector(tuple(min(point[i] for point in points) for i in range(3)))
    maximum = Vector(tuple(max(point[i] for point in points) for i in range(3)))
    center = (minimum + maximum) * 0.5
    diagonal = (maximum - minimum).length

    material = bpy.data.materials.new("Silhouette black")
    material.diffuse_color = (0.0, 0.0, 0.0, 1.0)
    material.use_nodes = True
    bsdf = material.node_tree.nodes.get("Principled BSDF")
    bsdf.inputs["Base Color"].default_value = (0.0, 0.0, 0.0, 1.0)
    bsdf.inputs["Roughness"].default_value = 1.0
    obj.data.materials.clear()
    obj.data.materials.append(material)

    scene = bpy.context.scene
    scene.render.engine = "BLENDER_WORKBENCH"
    scene.display.shading.light = "FLAT"
    scene.display.shading.color_type = "MATERIAL"
    scene.display.shading.show_shadows = False
    scene.display.shading.show_cavity = False
    scene.display.shading.show_specular_highlight = False
    scene.display.shading.background_type = "WORLD"
    scene.display.shading.background_color = (1.0, 1.0, 1.0)
    scene.render.resolution_x = 1024
    scene.render.resolution_y = 1024
    scene.render.resolution_percentage = 100
    scene.render.image_settings.file_format = "PNG"
    scene.render.image_settings.color_mode = "RGB"

    camera_data = bpy.data.cameras.new("Mask Camera")
    camera_data.type = "ORTHO"
    camera_data.ortho_scale = diagonal * 1.12
    camera = bpy.data.objects.new("Mask Camera", camera_data)
    bpy.context.collection.objects.link(camera)
    scene.camera = camera
    views = {
        "top": Vector((0.0, 0.0, 3.0)),
        "front": Vector((0.0, -3.0, 0.0)),
        "side": Vector((3.0, 0.0, 0.0)),
        "hero": Vector((0.0, -2.2, 1.8)),
        "back-hero": Vector((0.0, 2.2, 1.8)),
        "oblique": Vector((2.0, -2.0, 1.4)),
    }
    for view, direction in views.items():
        camera.location = center + direction.normalized() * diagonal * 2.0
        point_at(camera, center)
        scene.render.filepath = str(outdir / f"{args.label}-{view}.png")
        bpy.ops.render.render(write_still=True)


if __name__ == "__main__":
    main()
