from pathlib import Path
import json,struct,hashlib,csv
root=Path(__file__).resolve().parent
raw=json.loads((root/'local-results.json').read_text())
rows=[]
for name in ['whole','excerpt']:
 b=(root/(name+'.mid')).read_bytes();assert b[:4]==b'MThd' and b[14:18]==b'MTrk'
 fmt,tracks,division=struct.unpack_from('>HHH',b,8);assert (fmt,tracks,division)==(0,1,480)
 assert struct.unpack_from('>I',b,18)[0]==len(b)-22
 i=22;tick=0;tempo=500000;status=None;ons=[];offs=[];metas=[]
 def vlq():
  global i
  v=0
  while True:
   x=b[i];i+=1;v=(v<<7)|(x&127)
   if x<128:return v
 while i<len(b):
  tick+=vlq()
  if b[i]>=128:status=b[i];i+=1
  if status==255:
   kind=b[i];i+=1;n=vlq();data=b[i:i+n];i+=n;metas.append(kind)
   if kind==81:tempo=int.from_bytes(data,'big')
   if kind==47:break
  else:
   pitch,vel=b[i:i+2];i+=2
   if status&240==144 and vel:ons.append({'pitch':pitch,'tick':tick,'seconds':tick*tempo/division/1e6})
   elif status&240==128 or (status&240==144 and not vel):offs.append({'pitch':pitch,'tick':tick})
 assert len(ons)==len(offs)==4 and [n['pitch'] for n in ons]==[60,64,67,72]
 assert metas==[81,47],metas
 expected=next(r for r in raw['rows'] if r['name']==name)
 assert max(abs(a['seconds']-e['start']) for a,e in zip(ons,expected['notes']))<1/960
 rows.append({'name':name,'bytes':len(b),'sha256':hashlib.sha256(b).hexdigest(),'noteOns':ons,'noteOffs':offs,'tempoMicroseconds':tempo,'division':division,'metaEventTypes':metas})
comparison=[{'pitch':a['pitch'],'wholeSeconds':a['seconds'],'excerptSeconds':b['seconds'],'placedSeconds':1.6+b['seconds'],'residualSeconds':1.6+b['seconds']-a['seconds']} for a,b in zip(rows[0]['noteOns'],rows[1]['noteOns'])]
out={'result':'PASS','method':'Independent Python SMF/VLQ parse and tick-to-seconds check; no DAW import or audio rendering','rows':rows,'comparison':comparison}
(root/'independent-results.json').write_text(json.dumps(out,indent=2),encoding='utf-8')
with (root/'results.csv').open('w',newline='',encoding='utf-8') as f:
 w=csv.DictWriter(f,fieldnames=comparison[0].keys());w.writeheader();w.writerows(comparison)
print(json.dumps(out))
