"""Download source photos and the input generator, verifying hashes. Standard-library Python 3 only.
Usage: python3 download_dataset.py --section perspective --output barcode-data
       node barcode-data/generate_inputs.mjs --dir barcode-data --section perspective
Omit --section for all 556 originals (about 490 MB). Test images are not hosted: the generator
renders them from the originals and checks every result against the manifest's RGBA SHA-256.
"""
import argparse,hashlib,json,urllib.request
from pathlib import Path
from urllib.parse import urljoin
p=argparse.ArgumentParser(description=__doc__);p.add_argument('--base',default='https://barcode-benchmark-f-kleinicke.netlify.app/');p.add_argument('--section',default='all');p.add_argument('--output',default='barcode-data');a=p.parse_args();out=Path(a.output);out.mkdir(parents=True,exist_ok=True)
def get(path):
 with urllib.request.urlopen(urljoin(a.base,path),timeout=120) as r:return r.read()
def sha(b):return hashlib.sha256(b).hexdigest()
raw=get('public-manifest.json');m=json.loads(raw);(out/'public-manifest.json').write_bytes(raw)
for name in ['ATTRIBUTION.json','LICENSE-DATA.md','README.md','generate_inputs.mjs','generator/generate.mjs','generator/jpeg-js-0.4.4.mjs','generator/png.mjs','generator/JPEG-JS-LICENSE']:
 (out/name).parent.mkdir(parents=True,exist_ok=True);(out/name).write_bytes(get(name))
cases=[c for c in m['cases'] if a.section=='all' or c['challenge']==a.section]
if not cases:raise SystemExit('Unknown/empty section')
ids=sorted({s['assetId'] for c in cases for s in c['generator']['sources']} if all('generator' in c for c in cases) else {c['assetId'] for c in cases})
print('Source photos:',len(ids),'bytes:',sum(m['assets'][i]['bytes'] for i in ids))
print('Please conserve this site’s free bandwidth: download only what you need and reuse this output folder. Verified photos are skipped.')
for n,id in enumerate(ids):
 asset=m['assets'][id];dest=out/asset['downloadName']
 if dest.exists() and sha(dest.read_bytes())==asset['sha256']:continue
 temp=dest.with_suffix(dest.suffix+'.partial');digest=hashlib.sha256()
 with temp.open('wb') as f:
  for part in asset['parts']:
   data=get(part['url'])
   if sha(data)!=part['sha256']:raise RuntimeError('Part checksum mismatch')
   f.write(data);digest.update(data)
 if digest.hexdigest()!=asset['sha256'] or temp.stat().st_size!=asset['bytes']:raise RuntimeError('File checksum mismatch')
 temp.replace(dest);print(n+1,'/',len(ids),dest.name,flush=True)
print('Complete. Render verified PNG inputs with: node',out/'generate_inputs.mjs','--dir',out,'--section',a.section)
