"""Curved pedal pendant comparisons at retained pivot,clevis and pad datums."""
from pathlib import Path
import copy,hashlib,json,math
import cadquery as cq
from front_wing_geometry import absolute_tessellation
ROOT=Path(__file__).resolve().parent;SOURCE='pedal-support-reference';OUTPUT='curved-pedals-reference';S=2032/2.4
source=json.loads((ROOT/(SOURCE+'.json')).read_text());assembly=cq.Assembly.importStep(str(ROOT/(SOURCE+'.step')))
native={n:o.obj for n,o in assembly.objects.items()if o.obj is not None};changes={};construction={}
X=cq.Vector(1,0,0)
for label,node in source['nodes'].items():
 name='pedal_'+label+'_bored_hub_arm_and_upper_eye_comparison';old=native[name]
 x,py,pz=node['pivot_center_body_mm'];_,ey,ez=node['pad_center_body_mm']
 # Same formerly angular web as the historical builder. Remove only the
 # middle web,retaining the complete hub/upper linkage and lower pad tab.
 points=[(py+6,pz-7),(.59*S+10,-.18*S-8),(ey+8,ez-8),(ey-8,ez+8),(.59*S-10,-.18*S+8),(py-6,pz+7)]
 former=cq.Workplane('YZ',origin=(x-3,0,0)).polyline(points).close().extrude(6).val()
 hub_zone=cq.Solid.makeCylinder(20,26,cq.Vector(x-13,py,pz),X)
 upper_zone=cq.Solid.makeBox(40,130,100,(x-20,py-18,pz-50))
 pad_bb=native['pedal_'+label+'_rubber_pad_envelope'].BoundingBox()
 pad_zone=cq.Solid.makeBox(40,pad_bb.ylen+4,80,(x-20,pad_bb.ymin-2,ez-40))
 protected=hub_zone.fuse(upper_zone).fuse(pad_zone)
 controls=[(py,pz),(py-75,pz-55),(ey+55,ez+31),(ey,ez)]
 def center(t):
  a,b,c,d=controls;u=1-t
  return tuple(u**3*a[i]+3*u*u*t*b[i]+3*u*t*t*c[i]+t**3*d[i]for i in [0,1])
 def tangent(t):
  a,b,c,d=controls;u=1-t
  dy,dz=[3*u*u*(b[i]-a[i])+6*u*t*(c[i]-b[i])+3*t*t*(d[i]-c[i])for i in [0,1]]
  l=math.hypot(dy,dz);return dy/l,dz/l
 sides=[]
 for sign in [-1,1]:
  pts=[]
  for i in range(65):
   t=i/64;y,z=center(t);dy,dz=tangent(t);pts.append(cq.Vector(x-3,y-sign*9*dz,z+sign*9*dy))
  sides.append(pts)
 wire=cq.Wire.assembleEdges([cq.Edge.makeSpline(sides[0]),cq.Edge.makeLine(sides[0][-1],sides[1][-1]),cq.Edge.makeSpline(sides[1][::-1]),cq.Edge.makeLine(sides[1][0],sides[0][0])])
 web=cq.Solid.extrudeLinear(wire,[],X*6)
 new=old.cut(former.cut(protected)).fuse(web.cut(protected))
 new=new.cut(cq.Solid.makeCylinder(11.8,26,cq.Vector(x-13,py,pz),X)).clean()
 assert new.isValid()and len(new.Solids())==1,name
 changes[name]=new
 construction[label]=dict(centerline_control_yz_mm=controls,selected_normal_web_width_mm=18,retained_x_gauge_mm=6,offset_curve_samples_per_edge=65,protected_hub_radius_mm=20,protected_upper_start_y_mm=py-18,protected_pad_tab_y_bounds_mm=[pad_bb.ymin-2,pad_bb.ymax+2])
limits=('Factory brake p163 and clutch p153 illustrations support curved pendant-arm morphology,not a calibrated arm shape. '
        'Clutch p153 older-car coverage is crosschecked against Moss issue25 p197 listing AHA8071 through GAN6-212000 and CHA792 thereafter;no fitted variant is selected. '
        'Selected cubic centerline,18mm normal ribbon width and existing6mm X section are construction estimates;both arms repeat a generic curve without proof of handed or variant-specific shape. '
        'Complete old upper linkage/hub,lower pad-tab and rubber-pad contact regions,pivot/clevis bores,pad centers,rods,hardware,housing,seals and body placement remain. '
        'No new spring anchor,body notch,stop,switch or hidden receiver is fabricated. Return springs and adjustment roles remain unmodeled. '
        'No fitted pedal ratio,travel,free play,force,wear,loaded support,column/body clearance or verified replacement/removal procedure is established. '
        'Existing platform/column and pedal-plinth junction conflicts remain independent unresolved source relationships.')
revision=dict(factory_sources=['https://www.spridgets.de/downloads/Parts-1977.pdf#page=163','https://www.spridgets.de/downloads/Parts-1977.pdf#page=153'],catalog_crosscheck='https://www.moss-europe.co.uk/content/files/pdfs/moss-europe-sprite-midget-catalogue-issue-25.pdf#page=197',evidence='curved-pedals-evidence.json',owner_dimensions_applied=False,construction=construction,limits=limits,retained_source_sha256={ext:hashlib.sha256((ROOT/(SOURCE+ext)).read_bytes()).hexdigest()for ext in ['.json','.step']})
result=copy.deepcopy(source);out=cq.Assembly(name='pedal_assembly_with_curved_pendant_comparisons')
for p in result['parts']:
 n=p['name'];shape=changes.get(n,native[n]);out.add(shape,name=n,color=cq.Color(*p['color']))
 if n in changes:
  vs,ts=absolute_tessellation(shape,.08,.12);p['vertices']=[round(v,4)for pt in vs for v in pt.toTuple()];p['triangles']=[i for t in ts for i in t]
result['curved_arm_revision']=revision;result['limits']+=' '+limits
out.export(str(ROOT/(OUTPUT+'.step')));(ROOT/(OUTPUT+'.json')).write_text(json.dumps(result,separators=(',',':'))+'\n')
restored=cq.importers.importStep(str(ROOT/(OUTPUT+'.step'))).val();assert restored.isValid()and all(s.Volume()>0 for s in restored.Solids())
print(json.dumps(dict(groups=len(result['parts']),solids=len(restored.Solids()),connected_arms=2)))

# Keep this display export faithful to its unchanged native solids.
from display_mesh_geometry import repair_source as repair_display_source
repair_display_source(Path(__file__).parent / 'curved-pedals-reference.json')
