"""Astra reproduction from the supplied reference and reviewed baseline context.
Not a blind benchmark. Explicit numerical dimensions take precedence over SVG proportions.
Blender 5.2; no network, downloaded geometry, or generation API.
"""
import bpy, math, json, sys
from pathlib import Path
from mathutils import Vector

ROOT = Path(__file__).resolve().parents[1]

def material(name, rgb, metal=0, rough=.4):
    m = bpy.data.materials.new(name)
    m.diffuse_color = (*rgb, 1)
    m.use_nodes = True
    p = m.node_tree.nodes['Principled BSDF']
    p.inputs['Base Color'].default_value = (*rgb,1)
    p.inputs['Metallic'].default_value = metal
    p.inputs['Roughness'].default_value = rough
    return m

def part(obj, name, mat):
    obj.name = name
    obj.data.name = name+'_Mesh'
    obj.data.materials.append(mat)
    obj['rover_part'] = True
    obj['source_reference'] = 'reference_rover_three_view.svg'
    return obj

def box(name, xyz, dims, mat, bevel=.025):
    bpy.ops.mesh.primitive_cube_add(location=xyz)
    o=bpy.context.object
    o.dimensions=dims
    bpy.ops.object.transform_apply(location=False,rotation=False,scale=True)
    b=o.modifiers.new('Editable bevel','BEVEL'); b.width=bevel; b.segments=3
    return part(o,name,mat)

def cyl(name, xyz, radius, depth, mat, axis='Z'):
    rotation={'Z':(0,0,0),'Y':(math.pi/2,0,0),'X':(0,math.pi/2,0)}[axis]
    bpy.ops.mesh.primitive_cylinder_add(vertices=48,radius=radius,depth=depth,location=xyz,rotation=rotation)
    o=part(bpy.context.object,name,mat)
    b=o.modifiers.new('Editable bevel','BEVEL');b.width=.008;b.segments=3
    return o

def aim(o, target):
    o.rotation_euler=(Vector(target)-o.location).to_track_quat('-Z','Y').to_euler()

def save_export_render(variant):
    s=bpy.context.scene
    s['variant']=variant
    cams=[o for o in s.objects if o.type=='CAMERA']
    s.camera=bpy.data.objects['Camera_hero']
    for area in (a for screen in bpy.data.screens for a in screen.areas if a.type=='VIEW_3D'):
        area.spaces.active.region_3d.view_perspective='CAMERA'
    bpy.ops.object.select_all(action='DESELECT')
    body=bpy.data.objects['Body_Main'];body.select_set(True);bpy.context.view_layer.objects.active=body
    bpy.ops.wm.save_as_mainfile(filepath=str(ROOT/f'fieldnote_r1_{variant}.blend'))
    bpy.ops.object.select_all(action='DESELECT')
    for o in s.objects:
        if o.get('rover_part'):o.select_set(True)
    bpy.ops.export_scene.gltf(filepath=str(ROOT/f'fieldnote_r1_{variant}.glb'),export_format='GLB',use_selection=True,export_apply=True)
    for camera in cams:
        s.camera=camera
        view=camera.name.removeprefix('Camera_')
        s.render.filepath=str(ROOT/f'astra_{variant}_{view}.png')
        bpy.ops.render.render(write_still=True)
    s.camera=bpy.data.objects['Camera_hero']

def build():
    settings=json.loads((ROOT/'evidence/run-settings.json').read_text())['current_run']
    assert settings['payload']['model']=='gpt-6-astra'
    bpy.ops.wm.read_factory_settings(use_empty=True)
    s=bpy.context.scene
    s.unit_settings.system='METRIC';s.unit_settings.scale_length=1
    graphite=material('Body_Graphite',(.055,.095,.13),.3)
    teal=material('Accessory_Teal',(.02,.38,.40),.25)
    roof=material('Roof_Teal',(.02,.38,.40),.25)
    orange=material('Bumper_Orange',(.88,.17,.035),.15)
    rubber=material('Rubber',(.015,.019,.023),0,.7)
    metal=material('Aluminium',(.38,.46,.5),.75,.25)
    lens=material('Lens',(.045,.65,.8),.35,.15)
    # Envelope: -0.8..0.8 X, +-0.525 Y. Ground-to-body-top 0.70 m.
    box('Body_Main',(-.06,0,.54),(1.48,1.05,.32),graphite,.085)
    box('Front_Bumper',(.74,0,.52),(.12,.98,.24),orange,.025)
    box('Roof_Panel',(-.07,0,.7275),(.78,.56,.055),roof)
    for i in range(4):
        box(f'Roof_Cell_{i+1:02}',(-.355+.19*i,0,.764),(.165,.51,.018),roof,.007)
    for xname,x in [('Front',.49),('Rear',-.49)]:
        for yname,y in [('Left',.61),('Right',-.61)]:
            cyl(f'Wheel_{xname}_{yname}',(x,y,.28),.28,.18,rubber,'Y')
            cyl(f'Hub_{xname}_{yname}',(x,y+math.copysign(.09,y),.28),.105,.025,metal,'Y')
            cyl(f'Axle_{xname}_{yname}',(x,y*.79,.28),.05,.3,metal,'Y')
    for label,y in [('L',.19),('R',-.19)]:
        cyl('Front_Camera_'+label,(.815,y,.53),.055,.03,lens,'X')
    cyl('Mast_Column',(.15,0,.944),.035,.36,metal)
    box('Mast_Head',(.15,0,1.14),(.26,.18,.14),teal,.025)
    cyl('Mast_Lens',(.15,-.1,1.14),.047,.035,lens,'Y')
    box('Sample_Pod',(-.15,.585,.55),(.46,.16,.20),teal,.025)
    cyl('Sample_Pod_Cap',(.085,.585,.55),.065,.025,metal,'X')
    # Antenna bottom touches the chassis, unlike the floating baseline antenna.
    cyl('Antenna',(-.50,0,.85),.015,.30,metal)
    cyl('Status_Light',(-.50,0,1.015),.035,.04,lens)
    floor=box('Studio_Ground',(0,0,-.03),(200,200,.06),material('Ground',(.045,.058,.07)),0)
    del floor['rover_part']
    world=bpy.data.worlds.new('Studio');s.world=world;world.use_nodes=True
    world.node_tree.nodes['Background'].inputs[0].default_value=(.08,.11,.15,1)
    world.node_tree.nodes['Background'].inputs[1].default_value=.35
    for name,xyz,power,size,color in [('Key',(3,-4,5),1000,4,(1,.82,.7)),('Fill',(-3,-2,3),750,3,(.55,.75,1)),('Rim',(-1,4,4),1100,3,(.5,.9,1))]:
        data=bpy.data.lights.new(name,'AREA');data.energy=power;data.shape='DISK';data.size=size;data.color=color
        o=bpy.data.objects.new(name,data);s.collection.objects.link(o);o.location=xyz;aim(o,(0,0,.6))
    views={'hero':((3.35,4.15,2.55),False),'front':((5,0,.75),True),'side':((0,5,.75),True),'rear':((-5,0,.75),True),'top':((0,0,6),True)}
    for name,(xyz,ortho) in views.items():
        data=bpy.data.cameras.new('Camera_'+name);o=bpy.data.objects.new('Camera_'+name,data);s.collection.objects.link(o)
        o.location=xyz;aim(o,(0,0,.75));data.lens=58
        if ortho:data.type='ORTHO';data.ortho_scale=2.65
        if name=='top':o.rotation_euler=(0,0,0)
    s.render.engine='BLENDER_EEVEE';s.render.resolution_x=1000;s.render.resolution_y=800;s.render.resolution_percentage=100
    s.render.image_settings.file_format='PNG';s.view_settings.look='AgX - Medium High Contrast'
    s['model_identifier']=settings['payload']['model'];s['run_turn_id']=settings['payload']['turn_id']
    s['attribution_status']='Verified Astra continuation; see evidence/run-settings.json'
    s['source_reference']='reference_rover_three_view.svg'
    s['method']='New scene script in Astra continuation with prior baseline context; not blind image-to-3D'
    save_export_render('base')

if __name__=='__main__': build()
