"""GAN6 rear bumper topology; estimated station, sections and body interfaces."""
from pathlib import Path
import json
import cadquery as cq
OUT=Path(__file__).parent
S=json.loads((OUT.parent/'body-data.json').read_text())['published_reference_dimensions_mm']['wheelbase']/2.4
parts=[]
def V(p):return cq.Vector(*(v*S for v in p))
def box(w,h,d,p):return cq.Workplane('XY').box(w*S,h*S,d*S).translate(V(p).toTuple()).val()
def cyl(r,l,p,axis=(0,0,1)):return cq.Solid.makeCylinder(r*S,l*S,V(p),cq.Vector(*axis))
def ring(ro,ri,l,p):return cyl(ro,l,p).cut(cyl(ri,l+.002,[p[0],p[1],p[2]-.001]))
def rounded(w,h,d,p,r):return cq.Workplane(obj=box(w,h,d,p)).edges('|Z').fillet(r*S).val()
def add(name,shape,color=(.33,.34,.30)):
 assert shape.isValid() and shape.Volume()>0,name
 parts.append((name,shape,color))
t=8/S;y=.52;z=-2.18
profiles=[(1.61,.21,.20,[0,y,z],.07),(.25,.23,.38,[-.70,y,-2.055],.07),(.25,.23,.38,[.70,y,-2.055],.07)]
outer=None;cavity=None
for w,h,d,p,r in profiles:
 o=rounded(w,h,d,p,r);q=[p[0],p[1],p[2]+t];inside=rounded(w-2*t,h-2*t,d,q,max(.005,r-t))
 outer=o if outer is None else outer.fuse(o);cavity=inside if cavity is None else cavity.fuse(inside)
moulding=outer.cut(cavity)
add('rear_bumper_forward_open_moulding_reference',moulding,(.08,.09,.08))
web=box(1.47,.174,.005,[0,y,-2.215]);top=box(1.47,.005,.10,[0,.6075,-2.1675]);bottom=box(1.47,.005,.10,[0,.4325,-2.1675]);mounts=[]
body_holes=[[x,cy,-2.02] for x in [-.43,.43] for cy in [.49,.55]]
for x in [-.43,.43]:
 plate=box(.10,.18,.006,[x,y,-2.115])
 for cy in [.49,.55]:plate=plate.cut(cyl(.0055,.01,[x,cy,-2.121]))
 mounts.append(plate)
add('rear_bumper_open_armature_and_mount_faces_reference',cq.Compound.makeCompound([web,top,bottom,*mounts]))
for key,cy in [('upper',.612),('lower',.428)]:add('rear_bumper_'+key+'_clamping_strip_reference',box(1.45,.004,.045,[0,cy,-2.16]))
rivets=[]
for cy,sign in [(.612,1),(.428,-1)]:
 for i in range(14):
  x=-.69+i*1.38/13
  rivets.extend([cyl(.0025,.013,[x,cy,-2.16],(0,sign,0)),cyl(.005,.002,[x,cy+sign*.012,-2.16],(0,sign,0))])
add('rear_bumper_28_rivet_layout_comparison',cq.Compound.makeCompound(rivets),(.49,.50,.44))
for x,label in [(.43,'left'),(-.43,'right')]:
 fixings=[]
 for cy in [.49,.55]:
  fixings.extend([cyl(.005,.13,[x,cy,-2.115]),ring(.012,.0055,.003,[x,cy,-2.015]),ring(.009,.0055,.010,[x,cy,-2.010])])
 add('rear_bumper_'+label+'_body_stud_washer_nut_reference',cq.Compound.makeCompound(fixings),(.47,.48,.42))
assembly=cq.Assembly(name='gan6_rear_bumper_architecture_reference');meshes=[]
for name,shape,color in parts:
 assembly.add(shape,name=name,color=cq.Color(*color));vs,ts=shape.tessellate(.8,.2)
 meshes.append(dict(name=name,color=color,vertices=[round(v,4) for p in vs for v in p.toTuple()],triangles=[i for tri in ts for i in tri]))
assembly.export(str(OUT/'rear-bumper-reference.step'))
limits='All contours, moulding thickness, sections, rivet spacing, mounts, fasteners, apertures and body poses are estimates. BHM539/CHA244/CHA536 comparisons do not identify fitted parts. The 28-rivet and four-body-fixing counts are catalog topology, not an owner hardware count. The new source center Z=-2.18 body units is a provisional assembly station, not measured stand-off, overall length or verified body clearance. Existing rear panel/tank/suspension frames stay fixed and remain unregistered. No front spring/support arrangement, pre-GAN6 reinforcement or except-North-America number-plate bracket is imposed. No strength, impact, towing/lifting, clamp preload, rivet removal/reuse, safe support or verified replacement/removal path. Inspection separation is illustrative.'
meta=dict(units='mm',factory_sources=['https://www.spridgets.de/downloads/Parts-1977.pdf#page=269','https://www.spridgets.de/downloads/Parts-1977.pdf#page=271'],owner_photos=['IMG_6329','IMG_6341'],basis='Factory GAN6 BHM539 rear moulding, CHA244 then CHA536 armature from GAN6-158716, CHA252/CHA253 clamping strips, 28 CHA307 rivets and four body nuts. Owner photos support visible rear rubber-bumper presence and wrap context only; they do not survey the hidden armature or mount pattern. Old solid drawing is replaced; the rear body panel is not moved.',limits=limits,body_holes_units=body_holes,body_hole_radius_units=.006,estimated_main_moulding_center_units=[0,y,z],parts=meshes)
(OUT/'rear-bumper-reference.json').write_text(json.dumps(meta,separators=(',',':'))+'\n')
restored=cq.importers.importStep(str(OUT/'rear-bumper-reference.step')).val();assert restored.isValid()
# Verify actual open mouth and a solid rear face; absence cannot pass both.
assert moulding.intersect(cyl(.001,.05,[0,y,-2.12])).Volume()<1e-6
assert moulding.intersect(cyl(.001,.03,[0,y,-2.29])).Volume()>0
print(json.dumps(dict(groups=len(parts),solids=len(restored.Solids()),step_valid=True,moulding_forward_open=True,rear_face_present=True,catalog_rivets=28,catalog_body_fixings=4)))
