#!/usr/bin/env python3
"""Writes the game's web site (site/, one page per language) from the game's own texts and screenshots:
tools/site/shots.sh && python3 tools/site/build.py"""
import html, json, os, shutil, sys
from PIL import Image
HERE = os.path.dirname(os.path.abspath(__file__))
GAME = os.path.join(HERE, '..', '..')
sys.path.insert(0, os.path.join(HERE, '..'))
from lang import KEYS, T, DESC

SITE = os.path.join(GAME, 'site')
SHOTS = os.path.join(GAME, 'var', 'screenshots', 'site')
MAIN = 'en'
OG = {'en': 'en_US', 'fr': 'fr_FR', 'es': 'es_ES', 'de': 'de_DE', 'it': 'it_IT', 'pt': 'pt_BR', 'nl': 'nl_NL', 'pl': 'pl_PL', 'ru': 'ru_RU', 'uk': 'uk_UA',
      'tr': 'tr_TR', 'ja': 'ja_JP', 'zh': 'zh_CN', 'ko': 'ko_KR'}
# [page title, genre, how to play, the 8 skills, worlds heading, controls, features, free line]
X = {
 'en': ['Lumikins – puzzle game, play online free', 'Puzzle game', 'How to play', 'The 8 skills', '24 levels, 11 worlds',
        'Mouse or finger: pick a skill, then a Lumikin. Drag or arrow keys to scroll. Keys 1–8: skills, F: fast, P: pause.',
        'HD or retro graphics · original music · 14 languages · phone, tablet and computer · installable, plays offline', 'Free, in your browser, nothing to install.'],
 'fr': ['Lumikins – jeu de réflexion, jouer en ligne gratuitement', 'Jeu de réflexion', 'Comment jouer', 'Les 8 compétences', '24 niveaux, 11 mondes',
        'Souris ou doigt : choisissez une compétence, puis un Lumikin. Glissez ou utilisez les flèches pour défiler. Touches 1–8 : compétences, F : accéléré, P : pause.',
        'Graphismes HD ou rétro · musique originale · 14 langues · téléphone, tablette et ordinateur · installable, jouable hors ligne', 'Gratuit, dans votre navigateur, rien à installer.'],
 'es': ['Lumikins – juego de puzles, jugar online gratis', 'Juego de puzles', 'Cómo jugar', 'Las 8 habilidades', '24 niveles, 11 mundos',
        'Ratón o dedo: elige una habilidad y luego un Lumikin. Arrastra o usa las flechas para desplazarte. Teclas 1–8: habilidades, F: rápido, P: pausa.',
        'Gráficos HD o retro · música original · 14 idiomas · móvil, tableta y ordenador · instalable, funciona sin conexión', 'Gratis, en tu navegador, sin instalar nada.'],
 'de': ['Lumikins – Rätselspiel, kostenlos online spielen', 'Rätselspiel', 'So wird gespielt', 'Die 8 Fähigkeiten', '24 Level, 11 Welten',
        'Maus oder Finger: Fähigkeit wählen, dann einen Lumikin. Ziehen oder Pfeiltasten zum Scrollen. Tasten 1–8: Fähigkeiten, F: schnell, P: Pause.',
        'HD- oder Retro-Grafik · eigene Musik · 14 Sprachen · Handy, Tablet und Computer · installierbar, offline spielbar', 'Kostenlos im Browser, ohne Installation.'],
 'it': ['Lumikins – gioco di logica, gioca online gratis', 'Gioco di logica', 'Come si gioca', 'Le 8 abilità', '24 livelli, 11 mondi',
        'Mouse o dito: scegli un\'abilità, poi un Lumikin. Trascina o usa le frecce per scorrere. Tasti 1–8: abilità, F: veloce, P: pausa.',
        'Grafica HD o retrò · musica originale · 14 lingue · telefono, tablet e computer · installabile, giocabile offline', 'Gratis, nel browser, niente da installare.'],
 'pt': ['Lumikins – jogo de quebra-cabeça, jogue online grátis', 'Jogo de quebra-cabeça', 'Como jogar', 'As 8 habilidades', '24 níveis, 11 mundos',
        'Mouse ou dedo: escolha uma habilidade e depois um Lumikin. Arraste ou use as setas para rolar. Teclas 1–8: habilidades, F: rápido, P: pausa.',
        'Gráficos HD ou retrô · música original · 14 idiomas · celular, tablet e computador · instalável, funciona offline', 'Grátis, no navegador, sem instalar nada.'],
 'nl': ['Lumikins – puzzelspel, gratis online spelen', 'Puzzelspel', 'Zo speel je', 'De 8 vaardigheden', '24 levels, 11 werelden',
        'Muis of vinger: kies een vaardigheid en dan een Lumikin. Sleep of gebruik de pijltjes om te scrollen. Toetsen 1–8: vaardigheden, F: snel, P: pauze.',
        'HD- of retrobeeld · eigen muziek · 14 talen · telefoon, tablet en computer · installeerbaar, offline speelbaar', 'Gratis in je browser, niets te installeren.'],
 'pl': ['Lumikins – gra logiczna, graj online za darmo', 'Gra logiczna', 'Jak grać', '8 umiejętności', '24 poziomy, 11 światów',
        'Mysz lub palec: wybierz umiejętność, potem Lumikina. Przeciągnij lub użyj strzałek, by przewijać. Klawisze 1–8: umiejętności, F: szybko, P: pauza.',
        'Grafika HD lub retro · oryginalna muzyka · 14 języków · telefon, tablet i komputer · do zainstalowania, działa offline', 'Za darmo, w przeglądarce, bez instalacji.'],
 'ru': ['Lumikins – головоломка, играть онлайн бесплатно', 'Головоломка', 'Как играть', '8 умений', '24 уровня, 11 миров',
        'Мышь или палец: выберите умение, затем Люмикина. Тяните или жмите стрелки для прокрутки. Клавиши 1–8: умения, F: быстро, P: пауза.',
        'Графика HD или ретро · своя музыка · 14 языков · телефон, планшет и компьютер · можно установить, работает офлайн', 'Бесплатно, в браузере, без установки.'],
 'uk': ['Lumikins – головоломка, грати онлайн безкоштовно', 'Головоломка', 'Як грати', '8 умінь', '24 рівні, 11 світів',
        'Миша або палець: оберіть уміння, потім Люмікіна. Тягніть або тисніть стрілки для прокрутки. Клавіші 1–8: уміння, F: швидко, P: пауза.',
        'Графіка HD або ретро · власна музика · 14 мов · телефон, планшет і комп\'ютер · можна встановити, працює офлайн', 'Безкоштовно, у браузері, без встановлення.'],
 'tr': ['Lumikins – bulmaca oyunu, ücretsiz çevrimiçi oyna', 'Bulmaca oyunu', 'Nasıl oynanır', '8 yetenek', '24 bölüm, 11 dünya',
        'Fare ya da parmak: bir yetenek, sonra bir Lumikin seç. Kaydırmak için sürükle ya da ok tuşlarını kullan. 1–8: yetenekler, F: hızlı, P: duraklat.',
        'HD ya da retro grafik · özgün müzik · 14 dil · telefon, tablet ve bilgisayar · yüklenebilir, çevrimdışı oynanır', 'Ücretsiz, tarayıcında, kurulum yok.'],
 'ja': ['Lumikins – パズルゲーム、無料でオンラインプレイ', 'パズルゲーム', 'あそびかた', '8つのスキル', '24ステージ、11の世界',
        'マウスか指でスキルを選び、ルミキンをタップ。ドラッグか矢印キーでスクロール。1–8: スキル、F: 早送り、P: ポーズ。',
        'HD / レトロ画面 · オリジナル音楽 · 14言語 · スマホ、タブレット、PC · インストール可、オフライン対応', '無料、ブラウザですぐ遊べます。'],
 'zh': ['Lumikins – 益智游戏, 免费在线玩', '益智游戏', '玩法', '8 种技能', '24 个关卡, 11 个世界',
        '用鼠标或手指选择技能, 再点一个小灯灵。拖动或用方向键滚动。1–8: 技能, F: 加速, P: 暂停。',
        '高清或复古画面 · 原创音乐 · 14 种语言 · 手机、平板和电脑 · 可安装, 可离线游玩', '免费, 打开浏览器就能玩。'],
 'ko': ['Lumikins – 퍼즐 게임, 무료 온라인 플레이', '퍼즐 게임', '플레이 방법', '8가지 기술', '24개 레벨, 11개 세계',
        '마우스나 손가락으로 기술을 고르고 루미킨을 누르세요. 드래그하거나 방향키로 스크롤. 1–8: 기술, F: 빠르게, P: 일시정지.',
        'HD 또는 레트로 그래픽 · 오리지널 음악 · 14개 언어 · 휴대폰, 태블릿, 컴퓨터 · 설치 가능, 오프라인 플레이', '무료, 브라우저에서 바로.'],
}
SKILLS = ['climb', 'glide', 'boom', 'stop', 'bridge', 'bash', 'mine', 'dig']
WORLDS = [('forest', 'MIND THE GAP'), ('cave', 'DEEP DOWN'), ('forge', 'LAVA WORKS'), ('night', 'THE LONG WAY HOME')]

CSS = """
:root{--bg:#0c0818;--card:#1c1438;--rim:#3c2c6c;--gold:#ffd23c;--ink:#f4efff;--dim:#b0a6d6}
*{box-sizing:border-box}html{scroll-behavior:smooth}
body{margin:0;background:var(--bg);color:var(--ink);font:17px/1.6 system-ui,-apple-system,"Segoe UI",Roboto,"Noto Sans",sans-serif;overflow-x:hidden}
a{color:var(--gold)}img{max-width:100%;height:auto;display:block}
.wrap{max-width:1080px;margin:0 auto;padding:0 16px}
.hero{position:relative;text-align:center;padding:28px 0 56px;background:#161a44 url(IMG/bg.jpg) center bottom/cover no-repeat}
.hero:after{content:"";position:absolute;inset:auto 0 0 0;height:120px;background:linear-gradient(transparent,var(--bg))}
.hero>*{position:relative;z-index:1}
.logo{width:min(560px,88%);max-width:100%;margin:0 auto;filter:drop-shadow(0 0 28px #ffaa3c88)}
h1{font-size:clamp(1.3rem,4vw,2rem);margin:.2em 0 .1em;font-weight:800;text-shadow:0 2px 0 #1c1230}
.sub{color:var(--dim);margin:0 auto 22px;max-width:640px}
.play{display:inline-block;padding:16px 46px;border-radius:14px;font-size:1.35rem;font-weight:800;color:#3c1404;text-decoration:none;
 background:linear-gradient(#ffe08a,#ffb347 45%,#e0641c);box-shadow:0 6px 0 #8c2a12,0 0 40px #ffb34799;transition:transform .12s}
.play:hover{transform:translateY(-2px) scale(1.03)}.play:active{transform:translateY(4px);box-shadow:0 2px 0 #8c2a12}
.shot{margin:34px auto 0;width:100%;max-width:min(880px,100%);border-radius:14px;border:2px solid var(--rim);box-shadow:0 20px 60px #000a,0 0 60px #7c64b044;overflow:hidden}
.walk{display:flex;justify-content:center;gap:min(10px,2vw);margin-top:22px}
.walk img{width:min(48px,11vw);height:auto;image-rendering:pixelated;animation:bob .7s infinite alternate ease-in-out}
.walk img:nth-child(2n){animation-delay:.25s}.walk img:nth-child(3n){animation-delay:.45s}
@keyframes bob{to{transform:translateY(-7px)}}
h2{font-size:clamp(1.4rem,3.6vw,2rem);text-align:center;margin:64px 0 22px;color:var(--gold)}
.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(230px,1fr))}
.card{background:var(--card);border:1px solid var(--rim);border-radius:14px;padding:14px 16px;display:flex;gap:12px;align-items:center}
.card img{width:36px;height:36px;image-rendering:pixelated;flex:none}
.steps{counter-reset:s;display:grid;gap:12px;max-width:760px;margin:0 auto;padding:0;list-style:none}
.steps li{background:var(--card);border-left:4px solid var(--gold);border-radius:10px;padding:12px 16px 12px 54px;position:relative}
.steps li:before{counter-increment:s;content:counter(s);position:absolute;left:16px;top:10px;font-weight:800;font-size:1.3rem;color:var(--gold)}
.worlds{display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(230px,1fr))}
.worlds figure{margin:0;border-radius:14px;overflow:hidden;border:1px solid var(--rim);background:var(--card)}
.worlds figcaption{padding:10px 14px;font-weight:700}
.note{text-align:center;color:var(--dim);max-width:760px;margin:22px auto}
.cta{text-align:center;margin:56px 0 30px}
.langs{display:flex;flex-wrap:wrap;gap:8px;justify-content:center;padding:0;margin:26px 0;list-style:none;font-size:.9rem}
.langs a{display:block;padding:4px 10px;border:1px solid var(--rim);border-radius:99px;text-decoration:none;color:var(--dim)}
.langs a[aria-current]{color:var(--bg);background:var(--gold);border-color:var(--gold)}
footer{text-align:center;color:var(--dim);font-size:.9rem;padding:10px 16px 50px}
"""


def e(s):
    return html.escape(s, quote=True)


def page(lang):
    t = dict(zip(KEYS, T[lang]))
    title, genre, how, skills_h, worlds_h, controls, features, free = X[lang]
    up = '' if lang == MAIN else '../'
    img = up + 'images/'
    url = 'https://{{DOMAIN}}/' + ('' if lang == MAIN else lang + '/')
    play = up + 'play/' + ('' if lang == MAIN else '?back=../' + lang + '/')
    alts = ''.join(f'<link rel="alternate" hreflang="{c}" href="https://{{{{DOMAIN}}}}/{"" if c == MAIN else c + "/"}">\n' for c in T)
    alts += '<link rel="alternate" hreflang="x-default" href="https://{{DOMAIN}}/">\n'
    ld = {'@context': 'https://schema.org', '@type': 'VideoGame', 'name': 'Lumikins', 'description': DESC[lang], 'genre': genre, 'inLanguage': lang,
          'gamePlatform': ['Web browser', 'Android', 'iOS', 'Windows', 'Linux', 'macOS'], 'applicationCategory': 'Game', 'isAccessibleForFree': True,
          'image': 'https://{{DOMAIN}}/images/og.png', 'screenshot': [f'https://{{{{DOMAIN}}}}/images/shots/{w}.jpg' for w, _ in WORLDS], 'url': url,
          'offers': {'@type': 'Offer', 'price': '0', 'priceCurrency': 'EUR'},
          'potentialAction': {'@type': 'PlayAction', 'target': 'https://{{DOMAIN}}/play/'}}
    langs = ''.join(f'<li><a href="{up}{"" if c == MAIN else c + "/"}" hreflang="{c}" lang="{c}"{" aria-current=\"page\"" if c == lang else ""}>{e(dict(zip(KEYS, T[c]))["LANGNAME"])}</a></li>' for c in T)
    skills = ''.join(f'<div class="card"><img src="{img}skill-{s}.png" alt="" width="36" height="36"><span>{e(t["SKILL " + s])}</span></div>' for s in SKILLS)
    steps = ''.join(f'<li>{e(t[k])}</li>' for k in ('HINT DIG', 'HINT BRIDGE', 'HINT GLIDE', 'HINT BOOM'))
    worlds = ''.join(f'<figure><img src="{img}shots/{w}.jpg" alt="Lumikins – {e(t[name])}" width="960" height="540" loading="lazy"><figcaption>{e(t[name])}</figcaption></figure>' for w, name in WORLDS)
    walkers = ''.join(f'<img src="{img}lumi.png" alt="" width="48" height="48">' for _ in range(7))
    return f"""<!doctype html>
<html lang="{lang}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>{e(title)}</title>
<meta name="description" content="{e(DESC[lang])}">
<link rel="canonical" href="{url}">
{alts}<meta property="og:type" content="website">
<meta property="og:title" content="{e(title)}">
<meta property="og:description" content="{e(DESC[lang])}">
<meta property="og:url" content="{url}">
<meta property="og:image" content="https://{{{{DOMAIN}}}}/images/og.png">
<meta property="og:locale" content="{OG[lang]}">
<meta name="twitter:card" content="summary_large_image">
<meta name="theme-color" content="#1c1438">
<link rel="icon" type="image/png" href="{img}favicon.png">
<style>{CSS.replace('IMG/', img)}</style>
<script type="application/ld+json">{json.dumps(ld, ensure_ascii=False)}</script>
</head>
<body>
<header class="hero"><div class="wrap">
<img class="logo" src="{img}logo.png" alt="Lumikins" width="1260" height="444">
<h1>{e(t['TAGLINE'])}</h1>
<p class="sub">{e(DESC[lang])}</p>
<a class="play" href="{play}">▶ {e(t['PLAY'])}</a>
<div class="walk" aria-hidden="true">{walkers}</div>
<img class="shot" src="{img}shots/title.jpg" alt="Lumikins – {e(t['TAGLINE'])}" width="960" height="540">
</div></header>
<main class="wrap">
<h2>{e(how)}</h2>
<ol class="steps">{steps}</ol>
<p class="note">{e(controls)}</p>
<h2>{e(skills_h)}</h2>
<div class="grid">{skills}</div>
<h2>{e(worlds_h)}</h2>
<div class="worlds">{worlds}</div>
<p class="note">{e(features)}</p>
<div class="cta"><a class="play" href="{play}">▶ {e(t['PLAY'])}</a><p class="note">{e(free)}</p></div>
<nav aria-label="Languages"><ul class="langs">{langs}</ul></nav>
</main>
<footer>Lumikins · {e(t['CREDITS'])} · <a href="https://jumphp.net">jumphp.net</a></footer>
</body>
</html>
"""


def images():
    im = os.path.join(SITE, 'images')
    os.makedirs(os.path.join(im, 'shots'), exist_ok=True)
    for name in ('title', 'forest', 'cave', 'forge', 'night', 'boom', 'stairs', 'levels'):
        Image.open(os.path.join(SHOTS, name + '.png')).convert('RGB').save(os.path.join(im, 'shots', name + '.jpg'), quality=88)
    a = os.path.join(GAME, 'assets')
    shutil.copy(os.path.join(a, 'hd', 'sprites', 'logo.png'), os.path.join(im, 'logo.png'))
    Image.open(os.path.join(a, 'sprites', 'lumi.png')).crop((0, 0, 16, 16)).resize((96, 96), Image.NEAREST).save(os.path.join(im, 'lumi.png'))
    Image.open(os.path.join(a, 'icon.png')).resize((64, 64), Image.NEAREST).save(os.path.join(im, 'favicon.png'))
    names = json.load(open(os.path.join(a, 'icons.json')))
    sheet = Image.open(os.path.join(a, 'sprites', 'icons-gold.png'))
    for s in SKILLS:
        i = names.index(s)
        sheet.crop((i * 16, 0, i * 16 + 16, 16)).resize((72, 72), Image.NEAREST).save(os.path.join(im, f'skill-{s}.png'))
    sky = Image.open(os.path.join(a, 'hd', 'bg', 'forest-sky.jpg')).convert('RGBA')
    sky.alpha_composite(Image.open(os.path.join(a, 'hd', 'bg', 'forest-far.png')).crop((0, 0, 1440, 696)))
    sky.convert('RGB').save(os.path.join(im, 'bg.jpg'), quality=85)
    # og.png 1200 x 630 and the directory's thumbnail (16:9): a screenshot under the logo
    shot = Image.open(os.path.join(SHOTS, 'forest.png')).convert('RGBA')
    logo = Image.open(os.path.join(a, 'hd', 'sprites', 'logo.png'))
    og = shot.resize((1200, 675), Image.LANCZOS).crop((0, 30, 1200, 660))
    og.alpha_composite(Image.new('RGBA', og.size, (12, 8, 24, 70)))
    lg = logo.resize((900, int(900 * logo.height / logo.width)), Image.LANCZOS)
    og.alpha_composite(lg, (150, 20))
    og.convert('RGB').save(os.path.join(im, 'og.png'))
    th = shot.resize((1280, 720), Image.LANCZOS)
    lg = logo.resize((1000, int(1000 * logo.height / logo.width)), Image.LANCZOS)
    th.alpha_composite(lg, (140, 30))
    th.convert('RGB').save(os.path.join(a, 'thumbnail.png'))


if __name__ == '__main__':
    images()
    urls = ''
    for lang in T:
        d = SITE if lang == MAIN else os.path.join(SITE, lang)
        os.makedirs(d, exist_ok=True)
        open(os.path.join(d, 'index.html'), 'w', encoding='utf-8').write(page(lang))
        loc = 'https://{{DOMAIN}}/' + ('' if lang == MAIN else lang + '/')
        links = ''.join(f'<xhtml:link rel="alternate" hreflang="{c}" href="https://{{{{DOMAIN}}}}/{"" if c == MAIN else c + "/"}"/>' for c in T)
        urls += f'<url><loc>{loc}</loc>{links}</url>\n'
    open(os.path.join(SITE, 'sitemap.xml'), 'w').write('<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" '
                                                     'xmlns:xhtml="http://www.w3.org/1999/xhtml">\n' + urls + '</urlset>\n')
    open(os.path.join(SITE, 'robots.txt'), 'w').write('User-agent: *\nAllow: /\nSitemap: https://{{DOMAIN}}/sitemap.xml\n')
    print('site written:', len(T), 'languages')
