"""Formed radiator tank faces within retained bounds/interfaces; estimated sections."""
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='radiator-core-reference'
OUTPUT='radiator-formed-tanks-reference'
PACK='radiator_separate_sheet_fin_pack_comparison'
data=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 and not n.startswith(PACK+'/')}
native[PACK]=assembly.objects[PACK].toCompound()
T=1.2
CROWN=4.
CAP=8.
CAP_RUN=12.
changes={};geometry={}

def box_bounds(bb,inset=0):
    return cq.Solid.makeBox(bb.xlen-2*inset,bb.ylen-2*inset,bb.zlen-2*inset,
                            (bb.xmin+inset,bb.ymin+inset,bb.zmin+inset))

for side in ['upper','lower']:
    name='radiator_'+side+'_side_hollow_tank_reference'
    old=native[name];bb=old.BoundingBox();cx=(bb.xmin+bb.xmax)/2
    def front(x,y):
        end_distance=min(y-bb.ymin,bb.ymax-y)
        weight=.5*(1+math.cos(math.pi*max(0,end_distance)/CAP_RUN))if end_distance<CAP_RUN else 0
        return bb.zmax-CROWN*((x-cx)/(bb.xlen/2))**2-CAP*weight
    def loft(inset=0):
        lo,hi=bb.ymin+inset,bb.ymax-inset
        stations=sorted({lo,hi,*[bb.ymin+CAP_RUN*i/12 for i in range(13)],
                          *[bb.ymax-CAP_RUN*i/12 for i in range(13)]})
        stations=[y for y in stations if lo<=y<=hi]
        wires=[]
        for y in stations:
            xs=[bb.xmin+inset+(bb.xlen-2*inset)*i/12 for i in range(13)]
            face=[cq.Vector(x,y,front(x,y)-inset)for x in xs]
            rear=[cq.Vector(xs[0],y,bb.zmin+inset),cq.Vector(xs[-1],y,bb.zmin+inset)]
            # Exact quadratic Bezier rather than an interpolating spline:
            # same selected parabola,without unnecessary internal knots that
            # produced inconsistent kernel probe intersections on this skin.
            amplitude=CROWN*((xs[-1]-cx)/(bb.xlen/2))**2
            control=cq.Vector(cx,y,front(cx,y)-inset+amplitude)
            wires.append(cq.Wire.assembleEdges([cq.Edge.makeLine(rear[0],rear[1]),
                cq.Edge.makeLine(rear[1],face[-1]),cq.Edge.makeBezier([face[-1],control,face[0]]),cq.Edge.makeLine(face[0],rear[0])]))
        return cq.Solid.makeLoft(wires,ruled=True)
    outer,inner=loft(),loft(T)
    closed=box_bounds(bb).cut(box_bounds(bb,T))
    # Recover the old receiver/window removals from native material, rather
    # than inferring new port circles or another drilling/neck map.
    old_openings=closed.cut(old)
    shell=outer.cut(inner).cut(old_openings).clean()
    assert shell.isValid()and len(shell.Solids())==1
    changes[name]=shell
    geometry[side]=dict(retained_bounds_mm={k:[getattr(bb,k+'min'),getattr(bb,k+'max')]for k in ['x','y','z']},
                        selected_front_crown_mm=CROWN,selected_end_recession_mm=CAP,selected_end_transition_mm=CAP_RUN,
                        transverse_profile='Exact quadratic Bezier crown;ruled longitudinal end-transition stations.',
                        projected_front_z_wall_mm=T,side_and_end_coordinate_inset_mm=T,
                        retained_rear_plane_z_mm=bb.zmin,
                        removed_old_openings_volume_mm3=old_openings.Volume(1e-9))

limits=('Primary NRP1028 photograph supports formed side-tank faces and rounded end transitions,not fitted identity or calibrated sections. '
        'Selected4mm transverse crown,8mm end recession over12mm,ruled loft stations and1.2mm coordinate insets are construction estimates. '
        'The front wall is a projected-Z1.2mm section,not a normal factory gauge or forming/brazing specification. '
        'Retained box bounds,flat rear plane,neck receiver cuts and core-facing windows are inherited construction datums. '
        'Both tanks repeat the chosen morphology without proving handed production geometry. '
        'Eight other mesh records/native volumes,core/header/fin shapes,necks,hoses,clamps and all installed source transforms stay. '
        'Existing cowl/guard morphology already follows separate owner-photo reconstruction;hidden joint/attachment ownership remains unresolved. '
        'No new seam,receiver,baffle,braze joint,fixing,drain or fitted panel identity is invented. '
        'Source contacts do not establish fitted clearance,loaded support,pressure integrity,flow,thermal performance or verified replacement/removal.')
revision=dict(evidence='radiator-formed-tanks-evidence.json',owner_dimensions_applied=False,
              tank_geometry=geometry,limits=limits,
              source_sha256={e:hashlib.sha256((ROOT/(SOURCE+e)).read_bytes()).hexdigest()for e in ['.json','.step']})
result=copy.deepcopy(data);out=cq.Assembly(name='radiator_with_formed_side_tank_comparisons')
for part in result['parts']:
    n=part['name']
    if n==PACK:out.add(assembly.objects[PACK],name=PACK)
    else:out.add(changes.get(n,native[n]),name=n,color=cq.Color(*part['color']))
    if n in changes:
        vs,ts=absolute_tessellation(changes[n],.06,.18)
        part['vertices']=[round(v,4)for p in vs for v in p.toTuple()];part['triangles']=[v for t in ts for v in t]
result['formed_tank_revision']=revision
out.export(str(ROOT/(OUTPUT+'.step')))
p=ROOT/(OUTPUT+'.step');p.write_text('\n'.join(l.rstrip()for l in p.read_text().splitlines())+'\n')
(ROOT/(OUTPUT+'.json')).write_text(json.dumps(result,separators=(',',':'))+'\n')
restored=cq.importers.importStep(str(p)).val()
assert restored.isValid()and len(restored.Solids())==109 and all(s.Volume()>0 for s in restored.Solids())
print(json.dumps(dict(top_level_groups=10,positive_valid_solids=109,changed_tanks=2,held_meshes=8,fin_instances_retained=True)))
