from pathlib import Path
import shutil,json
import wx
_app=wx.App(False)
import pcbnew as k
B=Path('work/tri-product-continuation-2026-10-10.nosync').resolve();R=B/'lamp_driver_fanout_A56';R.mkdir(exist_ok=True)
if (R/'checks.json').exists():raise RuntimeError('Final candidate present: replay generation only in a fresh isolated comparison directory')
shutil.copytree(B/'lamp_driver_routing_A54/board',R/'board',dirs_exist_ok=True)
p=R/'board/led_driver.kicad_pcb'
import sys,re
sys.path.insert(0,str(B/'parity_metadata_closure_A22'));from prepare_candidates import forms
s=p.read_text()
for tag in ['segment','via']:
 for st,en,t in reversed(list(forms(s,tag))):
  keep=False
  if tag=='via':
   x,y=map(float,re.search(r'\(at ([\d.-]+) ([\d.-]+)',t).groups())
   keep='(net "/GND")' in t and abs(y-46)<=1.61 and any(abs(x-a)<=.81 for a in [23,53,83])
  if not keep:s=s[:st]+s[en:]
p.write_text(s);b=k.LoadBoard(str(p))
fps={f.GetReference():f for f in b.GetFootprints()}
for i,cx in enumerate([23,53,83],1):
 fps['R'+str(i)].SetPosition(k.VECTOR2I(k.FromMM(cx-6),k.FromMM(31.5)))
 for j,yy in [(2*i-1,31.5),(2*i,34.3)]:fps['C'+str(j)].SetPosition(k.VECTOR2I(k.FromMM(cx+6),k.FromMM(yy)))
for d in b.GetDrawings():
 if isinstance(d,k.PCB_TEXT):d.SetText(d.GetText().replace('A54 BENCH CANDIDATE','A56 FANOUT CANDIDATE'))
rows=[]
for ref,cx in [('U1',23),('U2',53),('U3',83)]:
 for pad in fps[ref].Pads():
  pin=pad.GetNumber()
  if not pin or pin=='39' or not pad.GetNetname():continue
  x,y=k.ToMM(pad.GetPosition().x),k.ToMM(pad.GetPosition().y);sgn=1 if x>cx else -1
  a=(x,y);c=(cx+sgn*3.75,y);v=(cx+sgn*9,46+(y-46)*1.7)
  for st,en in [(a,c),(c,v)]:
   t=k.PCB_TRACK(b);t.SetStart(k.VECTOR2I(k.FromMM(st[0]),k.FromMM(st[1])));t.SetEnd(k.VECTOR2I(k.FromMM(en[0]),k.FromMM(en[1])));t.SetWidth(k.FromMM(.2));t.SetLayer(k.F_Cu);t.SetNetCode(pad.GetNetCode());b.Add(t)
  t=k.PCB_VIA(b);t.SetPosition(k.VECTOR2I(k.FromMM(v[0]),k.FromMM(v[1])));t.SetWidth(k.FromMM(.6));t.SetDrill(k.FromMM(.3));t.SetViaType(k.VIATYPE_THROUGH);t.SetLayerPair(k.F_Cu,k.B_Cu);t.SetNetCode(pad.GetNetCode());b.Add(t)
  rows.append({'ref':ref,'pin':pin,'net':pad.GetNetname(),'pad':a,'escape_via':v})
k.SaveBoard(str(p),b);(R/'fanout_points.json').write_text(json.dumps(rows,indent=2));print('Fanout channels',len(rows))
