"""hakobu.blend から確認用の絵を書き出す（Cycles・CPU）。
使い方: python3 hakobu_render.py <hakobu.blend> <出力フォルダ> [views...] [--drawer-open 0.28]
views: front side back persp sim sim_open top
"""
import math
import os
import sys

import bpy  # noqa: I001
from mathutils import Vector


def setup(samples=24):
    sc = bpy.context.scene
    sc.render.engine = "CYCLES"
    sc.cycles.device = "CPU"
    sc.cycles.samples = samples
    sc.cycles.use_denoising = True
    try:
        sc.cycles.denoiser = "OPENIMAGEDENOISE"
    except Exception:
        pass
    sc.view_settings.view_transform = "Standard"
    sc.render.film_transparent = False
    world = bpy.data.worlds.new("W")
    sc.world = world
    world.use_nodes = True
    bg = world.node_tree.nodes["Background"]
    bg.inputs["Color"].default_value = (0.93, 0.93, 0.92, 1)
    bg.inputs["Strength"].default_value = 0.9
    sun = bpy.data.lights.new("Sun", "SUN")
    sun.energy = 1.8
    sun.angle = math.radians(25)
    so = bpy.data.objects.new("Sun", sun)
    so.rotation_euler = (math.radians(50), math.radians(-15), math.radians(-35))
    sc.collection.objects.link(so)
    return sc


def floor(sc, size=8, color=(0.80, 0.80, 0.78)):
    me = bpy.data.meshes.new("Floor")
    s = size / 2
    me.from_pydata([(-s, -s, 0), (s, -s, 0), (s, s, 0), (-s, s, 0)], [], [(0, 1, 2, 3)])
    ob = bpy.data.objects.new("Floor", me)
    mat = bpy.data.materials.new("FloorMat")
    mat.use_nodes = True
    mat.node_tree.nodes["Principled BSDF"].inputs["Base Color"].default_value = (*color, 1)
    mat.node_tree.nodes["Principled BSDF"].inputs["Roughness"].default_value = 0.8
    me.materials.append(mat)
    sc.collection.objects.link(ob)
    return ob


def person(sc, h=1.60, x=0.62, y=0.05):
    """大きさの目安の人（身長1.60m）"""
    mat = bpy.data.materials.new("PersonMat")
    mat.use_nodes = True
    mat.node_tree.nodes["Principled BSDF"].inputs["Base Color"].default_value = (0.42, 0.62, 0.78, 1)
    bpy.ops.mesh.primitive_cylinder_add(radius=0.17, depth=h - 0.24, location=(x, y, (h - 0.24) / 2))
    body = bpy.context.active_object
    bpy.ops.mesh.primitive_uv_sphere_add(radius=0.11, location=(x, y, h - 0.11))
    head = bpy.context.active_object
    for o in (body, head):
        o.data.materials.append(mat)
        o.name = "Person"


def camera(sc, loc, target, ortho=None, lens=50):
    cam = bpy.data.cameras.new("Cam")
    if ortho:
        cam.type = "ORTHO"
        cam.ortho_scale = ortho
    else:
        cam.lens = lens
    co = bpy.data.objects.new("Cam", cam)
    co.location = loc
    d = Vector(target) - Vector(loc)
    co.rotation_euler = d.to_track_quat("-Z", "Y").to_euler()
    sc.collection.objects.link(co)
    sc.camera = co
    return co


def main():
    args = sys.argv[1:]
    blend, out = args[0], args[1]
    views = [a for a in args[2:] if not a.startswith("--")]
    bpy.ops.wm.open_mainfile(filepath=blend)
    sc = setup()
    H = max((ob.matrix_world @ Vector(c)).z for ob in bpy.data.objects if ob.type == "MESH" for c in ob.bound_box)
    os.makedirs(out, exist_ok=True)
    fl = None
    for v in views or ["front", "side", "back", "persp"]:
        for ob in list(bpy.data.objects):
            if ob.type == "CAMERA":
                bpy.data.objects.remove(ob)
        for o in bpy.data.objects:
            if o.name.startswith(("Hakobu_Drawer", "Hakobu_Tray")):
                o.location.y = -0.26 if v.endswith("_open") else 0.0
        if v.startswith("sim") and fl is None:
            fl = floor(sc, color=(0.74, 0.66, 0.55))
            person(sc)
        if v == "people" and fl is None:
            fl = floor(sc, color=(0.74, 0.66, 0.55))
            person(sc)
        if fl:
            fl.hide_render = not (v.startswith("sim") or v == "people")
        for o in bpy.data.objects:
            if o.name.startswith("Person"):
                o.hide_render = not (v.startswith("sim") or v == "people")
        m = H * 1.12
        if v == "front":
            camera(sc, (0, -5, H / 2), (0, 0, H / 2), ortho=m)
            sc.render.resolution_x, sc.render.resolution_y = 640, 1000
        elif v == "side":
            camera(sc, (5, 0, H / 2), (0, 0, H / 2), ortho=m)
            sc.render.resolution_x, sc.render.resolution_y = 640, 1000
        elif v == "back":
            camera(sc, (0, 5, H / 2), (0, 0, H / 2), ortho=m)
            sc.render.resolution_x, sc.render.resolution_y = 640, 1000
        elif v == "top":
            camera(sc, (0, 0, 5), (0, 0.0001, 0), ortho=m * 0.7)
            sc.render.resolution_x, sc.render.resolution_y = 800, 800
        elif v == "persp":
            camera(sc, (1.25, -2.3, 1.25), (0, 0, H * 0.48), lens=55)
            sc.render.resolution_x, sc.render.resolution_y = 800, 1000
        elif v == "face":
            camera(sc, (0, -3, H * 0.84), (0, 0, H * 0.84), ortho=0.42)
            sc.render.resolution_x, sc.render.resolution_y = 800, 700
        elif v == "people":
            camera(sc, (1.9, -4.2, 1.35), (0.3, 0, 0.75), lens=50)
            sc.render.resolution_x, sc.render.resolution_y = 1000, 800
        elif v.startswith("sim"):
            # シミュレーションの寄り（左30°・75°見下ろし）に近い角度
            dist = 3.2
            el = math.radians(75)
            az = math.radians(-90 - 30)
            loc = (dist * math.cos(el) * math.cos(az), dist * math.cos(el) * math.sin(az), dist * math.sin(el))
            camera(sc, loc, (0, -0.05, H * 0.35), lens=45)
            sc.render.resolution_x, sc.render.resolution_y = 1000, 800
        sc.render.filepath = os.path.join(out, f"render_{v}.png")
        bpy.ops.render.render(write_still=True)
        print("wrote", sc.render.filepath)


if __name__ == "__main__":
    main()
