"""Door architecture and gooseneck-hinge comparison; fitted motion unverified."""
from pathlib import Path
import json
import cadquery as cq
from door_glazing_geometry import build as build_glazing, metadata as glazing_metadata
from front_wing_geometry import absolute_tessellation
from door_regulator_geometry import build as build_regulator,carrier_track,metadata as regulator_metadata
OUT=Path(__file__).parent
S=json.loads((OUT.parent/'body-data.json').read_text())['published_reference_dimensions_mm']['wheelbase']/2.4
parts=[];nodes={}
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=(1,0,0)):return cq.Solid.makeCylinder(r*S,l*S,V(p),cq.Vector(*axis))
def rod(a,b,r):
 a,b=V(a),V(b);d=b-a;return cq.Solid.makeCylinder(r*S,d.Length,a,d.normalized())
def add(name,shape,color=(.34,.35,.31)):
 assert shape.isValid() and shape.Volume()>0,name
 parts.append((name,shape,color))
for side,label in [(1,'left'),(-1,'right')]:
 x=side*.675
 frame=box(.003,.47,.81,[x,.755,-.305])
 for y,z,h,d in [(.77,-.51,.29,.23),(.72,-.18,.25,.25),(.94,-.37,.07,.18)]:frame=frame.cut(box(.012,h,d,[x,y,z]))
 frame=frame.cut(cyl(.01,.018,[x-.009,.78,-.30]))
 add('door_'+label+'_inner_access_frame_comparison',frame)
 trim=box(.003,.43,.76,[side*.663,.76,-.31]).cut(cyl(.012,.014,[side*.663-.007,.78,-.30]))
 add('door_'+label+'_trim_board_envelope_reference',trim,(.43,.23,.12))
 body_hinges=[];door_hinges=[]
 for y in [.62,.91]:
  body_leaf=box(.034,.065,.006,[side*.70,y,.15])
  door_leaf=box(.006,.065,.050,[side*.71,y,.075])
  for dy in [-.018,.018]:
   body_leaf=body_leaf.cut(cyl(.003,.012,[side*.70,y+dy,.144],(0,0,1)))
   door_leaf=door_leaf.cut(cyl(.003,.018,[side*.71-.009,y+dy,.07]))
  # Supplier AHA7428 photo shows a curved arm and forked three-hole base,
  # rather than two disconnected flat leaves. All sections/coordinates below
  # are estimates; retain the old pin and body-receiver axes.
  door_leaf=door_leaf.cut(cyl(.003,.018,[side*.71-.009,y,.090]))
  start=V([side*.70,y,.15]);middle_point=V([side*.70,y,.135]);end=V([side*.72,y,.13])
  path_edge=cq.Edge.makeThreePointArc(start,middle_point,end)
  arm_plane=cq.Plane(origin=start,xDir=cq.Vector(0,1,0),normal=path_edge.tangentAt(0))
  arm=cq.Workplane(arm_plane).rect(.016*S,6).sweep(cq.Workplane(obj=cq.Wire.assembleEdges([path_edge])),isFrenet=True).val()
  middle=cyl(.011,.020,[side*.72,y-.010,.12],(0,1,0))
  bore=cyl(.006,.08,[side*.72,y-.04,.12],(0,1,0))
  body_leaf=body_leaf.fuse(arm,middle).cut(bore).clean()
  ears=[cyl(.011,.010,[side*.72,y+dy-.005,.12],(0,1,0)) for dy in [-.025,.025]]
  necks=[box(.020,.010,.018,[side*.715,y+dy,.106]) for dy in [-.025,.025]]
  door_leaf=door_leaf.fuse(*ears,*necks).cut(bore).clean()
  assert body_leaf.isValid()and door_leaf.isValid()
  assert len(body_leaf.Solids())==len(door_leaf.Solids())==1
  assert body_leaf.intersect(door_leaf).Volume()<1e-5
  body_hinges.extend([body_leaf,cyl(.0055,.065,[side*.72,y-.0325,.12],(0,1,0))])
  door_hinges.append(door_leaf)
 add('door_'+label+'_body_hinge_leaves_reference',cq.Compound.makeCompound(body_hinges))
 add('door_'+label+'_door_hinge_leaves_reference',cq.Compound.makeCompound(door_hinges))
 latch=box(.047,.075,.035,[side*.715,.86,-.705]).cut(box(.022,.035,.025,[side*.715,.86,-.722]))
 add('door_'+label+'_latch_mouth_comparison',latch)
 striker=cq.Compound.makeCompound([box(.06,.08,.006,[side*.715,.86,-.748]),cyl(.008,.035,[side*.715-.0175,.86,-.725])])
 add('door_'+label+'_body_striker_comparison',striker)
 channel=box(.026,.025,.60,[side*.714,1.0075,-.42]).cut(box(.016,.025,.604,[side*.714,1.015,-.42]))
 guide=box(.026,.43,.025,[side*.714,.825,-.735]).cut(box(.016,.434,.024,[side*.714,.825,-.730]))
 add('door_'+label+'_glass_carrier_and_rear_guide_comparison',cq.Compound.makeCompound([channel,guide,carrier_track(side)]))
 # Factory/donor parallel-arm architecture replaces the generic crossed rods.
 add('door_'+label+'_static_window_regulator_comparison',build_regulator(side))
 stop=cq.Compound.makeCompound([box(.04,.008,.07,[side*.71,.60,-.27]),box(.005,.06,.025,[side*.73,.63,-.27])])
 add('door_'+label+'_glass_stop_bracket_comparison',stop)
 handle_x=side*.646
 handles=[rod([side*.705,.78,-.30],[handle_x,.78,-.30],.007),rod([handle_x,.78,-.30],[handle_x,.73,-.25],.008),cyl(.015,.018,[handle_x-.009,.73,-.25])]
 handles += [box(.02,.022,.19,[side*.645,.89,-.36]),rod([side*.645,.89,-.45],[side*.67,.89,-.45],.006),rod([side*.645,.89,-.27],[side*.67,.89,-.27],.006)]
 add('door_'+label+'_inner_pull_and_winder_envelopes',cq.Compound.makeCompound(handles),(.47,.48,.43))
 beam=box(.020,.055,.72,[side*.744,.74,-.30]).cut(box(.014,.043,.724,[side*.744,.74,-.30]))
 add('door_'+label+'_north_america_impact_bar_comparison',beam)
 nodes[label+'_hinge_axis_units']=[[side*.72,.62,.12],[side*.72,.91,.12]]
 nodes[label+'_glass_bottom_units']=[side*.714,1.015,-.42]
glazing_nodes={}
for side,label in [(1,'left'),(-1,'right')]:
 items,glazing_nodes[label]=build_glazing(side,json.loads((OUT.parent/'body-data.json').read_text()))
 for name,shape,color in items:add('door_'+label+'_'+name,shape,color)
assembly=cq.Assembly(name='closed_door_static_hardware_comparison');meshes=[]
for name,shape,color in parts:
 assembly.add(shape,name=name,color=cq.Color(*color));vs,ts=absolute_tessellation(shape,.15,.15) if any(t in name for t in ['raised_main_glass','quarterlight_','main_glass_setting']) else shape.tessellate(.7,.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/'door-hardware-reference.step'))
limits='All profiles, sections, access-hole outlines, hinge axes/leaves, latch/striker engagement, glass channel/stop stations, regulator arms/sector, trim, handles, reinforcement and body poses are estimates. Factory topology does not identify fitted parts. Left/right mirroring is a construction assumption. Impact-bar presence/section/anchors on this actual car are unverified. Closed drawing pose and existing raised glazing are not an operating door/window mechanism. No hinge rotation, scissor compatibility, gear teeth, glass travel, seals, load/impact strength, rod/cable linkage, lock actuation, tool access or verified replacement/removal path. Whole-door display offsets carry the estimated door-side references only; body hinges/strikers stay fixed as an inspection convention.'
meta=dict(units='mm',factory_sources=['https://www.spridgets.de/downloads/Parts-1977.pdf#page=239','https://www.spridgets.de/downloads/Parts-1977.pdf#page=241','https://www.spridgets.de/downloads/Parts-1977.pdf#page=242','https://www.spridgets.de/downloads/Parts-1977.pdf#page=243','https://www.spridgets.de/downloads/Parts-1977.pdf#page=244'],manufacturer_source='https://www.bmh-ltd.com/vehicle-parts/mg-midget-parts/mg-midget-skin-panels/',owner_photos=['IMG_6341','IMG_6329'],basis='Factory AHA7428 hinge, later lock/striker, AHA8533/34 regulator and lower/rear glass-channel architecture. Factory USA/Canada door branches remain distinct; BMH replacement doors explicitly omit NA side-impact bars. Owner photos show open-right/closed door and raised glass context, not a hidden-hardware survey.',limits=limits,nodes=nodes,parts=meshes)
meta['hinge_reconstruction']=dict(supplier_source='https://www.mgocspares.co.uk/product/aha7428/hinge-door-mid62-bmh8500',supplier_image='https://www.mgocspares.co.uk/ImageHandler/105051w900h900/aha7428?Sequence=105051&Width=900&Height=900',factory_source='https://www.spridgets.de/downloads/Parts-1977.pdf#page=239',factory_quantity_scope='Four AHA7428 hinges; eight HZS505/LWZ505 and twelve PMZ408/LWZ404 roles. Supplier photograph supports two-hole arm and three-hole fork-base architecture, not fitted sizes or coordinates.',retained_axis_units={label:nodes[label+'_hinge_axis_units']for label in ['left','right']},retained_body_receiver_holes=True,estimated_pin_radius_mm=.0055*S,estimated_eye_bore_radius_mm=.006*S,estimated_pin_reference_length_mm=.065*S,estimated_central_eye_length_mm=.020*S,estimated_outer_ear_length_mm=.010*S,estimated_outer_ear_center_offsets_mm=[-.025*S,.025*S],limits='Gooseneck curve, leaf sections, fork/ear construction, three-hole base map, pin retention and mounting pose remain unmeasured. Shared eye axes and a connected leaf do not prove fitted seating, A-post relief, loaded support, working sweep or safe removal. Existing body receiver axes remain construction references; no new body cutout or captive-plate fit is inferred.')
meta['limits']=meta['limits'].replace('No hinge rotation, scissor compatibility','No verified hinge rotation, scissor compatibility')
meta['limits']+=' The viewer may rotate the complete door around the retained estimated axis for inspection; its60-degree display pose is not a measured stop, check-strap limit, collision-approved sweep or release procedure. Window mechanism stays static.'
meta['window_regulator_reconstruction']=regulator_metadata()
meta['door_glazing_reconstruction']=dict(**glazing_metadata(),nodes=glazing_nodes)
meta['basis']+=' '+glazing_metadata()['basis']
meta['limits']+=' '+glazing_metadata()['limits']
meta['limits']+=' '+regulator_metadata()['limits']
(OUT/'door-hardware-reference.json').write_text(json.dumps(meta,separators=(',',':'))+'\n')
restored=cq.importers.importStep(str(OUT/'door-hardware-reference.step')).val();assert restored.isValid()
for i in [0,11]:assert parts[i][1].intersect(cyl(.001,.02,[(.675 if i==0 else -.675)-.01,.77,-.51])).Volume()<1e-6
print(json.dumps(dict(groups=len(parts),solids=len(restored.Solids()),step_valid=True,inner_access_open=True,closed_source_pose_only=True)))
