"""Original native orthographic camera study for Blender 5 Eevee
Run: blender --background --python render-isometric-study.py -- /new/output
Authored geometry and camera measurements, no AI inference
"""

import json
import math
import sys
from pathlib import Path

import bpy
from bpy_extras.object_utils import world_to_camera_view
from mathutils import Vector

out = Path(sys.argv[sys.argv.index("--") + 1])
out.mkdir(parents=True, exist_ok=False)
bpy.ops.wm.read_factory_settings(use_empty=True)
s = bpy.context.scene
s.render.engine = "BLENDER_EEVEE"
s.render.resolution_x = 960
s.render.resolution_y = 720
s.render.resolution_percentage = 100
s.render.image_settings.file_format = "PNG"
s.view_settings.view_transform = "AgX"
s.world = bpy.data.worlds.new("Studio")
s.world.color = (0.13, 0.13, 0.13)


def mat(name, color):
    m = bpy.data.materials.new(name)
    m.use_nodes = True
    b = m.node_tree.nodes.get("Principled BSDF")
    b.inputs["Base Color"].default_value = (*color, 1)
    b.inputs["Roughness"].default_value = 0.65
    return m


teal = mat("Teal", (0.025, 0.43, 0.42))
navy = mat("Navy", (0.04, 0.08, 0.14))
cream = mat("Cream", (0.72, 0.66, 0.50))
orange = mat("Orange", (0.85, 0.25, 0.035))


def box(name, loc, dims, material):
    bpy.ops.mesh.primitive_cube_add(size=1, location=loc)
    o = bpy.context.object
    o.name = name
    o.dimensions = dims
    bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
    o.data.materials.append(material)
    return o


box("Original stage", (0, 0, -0.12), (5, 4, 0.24), navy)
box("Desk top", (0, 0, 1.35), (2.8, 1.5, 0.16), cream)
for x in [-1.15, 1.15]:
    for y in [-0.5, 0.5]:
        box("Desk leg", (x, y, 0.65), (0.13, 0.13, 1.3), teal)
box("Display body", (-0.45, -0.1, 1.93), (1.05, 0.11, 0.65), navy)
box("Display screen", (-0.45, -0.035, 1.93), (0.91, 0.015, 0.50), teal)
box("Display stem", (-0.45, -0.1, 1.55), (0.10, 0.10, 0.28), navy)
box("Display foot", (-0.45, -0.1, 1.45), (0.5, 0.3, 0.045), navy)
box("Orange product block", (0.75, 0.15, 1.65), (0.55, 0.55, 0.45), orange)
box("Notebook", (0.4, -0.4, 1.46), (0.55, 0.35, 0.045), teal)
for loc, energy, size in [((1, -4, 7), 1100, 5), ((-4, 2, 5), 600, 4)]:
    bpy.ops.object.light_add(type="AREA", location=loc)
    o = bpy.context.object
    o.data.energy = energy
    o.data.size = size
    o.rotation_euler = (-o.location).to_track_quat("-Z", "Y").to_euler()
center = Vector((0, 0, 0.7))
records = {
    "kind": "Original native camera projection study with no AI inference",
    "blender": bpy.app.version_string,
    "resolution": [960, 720],
    "target": list(center),
    "cameras": {},
}


def pixel(c, p):
    v = world_to_camera_view(s, c, Vector(p))
    return [v.x * 960, (1 - v.y) * 720]


def length(a, b):
    return math.dist(a, b)


for name, offset, kind in [
    ("isometric", (6, 6, 6), "ORTHO"),
    ("lower-orthographic", (6, 6, 4), "ORTHO"),
    ("perspective", (6, 6, 6), "PERSP"),
]:
    bpy.ops.object.camera_add(location=center + Vector(offset))
    c = bpy.context.object
    c.name = name
    c.rotation_euler = (center - c.location).to_track_quat("-Z", "Y").to_euler()
    c.data.type = kind
    c.data.ortho_scale = 6.5
    c.data.lens = 48
    s.camera = c
    bpy.context.view_layer.update()
    origin = pixel(c, center)
    ends = [pixel(c, center + Vector(axis)) for axis in [(1, 0, 0), (0, 1, 0), (0, 0, 1)]]
    far = Vector((-1, -1, 0.3))
    near = Vector((1, 1, 1.3))
    dx = Vector((0.5, 0, 0))
    records["cameras"][name] = {
        "type": kind,
        "location": list(c.location),
        "rotationEuler": list(c.rotation_euler),
        "orthographicScale": c.data.ortho_scale,
        "lens": c.data.lens,
        "elevationDegrees": math.degrees(math.atan2(offset[2], math.hypot(offset[0], offset[1]))),
        "axisOriginPixels": origin,
        "axisEndPixels": ends,
        "axisLengthsPixels": [length(origin, e) for e in ends],
        "parallelXSegments": {
            "farWorld": list(far),
            "nearWorld": list(near),
            "worldLength": 0.5,
            "farPixels": length(pixel(c, far), pixel(c, far + dx)),
            "nearPixels": length(pixel(c, near), pixel(c, near + dx)),
        },
    }
    s.render.filepath = str(out / f"{name}.png")
    bpy.ops.render.render(write_still=True)
s.camera = bpy.data.objects["isometric"]
bpy.ops.wm.save_as_mainfile(filepath=str(out / "isometric-camera-study.blend"))
(out / "isometric-camera-measurements.json").write_text(json.dumps(records, indent=2) + "\n")
print(json.dumps(records["cameras"]))
