#!/usr/bin/env python3
"""Render diagnostic views of the original ring as a neutral clay reference."""

from __future__ import annotations

import math
import sys
from pathlib import Path

import bpy
from mathutils import Vector


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


def import_mesh(path: Path) -> bpy.types.Object:
    bpy.ops.import_scene.gltf(filepath=str(path))
    meshes = [obj for obj in bpy.context.scene.objects if obj.type == "MESH"]
    if not meshes:
        raise RuntimeError("No mesh in source")
    bpy.ops.object.select_all(action="DESELECT")
    for obj in meshes:
        obj.select_set(True)
    bpy.context.view_layer.objects.active = meshes[0]
    if len(meshes) > 1:
        bpy.ops.object.join()
    obj = bpy.context.object
    bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
    obj.data.validate(clean_customdata=False)
    for polygon in obj.data.polygons:
        polygon.use_smooth = True
    corners = [obj.matrix_world @ Vector(corner) for corner in obj.bound_box]
    center = sum(corners, Vector()) / 8.0
    obj.location -= center
    bpy.context.view_layer.update()
    return obj


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


def add_area(name: str, location: Vector, energy: float, size: float, target: Vector) -> None:
    data = bpy.data.lights.new(name, "AREA")
    data.energy = energy
    data.shape = "RECTANGLE"
    data.size = size
    data.size_y = size * 2.5
    obj = bpy.data.objects.new(name, data)
    bpy.context.collection.objects.link(obj)
    obj.location = location
    point_at(obj, target)


def material() -> bpy.types.Material:
    mat = bpy.data.materials.new("Neutral diagnostic clay")
    mat.use_nodes = True
    bsdf = mat.node_tree.nodes.get("Principled BSDF")
    bsdf.inputs["Base Color"].default_value = (0.32, 0.42, 0.55, 1.0)
    bsdf.inputs["Metallic"].default_value = 0.0
    bsdf.inputs["Roughness"].default_value = 0.32
    return mat


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)
    clear_scene()
    ring = import_mesh(source)
    ring.data.materials.clear()
    ring.data.materials.append(material())

    scene = bpy.context.scene
    scene.render.engine = "BLENDER_EEVEE"
    scene.render.resolution_x = 900
    scene.render.resolution_y = 900
    scene.render.resolution_percentage = 100
    scene.render.image_settings.file_format = "PNG"
    scene.world.color = (0.035, 0.035, 0.035)
    scene.view_settings.look = "AgX - Medium High Contrast"

    key = Vector((-2.8, -3.4, 4.0))
    fill = Vector((3.5, -1.2, 1.7))
    rim = Vector((0.8, 3.5, 2.8))
    add_area("Key strip", key, 950, 2.2, Vector())
    add_area("Fill strip", fill, 700, 2.8, Vector())
    add_area("Rim strip", rim, 1100, 1.6, Vector())

    camera_data = bpy.data.cameras.new("Diagnostic camera")
    camera_data.lens = 62
    camera = bpy.data.objects.new("Diagnostic camera", camera_data)
    bpy.context.collection.objects.link(camera)
    scene.camera = camera

    views = {
        "hero": Vector((0.0, -4.25, 3.45)),
        "front": Vector((0.0, -5.2, 0.0)),
        "top": Vector((0.0, 0.0, 5.2)),
        "side": Vector((5.2, 0.0, 0.0)),
        "back": Vector((0.0, 5.2, 0.0)),
        "underside": Vector((0.0, -4.25, -3.45)),
    }
    for name, location in views.items():
        camera.location = location
        point_at(camera, Vector((0.0, 0.0, -0.01)))
        scene.render.filepath = str(output / f"reference-{name}.png")
        bpy.ops.render.render(write_still=True)


if __name__ == "__main__":
    main()
