"""Rear spring front mounting and body-pocket comparisons; fitted geometry unknown."""
from pathlib import Path
import json,math
import cadquery as cq
OUT=Path(__file__).parent;BODY=json.loads((OUT.parent/'body-data.json').read_text());S=BODY['published_reference_dimensions_mm']['wheelbase']/2.4
parts=[];nodes={};T=1.5
FLOOR=BODY['native_cabin_understructure_placement']['estimated_nodes']['left']['floor_y_units']
def V(p):return cq.Vector(*(v*S for v in p))
def box(w,h,d,c):return cq.Workplane('XY').box(w,h,d).translate(c.toTuple()).val()
def ring(ro,ri,l,c,axis):return cq.Solid.makeCylinder(ro,l,c,axis).cut(cq.Solid.makeCylinder(ri,l+2,c-axis,axis))
def prism(points,delta):
 wire=cq.Wire.makePolygon(points+[points[0]]);return cq.Solid.extrudeLinear(wire,[],delta)
def add(name,shape,color=(.30,.33,.28)):
 assert shape.isValid() and shape.Volume()>0,name
 parts.append((name,shape,color))
for side,label in [(1,'left'),(-1,'right')]:
 c=V([side*.48,.33,-.54]);base_y=.27*S;roof_y=.40*S;w=.055*S;outer=w+T;plate=.18*S;X=cq.Vector(1,0,0);Y=cq.Vector(0,1,0)
 origin=cq.Vector(c.x,base_y,c.z)
 roof=box(2*outer,T,2*outer,cq.Vector(c.x,roof_y-T/2,c.z))
 walls=[]
 for direction in [-1,1]:
  walls.append(box(T,roof_y-base_y,2*outer,cq.Vector(c.x+direction*(w+T/2),(roof_y+base_y)/2,c.z)))
  walls.append(box(2*w,roof_y-base_y,T,cq.Vector(c.x,(roof_y+base_y)/2,c.z+direction*(w+T/2))))
 flange=box(plate,3,plate,origin+Y*1.5).cut(box(2*w,7,2*w,origin+Y*1.5))
 base=box(plate,3,plate,origin-Y*1.5);fixings=[];holes=[]
 for dx in [-.075*S,.075*S]:
  for dz in [-.075*S,.075*S]:
   p=origin+cq.Vector(dx,0,dz);holes.append(list(p.toTuple()));tool=cq.Solid.makeCylinder(5,14,p-Y*7,Y)
   flange=flange.cut(tool);base=base.cut(tool)
   fixings.extend([cq.Solid.makeCylinder(4.5,18,p-Y*3,Y),cq.Solid.makeCylinder(8,4,p-Y*7,Y)])
 # Open rear lead-in around the retained leaf pack; not a travel envelope.
 lead_slot=box(.08*S,(.31-.27)*S,.10*S,cq.Vector(c.x,(.31+.27)*S/2,c.z-.10*S))
 flange_slot=box(.08*S,10,.06*S,cq.Vector(c.x,base_y,c.z-.075*S))
 walls=[wall.cut(lead_slot) for wall in walls];flange=flange.cut(flange_slot)
 pocket=cq.Compound.makeCompound([roof,*walls,flange]);add('rear_'+label+'_front_spring_body_box_and_lower_flange_reference',pocket)
 # Plate BELOW the eye, as the factory drawing and AHA7174 product photo show.
 bush_half=.031*S;ear_t=3;distance_t=4;ear_offset=bush_half+distance_t+ear_t/2;ears=[]
 for sign in [-1,1]:
  x=c.x+sign*ear_offset-ear_t/2;r=22
  points=[cq.Vector(x,base_y,c.z-r),cq.Vector(x,base_y,c.z+r),cq.Vector(x,c.y,c.z+r)]
  points += [cq.Vector(x,c.y+r*math.sin(i*math.pi/32),c.z+r*math.cos(i*math.pi/32)) for i in range(1,33)]
  ear=prism(points,cq.Vector(ear_t,0,0)).cut(cq.Solid.makeCylinder(10.4,ear_t+4,cq.Vector(x-2,c.y,c.z),X));ears.append(ear)
 bracket=cq.Compound.makeCompound([base,*ears]);
 lead_gauge=box(.06*S,(.305-.272)*S,.12*S,cq.Vector(c.x,(.305+.272)*S/2,c.z-.06*S))
 assert pocket.intersect(lead_gauge).Volume()<1e-5
 assert bracket.intersect(lead_gauge).Volume()<1e-5
 add('rear_'+label+'_front_spring_lower_plate_and_bored_ears_comparison',bracket)
 add('rear_'+label+'_four_front_spring_body_fixing_envelopes',cq.Compound.makeCompound(fixings),(.47,.48,.43))
 special_t=2;underhead=-(ear_offset+ear_t/2+special_t);shoulder_end=-underhead
 pin=cq.Solid.makeCylinder(16,5,c+cq.Vector(underhead-5,0,0),X).fuse(cq.Solid.makeCylinder(.012*S,shoulder_end-underhead,c+cq.Vector(underhead,0,0),X)).fuse(cq.Solid.makeCylinder(8,12,c+cq.Vector(shoulder_end,0,0),X))
 distance=[ring(18,10.4,distance_t,c+cq.Vector(a,0,0),X) for a in [-bush_half-distance_t,bush_half]]
 special=[ring(20,10.4,special_t,c+cq.Vector(a,0,0),X) for a in [underhead,ear_offset+ear_t/2]]
 plane=cq.Plane(origin=c+cq.Vector(shoulder_end,0,0),xDir=(0,1,0),normal=X)
 nut=cq.Workplane(plane).polygon(6,27.7).extrude(9).val().cut(cq.Solid.makeCylinder(8.25,11,c+cq.Vector(shoulder_end-1,0,0),X))
 add('rear_'+label+'_front_eye_pin_distance_and_special_washer_references',cq.Compound.makeCompound([pin,*distance,*special,nut]),(.47,.48,.43))
 for h in holes:
  p=cq.Vector(*h);assert pocket.intersect(cq.Solid.makeCylinder(4.8,14,p-Y*7,Y)).Volume()<1e-5
  assert bracket.intersect(cq.Solid.makeCylinder(4.8,14,p-Y*7,Y)).Volume()<1e-5
 assert bracket.intersect(cq.Solid.makeCylinder(10.2,100,c-X*50,X)).Volume()<1e-5
 nodes[label]=dict(eye_axis_center_units=[side*.48,.33,-.54],eye_axis=[1,0,0],floor_y_units=FLOOR,plate_top_y_units=.27,estimated_rear_leaf_entry_width_units=.08,estimated_rear_leaf_entry_top_y_units=.31,static_leaf_lead_in_gauge_y_units=[.272,.305],body_box_roof_y_units=.40,body_box_inside_half_width_units=.055,body_box_sheet_gauge_mm=T,floor_pocket_half_width_units=outer/S,body_flange_half_width_units=.09,body_fixing_centers_mm=holes,body_fixing_bore_radius_mm=5,ear_center_x_offsets_mm=[-ear_offset,ear_offset],ear_thickness_mm=3,ear_bore_radius_mm=10.4,retained_bush_half_length_mm=bush_half,retained_pin_shoulder_radius_mm=.012*S,estimated_distance_washer_thickness_mm=4,estimated_special_washer_thickness_mm=2)
assembly=cq.Assembly(name='estimated_rear_front_spring_hanger_relationships');meshes=[]
for name,shape,color in parts:
 assembly.add(shape,name=name,color=cq.Color(*color));vs,ts=shape.tessellate(.6,.15)
 meshes.append(dict(name=name,color=color,vertices=[round(v,4) for p in vs for v in p.toTuple()],triangles=[i for t in ts for i in t]))
assembly.export(str(OUT/'rear-spring-hanger-reference.step'))
limits='No direct owner dimensions or fitted part identity are applied. Factory/supplier AHA7174 plate-below-eye topology corrects the old inverted body-pad arrangement, but every section, profile, gauge, hole pattern, mounting span, eye/body station, box height, washer/retainer and running allowance is estimated. Existing eye axis and front bush section are retained drawing references. Body-box architecture is a comparison, not a fitted AHA7210 identification or cross-reference to earlier repair panels. The open-rear leaf entry and lowered .27-unit plate top accommodate the retained static leaf lead-in reference; neither is a fitted factory contour or travel allowance. The floor opening follows the estimated box footprint, not a discovered factory hole or drilling/cutting template. Four body fixing envelopes do not establish screw lengths, captive-nut arrangement, thread fit or actual loads. Ear threading, bolt thread, compression, spring camber/preload, articulation, body/suspension strength, loaded contact, corrosion/repair condition and safe support/release sequence remain unresolved. Source separation is illustrative, without a verified spring/bracket removal path or safe lifting point.'
meta=dict(units='mm',source_frame='Canonical body +X vehicle-left, +Y up, +Z front, mm.',factory_source='https://www.spridgets.de/downloads/Parts-1977.pdf#page=150',supplier_source='https://rimmerbros.com/Item--i-AHA7174',body_reinforcement_source='https://www.bmh-ltd.com/vehicle-parts/mg-midget-parts/mg-midget-front-centre-body/',earlier_repair_comparison='https://www.ahspares.co.uk/austin-healey/sprite/rear-spring-hanger/spring-hanger-reinforcement-box.aspx',owner_dimensions_applied=False,basis='Factory p150 separates AHA7174 front mounting, AHA7180 eye bolt, AHA7178 distance/AHA7179 special washers and later/earlier fixing branches. Rimmer AHA7174 product photo corroborates lower plate/upward ears and lists Midget 1500. Manufacturer separates spring-hanger reinforcement from detachable bracket. Earlier repair-box application is not imposed as a fitted 1500 box.',nodes=nodes,limits=limits,parts=meshes)
(OUT/'rear-spring-hanger-reference.json').write_text(json.dumps(meta,separators=(',',':'))+'\n')
restored=cq.importers.importStep(str(OUT/'rear-spring-hanger-reference.step')).val();assert restored.isValid()
print(json.dumps(dict(groups=len(parts),solids=len(restored.Solids()),step_valid=True,eight_shared_body_fixing_bores=True,front_eye_ear_passages=True,plate_below_eye=True,static_leaf_lead_in_open=True)))
