"""Fusion-native edits to the imported final 27 R2 assembly with casting-release adaptation. All dimensions below are mm.
Run once in the Autodesk Fusion Python Text Commands console after 01_import_27.py.
Mesh rods are a lightweight purchased-weave representation; see procurement drawing.
"""
import adsk.core, adsk.fusion, json, pathlib, traceback, math
OUT=pathlib.Path(C28_ROOT)
app=adsk.core.Application.get();d=adsk.fusion.Design.cast(app.activeProduct);root=d.rootComponent
tm=adsk.fusion.TemporaryBRepManager.get();BT=adsk.fusion.BooleanTypes
P=adsk.core.Point3D;V=adsk.core.Vector3D
log=[]
def report(s):
    log.append(s);(OUT/'audit/build_progress.json').write_text(json.dumps(log,indent=2))
def pt(x,y,z):return P.create(x/10,y/10,z/10)
def box(x0,x1,y0,y1,z0,z1):
    return tm.createBox(adsk.core.OrientedBoundingBox3D.create(pt((x0+x1)/2,(y0+y1)/2,(z0+z1)/2),V.create(1,0,0),V.create(0,1,0),(x1-x0)/10,(y1-y0)/10,(z1-z0)/10))
def cyl(x,y,r,z0,z1,r1=None):return tm.createCylinderOrCone(pt(x,y,z0),r/10,pt(x,y,z1),(r if r1 is None else r1)/10)
def boolean(a,b,op):
    if not tm.booleanOperation(a,b,op):raise RuntimeError('Fusion boolean failed')
    return a
def join(a,b):return boolean(a,b,BT.UnionBooleanType)
def cut(a,b):return boolean(a,b,BT.DifferenceBooleanType)
def common(a,b):return boolean(a,b,BT.IntersectionBooleanType)
def rr(w,h,r,z0,z1,x=0,y=0):
    a=box(x-w/2+r,x+w/2-r,y-h/2,y+h/2,z0,z1)
    a=join(a,box(x-w/2,x+w/2,y-h/2+r,y+h/2-r,z0,z1))
    for sx in [-1,1]:
        for sy in [-1,1]:a=join(a,cyl(x+sx*(w/2-r),y+sy*(h/2-r),r,z0,z1))
    return a
def rr_ring(w,h,r,wi,hi,ri,z0,z1,x=0,y=0):return cut(rr(w,h,r,z0,z1,x,y),rr(wi,hi,ri,z0-.1,z1+.1,x,y))
def occurrence(term):
    return next(o for o in root.allOccurrences if term in o.component.name)
def worldcopy(o):return tm.copy(o.bRepBodies.item(0))
def replace(o,b,name):
    c=o.component
    inv=o.transform2.copy();inv.invert();tm.transform(b,inv)
    f=c.features.baseFeatures.item(0)
    f.startEdit()
    try:
        if not f.updateBody(f.sourceBodies[0],b):raise RuntimeError('updateBody failed '+name)
    finally:f.finishEdit()
    d.timeline.moveToEnd()
    c.name=name;c.bRepBodies.item(0).name=name
    report('Updated '+name)
    return c
new=[]
def add(name,bodies):
    o=root.occurrences.addNewComponent(adsk.core.Matrix3D.create());c=o.component;c.name=name
    f=c.features.baseFeatures.add();f.name=name+' / Fusion BRep';f.startEdit()
    for i,b in enumerate(bodies):
        bb=c.bRepBodies.add(b,f);bb.name=name+(f'_{i+1:03}' if len(bodies)>1 else '')
    f.finishEdit();d.timeline.moveToEnd();new.append(name);report('Added '+name)
    return c
try:
    shell_o=occurrence('_01_Front_Shell');shell=worldcopy(shell_o)
    # Close the old 121 molded/drilled holes only within the original face skin.
    # Preserve back-face plane and every internal seat, boss and datum.
    shell=join(shell,rr(49,51,12,-2.2,0,y=37.5))
    # Separate mesh is supported by a shallow pocket, with a true acoustic opening.
    shell=cut(shell,rr(48.4,50.4,11.7,-.3,1,y=37.5))
    shell=cut(shell,cyl(0,42,17.5,-2.201,1))
    # Fill old square front recess and opening only forward of the retention tabs.
    shell=join(shell,rr(36,36,9,-2.2,0,y=-47))
    shell=cut(shell,cyl(0,-47,13.95,-2.201,1))
    shell=cut(shell,cyl(0,-47,16.7,-.58,0,17.5))
    # Replace pill-shaped visible aperture, retaining the rear lens flange pocket.
    shell=join(shell,rr(4.6,10.6,2,-2.15,0,y=5))
    shell=cut(shell,cyl(0,5,2.45,-2.151,1))
    shell_c=replace(shell_o,shell,'C28_R1_01_Front_Shell_Chalk_Mesh_Round_Controls')
    # Keep the original actuator contact, lower retention wings and switch travel datum.
    o=occurrence('_03_Front_Action_Rigid');body=worldcopy(o)
    lower=common(tm.copy(body),box(-30,30,-80,-20,-3,-2.3))
    upper=common(body,cyl(0,-47,13.65,-2.31,-.65))
    carrier=join(lower,upper)
    carrier_c=replace(o,carrier,'C28_R1_03_Round_Action_Carrier_Original_Retention')
    # Lens collector remains at its original z=-3.5 interface to the optical coupler.
    o=occurrence('_04_Status_Lens');body=worldcopy(o)
    lens=common(body,box(-5,5,-1,11,-3.6,-2.15))
    lens=join(lens,cyl(0,5,2.35,-2.151,.32))
    lens=join(lens,cyl(0,5,2.35,.32,.42,2.25))
    replace(o,lens,'C28_R1_04_Circular_Status_Lens_Original_Collector')
    # 27 mm brushed-metal cap. Laser marking is artwork, not an open perforation.
    o=occurrence('_10_Front_Action_Pewter_Cap')
    cap=join(cyl(0,-47,13.5,-.55,.4),cyl(0,-47,13.5,.4,.55,13.35))
    cap_c=replace(o,cap,'C28_R1_10_Round_Brushed_Steel_Play_Cap')
    replace(occurrence('_11_Front_Action_Bond_Film'),cyl(0,-47,13.4,-.65,-.55),'C28_R1_11_Round_Play_Bond_Film')
    # Keep all concealed PTT engagement details and use a smooth capsule skin.
    o=occurrence('_16_Orange_Ribbed_PTT_Grip');old=worldcopy(o)
    hidden=common(old,box(-40.10001,-39,-20,44,-22,-2))
    skin=box(-41.9,-40.1,-9,33,-18,-6)
    for yy in [-9,33]:skin=join(skin,tm.createCylinderOrCone(pt(-41.9,yy,-12),.6,pt(-40.1,yy,-12),.6))
    grip=join(hidden,skin)
    grip_c=replace(o,grip,'C28_R1_16_Smooth_Ember_PTT_Grip')
    # Machined/stamped thin stainless bezel: shallow rear rebate captures mesh edge.
    bezel=rr_ring(48,50,11.5,44,46,9.5,-.1,.55,y=37.5)
    bezel=cut(bezel,rr(45.4,47.4,10.2,-.11,.35,y=37.5))
    add('C28_R1_57_Stainless_Grille_Bezel',[bezel])
    add('C28_R1_58_Grille_Perimeter_Bond_Film',[rr_ring(47.8,49.8,11.4,45.6,47.6,10.3,-.3,-.1,y=37.5)])
    add('C28_R1_59_Mesh_Edge_Bond_Film',[rr_ring(45,47,10,44,46,9.5,.26,.35,y=37.5)])
    add('C28_R1_60_Black_Acoustic_Scrim',[rr(44.8,46.8,9.9,-.3,-.2,y=37.5)])
    # Stock woven wire: 1.0 mm pitch, 0.18 mm nominal wire, 0.36 mm total thickness.
    # Parallel rods in two layers represent the supplier mesh envelope, not tooling.
    clip=rr(45,47,10,-.11,.27,y=37.5);wires=[]
    for x in range(-22,23):
        b=tm.createCylinderOrCone(pt(x,13,-.01),.009,pt(x,62,-.01),.009)
        b=common(b,clip)
        if b and b.volume>0:wires.append(b)
    for k in range(-23,24):
        y=37.5+k
        b=tm.createCylinderOrCone(pt(-23,y,.17),.009,pt(23,y,.17),.009)
        b=common(b,clip)
        if b and b.volume>0:wires.append(b)
    add('C28_R1_61_Graphite_Woven_Mesh_REFERENCE_RODS',wires)
    # Appearances are design intent; resin specifications are in handoff documentation.
    lib=next(a for a in app.materialLibraries if a.name=='Fusion Appearance Library')
    def appearance(name,source,rgb=None):
        src=lib.appearances.itemByName(source)
        a=d.appearances.addByCopy(src,name)
        if rgb:
            for p in a.appearanceProperties:
                if p.objectType=='adsk::core::ColorProperty' and ('color' in p.id.lower()):
                    try:p.value=adsk.core.Color.create(*rgb,255)
                    except:pass
        return a
    chalk=appearance('28 Chalk / cast urethane target','Plastic - Matte (White)',(224,221,209))
    grey=appearance('28 Warm grey elastomer target','Plastic - Matte (Gray)',(142,138,127))
    ember=appearance('28 Ember orange elastomer target','Plastic - Matte (Red)',(205,83,29))
    steel=appearance('28 Brushed stainless visual','Stainless Steel - Satin')
    graphite=appearance('28 Graphite woven stainless visual','Plastic - Matte (Black)',(47,48,45))
    lensapp=appearance('28 Diffusing circular lens','Plastic - Translucent Matte (White)',(239,230,209)) if lib.appearances.itemByName('Plastic - Translucent Matte (White)') else chalk
    for c in d.allComponents:
        n=c.name;a=None
        if 'Shell' in n:a=chalk
        elif 'Elastomer_Rim' in n:a=grey
        elif 'Smooth_Ember' in n:a=ember
        elif any(t in n for t in ['Steel_Play_Cap','Stainless_Grille','Volume_Up_Pewter','Volume_Down_Pewter']):a=steel
        elif any(t in n for t in ['Woven_Mesh','Acoustic_Scrim']):a=graphite
        elif 'Status_Lens'in n:a=lensapp
        if a:
            for b in c.bRepBodies:b.appearance=a
    d.timeline.moveToEnd();app.activeViewport.fit()
    (OUT/'audit/build_done.txt').write_text('Fusion native BRep update completed. Export and independent verification pending.\n')
    report('Native geometry complete, pending verification')
    print('Version 28 geometry built in Fusion. Export and verification next.')
except:
    (OUT/'audit/build_error.txt').write_text(traceback.format_exc());print(traceback.format_exc())
