"""Render an original concept film, not a browser recording. No audio."""
from pathlib import Path
import argparse, json, math, subprocess
import numpy as np
from PIL import Image, ImageDraw, ImageFont, ImageFilter

parser = argparse.ArgumentParser()
parser.add_argument('--output', required=True)
parser.add_argument('--title-font', required=True)
parser.add_argument('--body-font', required=True)
args = parser.parse_args()
root = Path(__file__).resolve().parents[1]
width, height, fps, seconds = 720, 1280, 24, 16
config = json.loads(subprocess.check_output([
    'node', '--input-type=module', '-e',
    "import {cinemaConfigs} from './public/shared/cinema-data.js';console.log(JSON.stringify(cinemaConfigs.moonthorn))"
], cwd=root))
main = Image.open(root/'public/assets/cinema-moonthorn.webp').convert('RGBA')
book = Image.open(root/'public/assets/cinema-moonbook.webp').convert('RGBA')
title_font = ImageFont.truetype(args.title_font, 85)
small_font = ImageFont.truetype(args.body_font, 14)
body_font = ImageFont.truetype(args.body_font, 20)
brand_font = ImageFont.truetype(args.title_font, 38)
page_font = ImageFont.truetype(args.title_font, 22)

def sample(track, progress):
    p = max(0, min(1, progress))
    if p <= track[0][0]: return track[0][1]
    for (a, av), (b, bv) in zip(track, track[1:]):
        if p <= b: return av+(bv-av)*(p-a)/(b-a)
    return track[-1][1]

def opacity(index, p):
    tracks = [
        [(0,1),(.18,1),(.30,0),(1,0)],
        [(0,0),(.30,0),(.40,1),(.60,1),(.72,0),(1,0)],
        [(0,0),(.72,0),(.84,1),(1,1)]
    ]
    return sample(tracks[index], p)

def place(canvas, asset, center, box, scale=1, angle=0, alpha=1):
    if alpha < .005: return
    fit = min(box[0]/asset.width, box[1]/asset.height)*scale
    size = max(1, round(asset.width*fit)), max(1, round(asset.height*fit))
    image = asset.resize(size, Image.Resampling.BICUBIC)
    if angle: image = image.rotate(-angle, Image.Resampling.BICUBIC, expand=True)
    if alpha < .999: image.putalpha(image.getchannel('A').point(lambda n: round(n*alpha)))
    canvas.alpha_composite(image, (round(center[0]-image.width/2), round(center[1]-image.height/2)))

yy, xx = np.mgrid[0:height, 0:width]
distance = ((xx-width*.72)/(width*.8))**2+((yy-height*.43)/(height*.57))**2
glow = np.clip(1-distance,0,1)[...,None]
base0=np.full((height,width,3),[26,17,34],dtype=float)
base1=np.full((height,width,3),[47,24,55],dtype=float)
base0+=glow*np.array([25,13,24]);base1+=glow*np.array([26,14,23])
rng=np.random.default_rng(12);grain=rng.normal(0,.8,(height,width,1))
titles = [
    ['Hay otra forma','de habitar','lo cotidiano.'],
    ['Sigue la luz.','Lo demás','espera.'],
    ['Tu próxima historia','empieza en','una página.']
]
copy = [
    ['Objetos, historias y un poco de magia.','Un universo muy tuyo.'],
    ['Un respiro entre el ruido de afuera','y las historias que llevas dentro.'],
    ['Diario de una bruja moderna.','Descubre el universo Moonthorn.']
]
out = Path(args.output);out.parent.mkdir(parents=True,exist_ok=True)
command=['ffmpeg','-v','error','-y','-f','rawvideo','-pix_fmt','rgb24','-s',f'{width}x{height}','-r',str(fps),'-i','-','-an','-c:v','libx264','-preset','fast','-crf','19','-pix_fmt','yuv420p','-movflags','+faststart',str(out)]
process=subprocess.Popen(command,stdin=subprocess.PIPE)
try:
    for frame in range(fps*seconds):
        p=frame/(fps*seconds-1)
        middle=sample([(0,0),(.22,0),(.37,1),(.64,1),(.8,0),(1,0)],p)
        background=np.clip(base0*(1-middle)+base1*middle+grain,0,255).astype(np.uint8)
        canvas=Image.fromarray(background).convert('RGBA')
        drawing=ImageDraw.Draw(canvas)
        drawing.ellipse((width*.19,height*.28,width*1.16,height*.98),outline=(104,74,111,120),width=1)
        drawing.ellipse((width*.16,height*.29,width*1.19,height*.97),outline=(77,53,88,120),width=1)
        drawing.text((width/2,45),'moonthorn',font=brand_font,anchor='mt',fill=(228,207,223))
        drawing.text((width/2,95),'OBJECTS, STORIES & A LITTLE MAGIC',font=small_font,anchor='mt',fill=(166,137,161))
        for i in range(18):
            angle=i/18*math.pi*2+p*.3
            spread=sample([(0,0),(.18,0),(.38,1),(.65,1),(.85,.1),(1,0)],p)
            x=width*.5+math.cos(angle)*width*.3*spread
            y=height*.66+math.sin(angle)*height*.19*spread
            radius=1.2+(i%3)*.6
            drawing.ellipse((x-radius,y-radius,x+radius,y+radius),fill=(218,184,201,round(spread*170)))
        pose={**config['pose'],**config['mobile']}
        values={key:sample(track,p) for key,track in pose.items()}
        place(canvas,main,(width*.5+values['x']/100*width,height*.65+values['y']/100*height),(width*.82,height*.59),values['scale'],values['rotate'],values.get('opacity',1))
        amount=sample([(0,0),(.52,0),(.66,1),(1,1)],p)
        place(canvas,book,(width*.5,height*.74),(width*1.04,height*.44),.45+amount*.65,(1-amount)*-15-3,amount)
        wash=sample([(0,0),(.35,0),(.48,.85),(.62,0),(1,0)],p)
        if wash>0:
            smoke=Image.new('RGBA',(width,height),(0,0,0,0));smoked=ImageDraw.Draw(smoke)
            for j in range(4):
                rx=width*(.12+j*.1+wash*.45);ry=height*(.1+j*.09+wash*.3)
                smoked.ellipse((width*.5-rx,height*.6-ry,width*.5+rx,height*.6+ry),fill=(193+j*10,157+j*10,185+j*9,round(wash*(28-j*4))))
            canvas=Image.alpha_composite(canvas,smoke.filter(ImageFilter.GaussianBlur(35)))
        for i in range(3):
            a=opacity(i,p)
            if a<.005:continue
            text_layer=Image.new('RGBA',(width,height),(0,0,0,0));d=ImageDraw.Draw(text_layer)
            d.text((48,166),f'CHAPITRE 0{i+1} / UN PETIT RITUEL',font=small_font,fill=(181,151,173,round(a*255)))
            y=217+(1-a)*18
            for j,line in enumerate(titles[i]):
                font=title_font if len(line)<19 else ImageFont.truetype(args.title_font,72)
                d.text((46,y+j*84),line,font=font,fill=((*((218,173,202) if j==2 else (237,223,212)),round(a*255))))
            for j,line in enumerate(copy[i]):d.text((50,y+len(titles[i])*84+23+j*33),line,font=body_font,fill=(184,162,183,round(a*255)))
            canvas=Image.alpha_composite(canvas,text_layer)
        d=ImageDraw.Draw(canvas)
        d.text((48,height-82),'FILM DE CONCEPTO / LEMONLABS',font=small_font,fill=(151,125,149))
        d.text((width-48,height-82),f'{frame/fps:04.1f} / 16.0',font=small_font,anchor='ra',fill=(151,125,149))
        d.line((0,height-2,width*p,height-2),fill=(218,174,201),width=3)
        process.stdin.write(canvas.convert('RGB').tobytes())
        if frame%96==0:print(f'{frame}/{fps*seconds} frames',flush=True)
finally:
    process.stdin.close()
if process.wait()!=0:raise SystemExit('ffmpeg failed')
print(out,flush=True)
