"""Clout original comparison scenes
Run in a NEW background process only:
blender --background --factory-startup --python finishing-studies.py -- --output-root NEW_DIRECTORY
Writes PNGs and a manifest; never saves a Blender project or edits the foreground application.
"""

import argparse
import json
import sys
from pathlib import Path

import bpy
from mathutils import Vector

if not bpy.app.background:
    raise RuntimeError("Use a new background factory-startup process, not an open project")
p = argparse.ArgumentParser()
p.add_argument("--output-root", required=True)
args = p.parse_args(sys.argv[sys.argv.index("--") + 1 :])
out = Path(args.output_root)
out.mkdir(parents=True, exist_ok=False)


def reset():
    bpy.ops.object.select_all(action="SELECT")
    bpy.ops.object.delete(use_global=False)
    s = bpy.context.scene
    s.render.engine = "CYCLES"
    s.cycles.device = "CPU"
    s.cycles.samples = 48
    s.cycles.use_denoising = True
    s.cycles.seed = 17
    s.render.resolution_x = 900
    s.render.resolution_y = 900
    s.render.resolution_percentage = 100
    s.render.image_settings.file_format = "PNG"
    s.world.color = (0.03, 0.03, 0.03)
    s.view_settings.view_transform = "AgX"
    s.render.film_transparent = False
    return s


def mat(name, color, sss=0, scale=0.12):
    m = bpy.data.materials.new(name)
    m.use_nodes = True
    bs = m.node_tree.nodes.get("Principled BSDF")
    bs.inputs["Base Color"].default_value = (*color, 1)
    bs.inputs["Roughness"].default_value = 0.36
    bs.inputs["Subsurface Weight"].default_value = sss
    bs.inputs["Subsurface Scale"].default_value = scale
    bs.inputs["Subsurface Radius"].default_value = (1, 0.35, 0.15)
    return m


def assign(obj, m):
    obj.data.materials.clear()
    obj.data.materials.append(m)


def camera(pos, target, scale):
    bpy.ops.object.camera_add(location=pos)
    c = bpy.context.object
    c.rotation_euler = (Vector(target) - c.location).to_track_quat("-Z", "Y").to_euler()
    c.data.type = "ORTHO"
    c.data.ortho_scale = scale
    bpy.context.scene.camera = c


def area(pos, target, power, size):
    bpy.ops.object.light_add(type="AREA", location=pos)
    light = bpy.context.object
    light.data.energy = power
    light.data.shape = "DISK"
    light.data.size = size
    light.rotation_euler = (Vector(target) - light.location).to_track_quat("-Z", "Y").to_euler()


def render(name):
    bpy.context.scene.render.filepath = str(out / (name + ".png"))
    bpy.ops.render.render(write_still=True)


# One Suzanne geometry, material and illumination; only the SSS weight changes
s = reset()
camera((2, -7, 1.7), (0, 0, 0.1), 3.8)
area((-2, 3, 2.4), (0, 0, 0), 1100, 2)
area((-3, -4, 2), (0, 0, 0), 95, 3)
bpy.ops.mesh.primitive_monkey_add(location=(0, 0, 0))
obj = bpy.context.object
sub = obj.modifiers.new("Smooth study geometry", "SUBSURF")
sub.levels = 2
sub.render_levels = 2
for poly in obj.data.polygons:
    poly.use_smooth = True
m = mat("Warm wax study", (0.63, 0.34, 0.19), 0, 0.18)
assign(obj, m)
render("sss-surface-only")
m.node_tree.nodes.get("Principled BSDF").inputs["Subsurface Weight"].default_value = 1
render("sss-scattering")
# AO shader to emission, constant geometry and camera, changing only sample distance
s = reset()
camera((4, -6, 4), (0, 0, 0.6), 5.0)
bpy.ops.mesh.primitive_plane_add(size=200, location=(0, 0, 0))
objects = [bpy.context.object]
for pos, scale in [((-0.8, 0.5, 0.6), (0.65, 0.65, 0.6)), ((0.65, 0.45, 0.3), (0.5, 0.65, 0.3))]:
    bpy.ops.mesh.primitive_cube_add(size=2, location=pos)
    o = bpy.context.object
    o.scale = scale
    bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
    be = o.modifiers.new("Small bevel", "BEVEL")
    be.width = 0.07
    be.segments = 3
    objects.append(o)
bpy.ops.mesh.primitive_uv_sphere_add(
    segments=48, ring_count=24, radius=0.48, location=(0.3, -0.65, 0.48)
)
objects.append(bpy.context.object)
for poly in bpy.context.object.data.polygons:
    poly.use_smooth = True
m = bpy.data.materials.new("AO factor visualization")
m.use_nodes = True
n = m.node_tree.nodes
n.clear()
ao = n.new("ShaderNodeAmbientOcclusion")
ao.samples = 16
ao.inputs["Distance"].default_value = 0.12
em = n.new("ShaderNodeEmission")
output = n.new("ShaderNodeOutputMaterial")
m.node_tree.links.new(ao.outputs["AO"], em.inputs["Color"])
m.node_tree.links.new(em.outputs["Emission"], output.inputs["Surface"])
for o in objects:
    assign(o, m)
render("ao-near-distance")
ao.inputs["Distance"].default_value = 0.8
render("ao-wide-distance")
# A separately authored product-style still for deterministic image finishing tools
s = reset()
s.render.resolution_x = 1600
s.render.resolution_y = 900
camera((4, -7, 3), (0, 0, 0.9), 5.5)
area((-3, -4, 5), (0, 0, 0.8), 650, 4)
bpy.ops.mesh.primitive_plane_add(size=200)
assign(bpy.context.object, mat("Charcoal floor", (0.035, 0.06, 0.065)))
bpy.ops.mesh.primitive_cylinder_add(vertices=96, radius=0.65, depth=1.6, location=(0, 0, 0.8))
o = bpy.context.object
bev = o.modifiers.new("Rounded jar edges", "BEVEL")
bev.width = 0.10
bev.segments = 5
assign(o, mat("Teal ceramic", (0.03, 0.21, 0.23)))
for poly in o.data.polygons:
    poly.use_smooth = True
bpy.ops.mesh.primitive_cylinder_add(vertices=96, radius=0.69, depth=0.13, location=(0, 0, 1.63))
o = bpy.context.object
bev = o.modifiers.new("Rounded lid", "BEVEL")
bev.width = 0.035
bev.segments = 3
assign(o, mat("Warm lid", (0.6, 0.36, 0.12)))
m = bpy.data.materials.new("Bright tube")
m.use_nodes = True
bs = m.node_tree.nodes.get("Principled BSDF")
bs.inputs["Emission Color"].default_value = (1, 0.96, 0.8, 1)
bs.inputs["Emission Strength"].default_value = 8
for x in [-1.9, 1.9]:
    bpy.ops.mesh.primitive_cylinder_add(vertices=32, radius=0.05, depth=3.8, location=(x, 1, 1.9))
    assign(bpy.context.object, m)
render("finishing-studio-source")
(out / "render-manifest.json").write_text(
    json.dumps(
        {
            "author": "Clout AI",
            "Blender": bpy.app.version_string,
            "engine": "Cycles CPU",
            "samples": 48,
            "seed": 17,
            "sss": {
                "geometry": "Blender built-in Suzanne with subdivision 2",
                "color": [0.63, 0.34, 0.19],
                "radius": [1, 0.35, 0.15],
                "scale": 0.18,
                "weights": [0, 1],
                "camera_and_lights": "identical",
            },
            "ao": {
                "shader": "Ambient Occlusion factor connected to Emission",
                "samples": 16,
                "distances": [0.12, 0.8],
                "geometry_and_camera": "identical",
            },
            "source": "Separately authored fictional ceramic jar studio scene",
            "meaning": "Original 3D renders, not generated model results, measured real materials or lens/film simulations",
        },
        indent=2,
    )
)
