"""North American fascia architecture; no fitted gauge identities or body survey."""
from pathlib import Path
import json
import cadquery as cq
from cabin_layout import cabin_metadata
from demister_layout import cut_openings,interface_metadata
from front_wing_geometry import absolute_tessellation
from steering_column_layout import TOP_UNITS, LOWER_UNITS, BODY_PLANE_Z_UNITS, column_at_units, layout_metadata
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 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=(.12,.13,.12)):
 assert shape.isValid() and shape.Volume()>0,name
 parts.append((name,shape,color))
def XY(x,y):return (x*S,y*S)
z=-.105;th=1.5/S
outline=(cq.Workplane('XY').moveTo(*XY(-.64,.84)).lineTo(*XY(.306,.84)).lineTo(*XY(.306,.895)).threePointArc(XY(.386,.945),XY(.466,.895)).lineTo(*XY(.466,.84)).lineTo(*XY(.64,.84)).lineTo(*XY(.64,1.010)).threePointArc(XY(0,1.025),XY(-.64,1.010)).close())
face=outline.extrude(th*S).translate((0,0,z*S)).val()
mounts=[(.60,.905,.024),(.52,.91,.05),(.40,.975,.024),(.325,.975,.024),(.245,.91,.05)]
for x,y,r in mounts:face=face.cut(cyl(r,.008,[x,y,z-.002]))
face=face.cut(box(.45,.15,.012,[-.34,.92,z])).cut(box(.12,.15,.012,[.08,.93,z]))
rail=(cq.Workplane('XY').moveTo(*XY(-.64,1.010)).threePointArc(XY(0,1.025),XY(.64,1.010)).lineTo(*XY(.64,.998)).threePointArc(XY(0,1.013),XY(-.64,.998)).close().extrude(.11*S).translate((0,0,z*S)).val())
rail=cut_openings(rail,S)
add('fascia_open_back_face_and_padded_rail_reference',cq.Compound.makeCompound([face,rail]))
for i,(x,y,r) in enumerate(mounts):
 body=cyl(r-.001,.065,[x,y,z+th]);bezel=ring(r+.004,r-.004,.006,[x,y,z-.005]);blank=cyl(r-.004,.001,[x,y,z-.005])
 add('fascia_circular_mount_'+str(i+1)+'_reference',cq.Compound.makeCompound([body,bezel,blank]),(.28,.29,.26))
liner=box(.445,.145,.19,[-.34,.92,-.005]).cut(box(.441,.141,.19,[-.34,.92,-.007]))
add('fascia_glovebox_liner_reference',liner)
lid=box(.45,.15,.005,[-.34,.92,z-.006]).cut(cyl(.009,.009,[-.34,.98,z-.012]))
add('fascia_glovebox_lid_reference',lid)
hardware=[]
for x in [-.52,-.17]:hardware.append(cyl(.006,.04,[x-.02,.845,z+.005],(1,0,0)))
hardware.extend([ring(.012,.007,.008,[-.34,.98,z-.013]),cyl(.007,.02,[-.34,.98,z-.005])])
add('fascia_glovebox_hinge_and_lock_cues_reference',cq.Compound.makeCompound(hardware),(.40,.41,.37))
nacelle=box(.12,.15,.025,[.08,.93,z+.005]).cut(box(.114,.144,.027,[.08,.93,z+.007]))
for y in [.90,.96]:nacelle=nacelle.fuse(box(.03,.04,.006,[.08,y,z-.007]))
add('fascia_switch_nacelle_reference',nacelle)
radio=box(.25,.12,.045,[.04,.77,z-.02]).cut(box(.244,.114,.045,[.04,.77,z-.018]))
radio=radio.cut(box(.17,.065,.009,[.04,.78,z-.046]))
add('fascia_radio_mounting_panel_reference',radio)
supports=[]
for x in [-.56,-.18,.18,.56]:
 plate=box(.045,.025,.085,[x,1.010,-.025]).cut(cyl(.003,.03,[x,.995,.005],(0,1,0)))
 supports.append(plate)
add('fascia_support_plate_and_stud_cues_reference',cq.Compound.makeCompound(supports),(.40,.41,.37))
# Shared estimated wheel setback/column axis. No fitted collapse/load mechanism.
top=cq.Vector(*TOP_UNITS);bottom=cq.Vector(*LOWER_UNITS);direction=(bottom-top).normalized()
def column_at(z):return cq.Vector(*column_at_units(z))
p=column_at(-.02);d=direction
collar=cq.Solid.makeCylinder(.048*S,.03*S,V(p.toTuple())-d*(.015*S),d).cut(cq.Solid.makeCylinder(.039*S,.034*S,V(p.toTuple())-d*(.017*S),d))
upper=[collar,box(.18,.006,.045,[p.x,.99,.06])]
for side in [-1,1]:upper.append(rod([p.x+side*.045,p.y,-.02],[p.x+side*.075,.985,.06],.005))
add('column_upper_support_comparison',cq.Compound.makeCompound(upper),(.38,.39,.35))
p=column_at(BODY_PLANE_Z_UNITS);c=p.toTuple()
plate=box(.145,.13,.004,[p.x,p.y,.124])
def oval_cut(shape):
 hole=cq.Workplane('XY').ellipse(.041*S,.044*S).extrude(.02*S).translate((p.x*S,p.y*S,.116*S)).val();return shape.cut(hole)
plate=oval_cut(plate)
for dx in [-.058,.058]:
 for dy in [-.05,.05]:plate=plate.cut(cyl(.003,.008,[p.x+dx,p.y+dy,.119]))
add('column_body_flange_comparison',plate,(.38,.39,.35))
seal=cq.Workplane('XY').ellipse(.050*S,.053*S).extrude(.006*S).translate((p.x*S,p.y*S,.127*S)).val()
add('column_body_seal_comparison',oval_cut(seal))
assembly=cq.Assembly(name='north_america_fascia_and_column_mounting_reference');meshes=[]
for name,shape,color in parts:
 assembly.add(shape,name=name,color=cq.Color(*color));vs,ts=absolute_tessellation(shape,.06,.15)
 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/'fascia-reference.step'))
limits='Factory NA/Sweden layout comparison only; owner IMG_6341 does not identify the hidden fascia fittings or calibrate them. All profiles, padding, wall gauges, apertures, circular mount identities/sizes, glovebox, switch/radio details, supports, fastener patterns, column flange/seal and body coordinates are estimates. Circular faces are blank mounting envelopes, not assigned working instruments or switch identities. No collapse behavior, load support, gasket seating, circuit, cable/capillary route, fitted clearance, lid motion or verified replacement/removal path. Column axis follows the revised estimated wheel setback from IMG_6341 context, shared with rack entry and the body aperture. All installed coordinates, sleeve boundary and support lengths remain uncalibrated; no operating clearance is verified. Inspection offsets are illustrative.'
metadata=dict(demister_interface_reference=interface_metadata(S),units='mm',factory_sources=['https://www.spridgets.de/downloads/Parts-1977.pdf#page=278','https://www.spridgets.de/downloads/Parts-1977.pdf#page=279','https://www.spridgets.de/downloads/Parts-1977.pdf#page=137','https://www.spridgets.de/downloads/Parts-1977.pdf#page=138'],owner_photos=['IMG_6341'],basis='AHA8866 NA/Sweden fascia, separate switch/radio nacelle, CHA55 cubby lid and support architecture. GAN6 North American collapsible-column and four bulkhead fixing relationship are comparisons, not fitted identities or hole coordinates.',limits=limits,circular_mounts_units=mounts,cabin_height_reconstruction=cabin_metadata(S),column_layout=layout_metadata(S),column_body_center_units=c,column_body_opening_radii_units=[.041,.044],parts=meshes)
(OUT/'fascia-reference.json').write_text(json.dumps(metadata,separators=(',',':'))+'\n')
restored=cq.importers.importStep(str(OUT/'fascia-reference.step')).val();assert restored.isValid()
for x,y,r in mounts:assert face.intersect(cyl(.001,.01,[x,y,z-.003])).Volume()<1e-6
assert plate.intersect(cyl(.001,.02,[p.x,p.y,.116])).Volume()<1e-6
print(json.dumps(dict(groups=len(parts),solids=len(restored.Solids()),step_valid=True,fascia_apertures_open=True,column_body_center_units=c)))
