from PIL import Image, ImageDraw, ImageFont, ImageEnhance, ImageFilter, ImageChops
from pathlib import Path
import random, math

W,H=1080,1350
FONT='/home/spazio/.fonts/cl/Manrope.ttf'
BOLD='/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf'
VIOLET=(187,104,255,255); YELLOW=(250,216,60,255); GOLD=(214,175,91,255); WHITE=(250,247,240,255); PALE=(230,223,234,255)
OUTDIR=Path('/home/spazio/reality_glitch_carousel'); OUTDIR.mkdir(exist_ok=True)

slides=[
('/home/spazio/.hermes/cache/images/img_3c9ded162b40.jpg', [('ТЫ ВИДИШЬ',WHITE),('ТОЛЬКО ОДНУ',VIOLET),('ВЕРСИЮ?',YELLOW)], 'MULTIPLE VERSIONS ONLINE'),
('/home/spazio/.hermes/cache/images/img_efada60f4dcc.jpg', [('ВНИМАНИЕ',VIOLET),('СОБИРАЕТ',WHITE),('ВИДИМОЕ.',YELLOW)], 'WHAT YOU NOTICE BECOMES AVAILABLE'),
('/home/spazio/.hermes/cache/images/img_febc8de5e70b.jpg', [('ТЫ СМОТРИШЬ',WHITE),('ИЗ СВОЕЙ',WHITE),('ВНУТРЕННЕЙ',VIOLET),('СБОРКИ.',YELLOW)], 'INNER CONFIGURATION / OUTER VIEW'),
('/home/spazio/.hermes/cache/images/img_4c8552ed2b86.jpg', [('СТАРЫЙ ВЫВОД',VIOLET),('СУЖАЕТ',WHITE),('ОБЗОР.',YELLOW)], 'PAST CONCLUSION / LIMITED RENDER'),
('/home/spazio/.hermes/cache/images/img_fed88fa195d7.jpg', [('ВОПРОС',YELLOW),('СОЗДАЁТ СБОЙ',WHITE),('В ПРИВЫЧНОМ',WHITE),('РЕНДЕРЕ.',VIOLET)], 'QUESTION / INTERRUPTION / NEW SIGNAL'),
('/home/spazio/.hermes/cache/images/img_58f867ae061d.jpg', [('КАКОЙ ВАРИАНТ',WHITE),('Я ПОКА ДАЖЕ',VIOLET),('НЕ РАССМАТРИВАЮ?',YELLOW)], 'LET ATTENTION MOVE BEFORE ANSWERING'),
('/home/spazio/.hermes/cache/images/img_8ef882e5add5.jpg', [('БОЛЬШЕ ВЕРСИЙ',WHITE),('РЕАЛЬНОСТИ —',VIOLET),('В TELEGRAM-КАНАЛЕ',WHITE),('COSMOS LUMINOSITY',YELLOW)], 'ССЫЛКА В ПРОФИЛЕ'),
]

def crop45(src):
    sw,sh=src.size; target=W/H
    if sw/sh>target:
        nw=int(sh*target); l=(sw-nw)//2; src=src.crop((l,0,l+nw,sh))
    else:
        nh=int(sw/target); t=max(0,(sh-nh)//2); src=src.crop((0,t,sw,t+nh))
    return src.resize((W,H),Image.Resampling.LANCZOS)

def fit(text,maxw,start=58,minsize=30):
    dummy=ImageDraw.Draw(Image.new('RGB',(10,10)))
    for s in range(start,minsize-1,-1):
        f=ImageFont.truetype(BOLD,s)
        if dummy.textbbox((0,0),text,font=f)[2] <= maxw:return f
    return ImageFont.truetype(BOLD,minsize)

def render(index,path,lines,micro):
    raw=crop45(Image.open(path).convert('RGB'))
    raw=Image.blend(raw,raw.filter(ImageFilter.MedianFilter(3)),.28)
    im=ImageEnhance.Color(raw).enhance(.86); im=ImageEnhance.Contrast(im).enhance(1.20); im=ImageEnhance.Brightness(im).enhance(.90)
    p=im.load()
    for y in range(H):
        for x in range(W):
            r,g,b=p[x,y]; lum=(r+g+b)/3; s=max(0,min(1,(155-lum)/155))
            p[x,y]=(max(0,min(255,int(r+17*s))),max(0,min(255,int(g-8*s))),max(0,min(255,int(b+37*s))))
    im=im.convert('RGBA')
    # restrained RGB split
    rr,gg,bb=im.convert('RGB').split(); rr=ImageChops.offset(rr,5,0); bb=ImageChops.offset(bb,-6,0)
    im=Image.blend(im,Image.merge('RGB',(rr,gg,bb)).convert('RGBA'),.13)
    # upper and lower clean veils
    veil=Image.new('RGBA',(W,H),(0,0,0,0)); vp=veil.load()
    for y in range(H):
        a=0
        if y<440:a=int(215*(1-y/480))
        if y>1225:a=max(a,int(230*((y-1225)/125)))
        if a:
            for x in range(W):vp[x,y]=(9,3,21,a)
    im=Image.alpha_composite(im,veil)
    # side-only glitch slices
    for yy,hh,dx,alpha in [(515,16,22,110),(725,12,-18,95),(1010,20,24,100)]:
        lay=Image.new('RGBA',(W,H),(0,0,0,0))
        for x1,x2 in [(0,250),(830,W)]:
            st=im.crop((x1,yy,x2,yy+hh)); st.putalpha(Image.new('L',st.size,alpha)); lay.paste(st,(x1+dx,yy),st)
        im=Image.alpha_composite(im,lay)
    fx=Image.new('RGBA',(W,H),(0,0,0,0)); d=ImageDraw.Draw(fx)
    # exterior broken arcs
    d.arc((-115,380,390,1110),145,215,fill=VIOLET,width=3)
    d.arc((690,330,1195,1060),325,395,fill=YELLOW,width=3)
    # sparse side data marks
    d.rectangle((35,610,170,760),outline=(*VIOLET[:3],170),width=2)
    d.text((47,620),f'V.{index:02}',font=ImageFont.truetype(FONT,14),fill=VIOLET)
    d.line((47,725,76,700,108,714,155,674),fill=(*GOLD[:3],150),width=1)
    for yy,col in [(520,VIOLET),(980,YELLOW)]:
        d.line((25,yy,155,yy),fill=col,width=2); d.line((925,yy,1055,yy),fill=col,width=2)
    # header metadata
    meta=ImageFont.truetype(FONT,17)
    d.text((55,42),'COSMOS LUMINOSITY  /  REALITY GLITCH',font=meta,fill=GOLD)
    d.text((1025,42),f'{index:02} / 07',font=meta,fill=WHITE,anchor='ra')
    d.line((55,78,1025,78),fill=(214,175,91,115),width=1)
    # title
    y=104
    for text,col in lines:
        f=fit(text,940,58 if len(lines)<=3 else 50,28)
        d.text((65,y),text,font=f,fill=col)
        y=d.textbbox((65,y),text,font=f)[3]+2
    # footer bar and microcopy
    d.line((55,1262,1025,1262),fill=(214,175,91,120),width=1)
    fm=ImageFont.truetype(FONT,16 if len(micro)<32 else 14)
    d.text((55,1286),micro,font=fm,fill=GOLD)
    d.text((1025,1286),'A LIVING INTERFACE OF ATTENTION',font=ImageFont.truetype(FONT,14),fill=WHITE,anchor='ra')
    soft=fx.filter(ImageFilter.GaussianBlur(7)); soft.putalpha(fx.getchannel('A').point(lambda a:int(a*.17)))
    im=Image.alpha_composite(im,soft); im=Image.alpha_composite(im,fx)
    out=OUTDIR/f'slide_{index:02}.jpg'; im.convert('RGB').save(out,quality=96,subsampling=0)
    return out

outs=[]
for i,(p,l,m) in enumerate(slides,1):outs.append(render(i,p,l,m))
# contact sheet
thumbs=[]
for p in outs:
    im=Image.open(p); im.thumbnail((270,338)); thumbs.append(im.copy())
sheet=Image.new('RGB',(1080,736),(12,6,22)); dd=ImageDraw.Draw(sheet); lab=ImageFont.truetype(FONT,17)
for i,t in enumerate(thumbs):
    x=(i%4)*270; y=(i//4)*368; sheet.paste(t,(x,y)); dd.text((x+12,y+342),f'{i+1:02}',font=lab,fill=GOLD)
sheet.save(OUTDIR/'contact_sheet.jpg',quality=94)
print('\n'.join(str(x) for x in outs)); print(OUTDIR/'contact_sheet.jpg')
