"""Lernskript: python kachelwerkstatt.py bild.jpg ausgabe
Voraussetzung: Python 3 und Pillow (python -m pip install Pillow).
Nur vorhandene Bilder zerlegen; keine Bildgenerierung, kein Cloud-Aufruf.
"""
import argparse,json,hashlib
from pathlib import Path
from PIL import Image,ImageDraw,ImageChops

def main():
 p=argparse.ArgumentParser(description=__doc__); p.add_argument('bild',type=Path);p.add_argument('ausgabe',type=Path);p.add_argument('--kern',type=int,default=400);p.add_argument('--rand',type=int,default=64);a=p.parse_args()
 if a.kern<1 or a.rand<0: p.error('Kern > 0, Rand >= 0 erforderlich')
 if a.ausgabe.exists(): p.error('Ausgabeordner existiert bereits; neuen Namen verwenden')
 im=Image.open(a.bild).convert('RGB')
 if im.width*im.height>40_000_000: p.error('Lernskript auf max. 40 MP begrenzt; kleinere Übungskopie verwenden')
 a.ausgabe.mkdir(parents=True); rows=[]; stitched=Image.new('RGB',im.size); overview=im.copy();d=ImageDraw.Draw(overview)
 for ri,y in enumerate(range(0,im.height,a.kern),1):
  for ci,x in enumerate(range(0,im.width,a.kern),1):
   core=[x,y,min(x+a.kern,im.width),min(y+a.kern,im.height)]
   crop=[max(0,x-a.rand),max(0,y-a.rand),min(im.width,core[2]+a.rand),min(im.height,core[3]+a.rand)]
   name=f'R{ri:02}C{ci:02}'; tile=im.crop(crop);tile.save(a.ausgabe/f'{name}.png')
   local=[core[0]-crop[0],core[1]-crop[1],core[2]-crop[0],core[3]-crop[1]]
   stitched.paste(tile.crop(local),(x,y)); rows.append(dict(id=name,file=name+'.png',core=core,crop=crop,local_core=local))
   d.rectangle([core[0],core[1],core[2]-1,core[3]-1],outline='#ffc34a',width=3);d.text((x+8,y+8),name,fill='white',stroke_width=1,stroke_fill='black')
 stitched.save(a.ausgabe/'Rueckmontage.png'); overview.save(a.ausgabe/'Raster.png')
 equal=ImageChops.difference(im,stitched).getbbox() is None
 data=dict(source=str(a.bild.resolve()),pixels=im.size,coordinates='right/bottom exclusive',tiles=rows,pixel_equal=equal)
 (a.ausgabe/'manifest.json').write_text(json.dumps(data,indent=2))
 assert equal,'Rückmontage stimmt nicht überein'
 print(f'{len(rows)} Kacheln; Rückmontage pixelgleich: {equal}')
if __name__=='__main__':main()
