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

SRC='/home/spazio/.hermes/cache/images/img_3c9ded162b40.jpg'
OUT='/home/spazio/cosmos_luminosity_reality_glitch_post01.jpg'
W,H=1080,1350
FONT='/home/spazio/.fonts/cl/Manrope.ttf'
SERIF='/home/spazio/.fonts/cl/PlayfairDisplay-Bold.ttf'
VIOLET=(187,104,255,255); YELLOW=(250,216,60,255); GOLD=(214,175,91,255); WHITE=(250,247,240,255)

src=Image.open(SRC).convert('RGB')
# crop to exact 4:5 while preserving the whole central body and both flowers
sw,sh=src.size
target=W/H
if sw/sh>target:
    nw=int(sh*target); left=(sw-nw)//2; src=src.crop((left,0,left+nw,sh))
else:
    nh=int(sw/target); top=max(0,(sh-nh)//2); src=src.crop((0,top,sw,top+nh))
src=src.resize((W,H),Image.Resampling.LANCZOS)
# Gentle low-light cleanup before grading; blend preserves facial detail.
clean=src.filter(ImageFilter.MedianFilter(3))
src=Image.blend(src,clean,.34)

# Premium night grade: deeper indigo shadows, controlled saturation, luminous installations.
im=ImageEnhance.Color(src).enhance(.90)
im=ImageEnhance.Contrast(im).enhance(1.22)
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))
        # violet-blue night shadows
        r=max(0,min(255,int(r+17*s))); g=max(0,min(255,int(g-8*s))); b=max(0,min(255,int(b+37*s)))
        # warm highlights retained
        p[x,y]=(r,g,b)
im=im.convert('RGBA')

# Luminous bloom from existing white installations/highlights.
mask=Image.new('L',(W,H),0); mp=mask.load(); sp=src.load()
for y in range(H):
    for x in range(W):
        r,g,b=sp[x,y]
        if min(r,g,b)>178 and max(r,g,b)-min(r,g,b)<48:
            mp[x,y]=min(255,int((min(r,g,b)-160)*3))
blur=mask.filter(ImageFilter.GaussianBlur(18)).point(lambda a:int(a*.25))
bloom=Image.new('RGBA',(W,H),(184,135,255,0)); bloom.putalpha(blur)
im=Image.alpha_composite(im,bloom)

# restrained chromatic displacement: edges feel unstable, subject stays readable
rgb=im.convert('RGB'); rr,gg,bb=rgb.split()
rr=ImageChops.offset(rr,6,0); bb=ImageChops.offset(bb,-7,0)
chrom=Image.merge('RGB',(rr,gg,bb)).convert('RGBA')
im=Image.blend(im,chrom,.17)

# Intentional reality slices live around the model, keeping face/body coherent.
slices=[(150,16,-22,120),(535,21,25,125),(720,13,-18,110),(910,22,24,110),(1080,15,-20,100)]
for y,h,dx,alpha in slices:
    lay=Image.new('RGBA',(W,H),(0,0,0,0))
    for x1,x2 in [(0,330),(760,W)]:
        strip=im.crop((x1,y,x2,min(H,y+h)))
        strip.putalpha(Image.new('L',strip.size,alpha))
        lay.paste(strip,(x1+dx,y),strip)
    im=Image.alpha_composite(im,lay)

fx=Image.new('RGBA',(W,H),(0,0,0,0)); d=ImageDraw.Draw(fx)
# Broken probability brackets stay outside the headline and body.
d.arc((8,300,390,1050),145,218,fill=VIOLET,width=3)
d.arc((690,300,1072,1050),322,395,fill=YELLOW,width=3)
d.arc((24,340,360,1010),150,210,fill=GOLD,width=1)
d.arc((720,340,1056,1010),330,390,fill=GOLD,width=1)

# version windows in negative-space zones
windows=[(40,338,192,492,'V.01',VIOLET),(45,835,192,1000,'V.02',GOLD)]
small=ImageFont.truetype(FONT,15)
for x1,y1,x2,y2,label,col in windows:
    d.rectangle((x1,y1,x2,y2),outline=col,width=2)
    d.line((x1,y1+27,x2,y1+27),fill=col,width=1)
    d.text((x1+9,y1+5),label,font=small,fill=col)
    # internal probability trace
    d.line((x1+12,y2-30,x1+42,y2-55,x1+75,y2-42,x2-14,y2-75),fill=(*col[:3],130),width=1)

# scan/cut markers around frame
for yy,col in [(330,VIOLET),(620,GOLD),(1048,YELLOW)]:
    d.line((28,yy,172,yy),fill=col,width=2)
    d.line((908,yy,1052,yy),fill=col,width=2)
    d.rectangle((28,yy-3,34,yy+3),fill=col); d.rectangle((1046,yy-3,1052,yy+3),fill=col)

# sparse interface particles
random.seed(31)
for _ in range(18):
    x=random.randint(35,W-35); y=random.randint(120,H-110)
    if 340<x<740 and 190<y<1040: continue
    r=random.choice([1,1,2]); col=random.choice([(161,84,238,125),(248,211,54,135),(214,175,91,110)])
    d.ellipse((x-r,y-r,x+r,y+r),fill=col)

# header / manifesto headline. Dark translucent upper veil for readability, no opaque card.
veil=Image.new('RGBA',(W,H),(0,0,0,0)); vp=veil.load()
for y in range(H):
    a=0
    if y<300: a=max(a,int(180*(1-y/300)))
    if y>1248: a=max(a,int(225*((y-1248)/(H-1248))))
    if a:
        for x in range(W): vp[x,y]=(10,3,22,a)
fx=Image.alpha_composite(veil,fx)
d=ImageDraw.Draw(fx)
fmeta=ImageFont.truetype(FONT,18)
HEAD='/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf'
fhead=ImageFont.truetype(HEAD,53)
fhead2=ImageFont.truetype(HEAD,57)
d.text((55,42),'COSMOS LUMINOSITY  /  REALITY GLITCH',font=fmeta,fill=GOLD)
d.text((1018,42),'01 / 07',font=fmeta,fill=WHITE,anchor='ra')
d.line((55,78,1018,78),fill=(214,175,91,125),width=1)
d.text((70,101),'ТЫ ВИДИШЬ',font=fhead,fill=WHITE)
d.text((70,157),'ТОЛЬКО ОДНУ',font=fhead2,fill=VIOLET)
d.text((70,220),'ВЕРСИЮ?',font=fhead2,fill=YELLOW)

# bottom microcopy only; avoid competing paragraph on visual
fsmall=ImageFont.truetype(FONT,17)
d.line((55,1270,1018,1270),fill=(214,175,91,120),width=1)
d.text((55,1290),'MULTIPLE VERSIONS ONLINE',font=fsmall,fill=GOLD)
d.text((1018,1290),'A LIVING INTERFACE OF ATTENTION',font=fsmall,fill=WHITE,anchor='ra')

soft=fx.filter(ImageFilter.GaussianBlur(8)); soft.putalpha(fx.getchannel('A').point(lambda a:int(a*.20)))
im=Image.alpha_composite(im,soft); im=Image.alpha_composite(im,fx)
im.convert('RGB').save(OUT,quality=96,subsampling=0)
print(OUT, im.size)
