import sys,re,json,shutil,uuid
from pathlib import Path
ROOT=Path(__file__).resolve().parent;BASE=ROOT.parent
sys.path.insert(0,str(BASE/'parity_metadata_closure_A22'))
from prepare_candidates import forms,run,sha
from align_and_verify import footprint_rows,netlist
SRC=BASE/'mosfet_pin_audit_A25/vacuum_corrected';DST=ROOT/'vacuum_esd_candidate';DST.mkdir(exist_ok=True)
OLD='vacuum_control_evt_a2_pmos_pinfix';NEW='vacuum_control_evt_a3_esd_railfree'
FP='Vacuum_ESD:TI_DCK0003A_TPD2E2U06';MPN='TPD2E2U06DCKR';URL='https://www.ti.com/lit/ds/symlink/tpd2e2u06.pdf'
def uid(key):return str(uuid.uuid5(uuid.NAMESPACE_URL,'pcbapartner:A27:'+key))
for name in ['sym-lib-table','Project.kicad_sym','fp-lib-table']:shutil.copyfile(SRC/name,DST/name)
shutil.copytree(SRC/'Vacuum_Shunt.pretty',DST/'Vacuum_Shunt.pretty',dirs_exist_ok=True)
shutil.copyfile(SRC/(OLD+'.kicad_pro'),DST/(NEW+'.kicad_pro'))
symbol='''(symbol "Project:TPD2E2U06_DCK" (pin_names (offset 0.3)) (in_bom yes) (on_board yes)
 (property "Reference" "U" (at 0 5.08 0) (effects (font (size 1 1))))
 (property "Value" "TPD2E2U06_DCK" (at 0 3.81 0) (effects (font (size 1 1))))
 (property "Footprint" "Vacuum_ESD:TI_DCK0003A_TPD2E2U06" (at 0 0 0) (effects (font (size 1 1)) (hide yes)))
 (property "Datasheet" "https://www.ti.com/lit/ds/symlink/tpd2e2u06.pdf" (at 0 0 0) (effects (font (size 1 1)) (hide yes)))
 (symbol "TPD2E2U06_DCK_0_1" (rectangle (start -2.54 2.54) (end 2.54 -5.08) (stroke (width 0.254) (type default)) (fill (type background))))
 (symbol "TPD2E2U06_DCK_1_1"
  (pin passive line (at -5.08 0 0) (length 2.54) (name "IO1" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
  (pin passive line (at -5.08 -2.54 0) (length 2.54) (name "IO2" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
  (pin passive line (at 0 -7.62 90) (length 2.54) (name "GND" (effects (font (size 1 1)))) (number "3" (effects (font (size 1 1)))))
 ))'''
s=(SRC/(OLD+'.kicad_sch')).read_text().replace('(project "'+OLD+'"','(project "'+NEW+'"')
for st,en,b in reversed(list(forms(s,'symbol'))):
 if b.startswith('(symbol "Project:USBLC6-2P6"'):s=s[:st]+symbol+s[en:]
 elif '(property "Reference" "U2"' in b:
  b=b.replace('Project:USBLC6-2P6','Project:TPD2E2U06_DCK').replace('"USBLC6-2SC6"',json.dumps(MPN)).replace('"Package_TO_SOT_SMD:SOT-23-6"',json.dumps(FP))
  b=b.replace('(property "Datasheet" ""','(property "Datasheet" '+json.dumps(URL))
  s=s[:st]+b+s[en:]
# Remove only U2's duplicate pass-through pins and obsolete VBUS pin stubs.
removed_schematic=[]
for tag in ['wire','label']:
 for st,en,b in reversed(list(forms(s,tag))):
  if any(pt in b for pt in ['120.65 77.47','128.27 77.47','115.57 69.85','123.19 69.85','120.65 74.93','128.27 74.93']):
   removed_schematic.append(b);s=s[:st]+s[en:]
(DST/(NEW+'.kicad_sch')).write_text(s)
lib=(DST/'Project.kicad_sym').read_text()
for st,en,b in reversed(list(forms(lib,'symbol'))):
 if b.startswith('(symbol "USBLC6-2P6"'):lib=lib[:st]+symbol.replace('"Project:TPD2E2U06_DCK"','"TPD2E2U06_DCK"',1)+lib[en:]
(DST/'Project.kicad_sym').write_text(lib)
# Dedicated manufacturer-recommended land: 0.95 x 0.4, center spacing 2.2,
# lead pitch 0.65, absolute corner radius 0.05, all dimensions in mm.
fp=Path('/Applications/KiCad/KiCad.app/Contents/SharedSupport/footprints/Package_TO_SOT_SMD.pretty/SOT-323_SC-70.kicad_mod').read_text()
fp=fp.replace('(footprint "SOT-323_SC-70"','(footprint "TI_DCK0003A_TPD2E2U06"',1)
for st,en,b in reversed(list(forms(fp,'pad'))):
 pin=re.match(r'\(pad "([^\"]+)"',b).group(1)
 xy={'1':(-1.1,-.65),'2':(-1.1,.65),'3':(1.1,0)}[pin]
 b=re.sub(r'\(at [^)]*\)','(at %s %s)'%xy,b,count=1)
 b=re.sub(r'\(size [^)]*\)','(size 0.95 0.4)',b,count=1).replace('(roundrect_rratio 0.25)','(roundrect_rratio 0.125)')
 fp=fp[:st]+b+fp[en:]
for st,en,b in reversed(list(forms(fp,'fp_line'))):
 if '(layer "F.CrtYd")' in b:fp=fp[:st]+fp[en:]
pts=[(-1.85,-1.35),(1.85,-1.35),(1.85,1.35),(-1.85,1.35),(-1.85,-1.35)]
crt=''.join('\n(fp_line (start %s %s) (end %s %s) (stroke (width 0.05) (type solid)) (layer "F.CrtYd"))'%(*a,*b) for a,b in zip(pts,pts[1:]))
fp=fp.rstrip()[:-1]+crt+'\n)\n'
(DST/'Vacuum_ESD.pretty').mkdir(exist_ok=True);(DST/'Vacuum_ESD.pretty/TI_DCK0003A_TPD2E2U06.kicad_mod').write_text(fp)
t=(DST/'fp-lib-table').read_text().rstrip();t=t[:-1]+' (lib (name "Vacuum_ESD") (type "KiCad") (uri "${KIPRJMOD}/Vacuum_ESD.pretty") (options "") (descr "TI DCK0003A recommended land")))\n';(DST/'fp-lib-table').write_text(t)
s=(SRC/(OLD+'.kicad_pcb')).read_text()
for st,en,b,r,_ in footprint_rows(s):
 if r!='U2':continue
 old_u2=b;fp_id=re.search(r'\(uuid "([^\"]+)"',b).group(1)
 new=fp.replace('(footprint "TI_DCK0003A_TPD2E2U06"','(footprint '+json.dumps(FP),1)
 new=re.sub(r'\((?:version|generator|generator_version) [^)]*\)','',new)
 new=new.replace('(layer "F.Cu")','(layer "F.Cu") (uuid "'+fp_id+'") (at 67 13.5)',1)
 new=new.replace('"REF**"','"U2"').replace('(property "Value" "SOT-323_SC-70"','(property "Value" '+json.dumps(MPN))
 for ps,pe,p in reversed(list(forms(new,'pad'))):
  pin=re.match(r'\(pad "([^\"]+)"',p).group(1);net={'1':'/USB_DP','2':'/USB_DM','3':'/GND'}[pin]
  p=p.rstrip()[:-1]+' (net '+json.dumps(net)+') (uuid "'+uid('pad'+pin)+'"))'
  new=new[:ps]+p+new[pe:]
 new=new.rstrip()[:-1]+'\n(property "Datasheet" '+json.dumps(URL)+' (at 0 0 0) (layer "F.Fab") (hide yes) (effects (font (size 1 1))))\n)'
 s=s[:st]+new+s[en:];break
removed=[]
old_data_nodes=['52.1375 19.05','49.8625 19.05','52.4349 19.3474','53.3097 19.3474','52.1375 20.95','49.8625 20.95','49.1297 20.95','48.0204 19.8407','48.0204 19.3722']
for st,en,b in reversed(list(forms(s,'segment'))):
 if ('"/VBUS_RAW"' in b and any(pt in b for pt in ['52.9281 20.1492','52.2867 20.1492','52.1375 20','54f87d99-3cf3-497b-ac86-6b50e128a094','79251153-d0b3-4db2-907b-b2083c87860a'])) or ('"/GND"' in b and any(pt in b for pt in ['48.6867 19.6508','49.5133 19.6508','49.8625 20','cf2e87b7-62e6-4efe-8489-4ad2d371b50f'])) or (('"/USB_DP"' in b or '"/USB_DM"' in b) and '"F.Cu"' in b and any(pt in b for pt in old_data_nodes)):
  removed.append(b);s=s[:st]+s[en:]
for st,en,b in reversed(list(forms(s,'via'))):
 if any(pt in b for pt in ['(at 48.6867 19.6508)','(at 53.3097 19.3474)','(at 42.9623 38.1873)']):s=s[:st]+s[en:]
# Place TVS just behind J1. Connector traces reach the protection pad nodes
# before continuing to the PD IC; the ground return reaches a J1 shield pad.
def seg(a,b,n,l='F.Cu'):
 key=str((a,b,n,l));return '\n(segment (start %s %s) (end %s %s) (width 0.2) (layer "%s") (net "%s") (uuid "%s"))'%(*a,*b,l,n,uid(key))
added=[]
for pts,n,l in [([(64.9559,6.2316),(64.65,6.5375),(64.65,11.6),(65.9,12.85),(67,12.85)],'/USB_DP','F.Cu'), ([(67,12.85),(53.3097,19.3474)],'/USB_DP','B.Cu'), ([(61.5627,5.8299),(61.5627,9.8127),(65.9,14.15),(53.1998,22.0123)],'/USB_DM','F.Cu'), ([(68.1,13.5),(68.1,14.5)],'/GND','F.Cu'), ([(68.1,14.5),(68.1,10.7),(65.68,8.05)],'/GND','B.Cu')]:
 added += [seg(a,b,n,l) for a,b in zip(pts,pts[1:])]
for p,n in [((67,12.85),'/USB_DP'),((68.1,14.5),'/GND')]:
 added.append('\n(via (at %s %s) (size 0.6) (drill 0.3) (layers "F.Cu" "B.Cu") (net "%s") (uuid "%s"))'%(*p,n,uid(str(p))))
s=s.rstrip()[:-1]+''.join(added)+'\n)\n';pcb=DST/(NEW+'.kicad_pcb');pcb.write_text(s)
for args,name in [(['sch','export','netlist','--format','kicadxml','--output',str(DST/'netlist.xml'),str(DST/(NEW+'.kicad_sch'))],'netlist.log'),(['sch','erc','--format','json','--output',str(DST/'erc.json'),str(DST/(NEW+'.kicad_sch'))],'erc.log'),(['pcb','drc','--format','json','--schematic-parity','--output',str(DST/'drc.json'),str(pcb)],'drc.log')]:assert run(args,DST/name)==0
_,oldpins=netlist(SRC/'netlist.xml');_,pins=netlist(DST/'netlist.xml')
assert {k:v for k,v in pins.items() if k[0]!='U2'}=={k:v for k,v in oldpins.items() if k[0]!='U2'}
assert {k[1]:v for k,v in pins.items() if k[0]=='U2'}=={'1':'/USB_DP','2':'/USB_DM','3':'/GND'}
oldfps={r:b for _,_,b,r,_ in footprint_rows((SRC/(OLD+'.kicad_pcb')).read_text())};newfps={r:b for _,_,b,r,_ in footprint_rows(s)}
assert {r for r in oldfps if oldfps[r]!=newfps[r]}=={'U2'}
check={'source_board_sha256':sha(SRC/(OLD+'.kicad_pcb')),'candidate_board_sha256':sha(pcb),'candidate_schematic_sha256':sha(DST/(NEW+'.kicad_sch')),'U2_MPN':MPN,'U2_pin_nets':{'1':'/USB_DP','2':'/USB_DM','3':'/GND'},'U2_has_no_VBUS_supply_pin':True,'U2_position_mm':[67,13.5],'all_other_schematic_pin_nets_unchanged':True,'all_other_board_footprints_byte_identical':True,'schematic_removed_stubs_and_labels':len(removed_schematic),'removed_segments':len(removed),'added_segments':len(added)-2,'removed_vias':3,'added_vias':2,'land_mm':{'pad_size':[.95,.4],'column_pitch':2.2,'lead_pitch':.65,'corner_radius':.05},'primary_source_url':URL,'primary_source_sha256':sha(ROOT/'sources/tpd2e2u06.pdf'),'physical_validation':'NOT_RUN_NO_SAMPLE','manufacturing_status':'FABRICATION_HOLD'}
(ROOT/'esd_candidate_checks.json').write_text(json.dumps(check,indent=2)+'\n')
d=json.loads((DST/'drc.json').read_text());print(json.dumps({'violations':len(d['violations']),'unconnected':len(d['unconnected_items']),'parity':len(d['schematic_parity'])},indent=2))
