#!/usr/bin/env python3
"""Tutte le icone dell'app, dal disegno ufficiale di Catch Up.

Il disegno e' `assets/logo/icona-catchup-F.jpg`, la F del logo fra le due
frecce su fondo di carta, mandato da Matthieu il 31/08/2026 insieme al
permesso di usarlo. E' un quadrato di 461 pixel, quindi ogni ingrandimento
parte da li': quando arrivera' l'originale piu' grande basta sostituire il
file e rilanciare questo script.

Le tre regole che decidono tutto, imparate guardando l'icona sul dispositivo:

  1. IL MASTER E' QUADRATO E PIENO. L'arrotondamento lo mette il sistema. Se lo
     incidessimo noi si vedrebbe un angolo dentro l'altro.
  2. iOS ritaglia poco (angoli tondi), quindi il disegno sta all'84% del lato:
     al 100% la maschera mangiava le punte delle frecce.
  3. ANDROID RITAGLIA MOLTO. L'icona adattiva e' alta 108 unita' ma il sistema
     garantisce solo il cerchio centrale da 66, e sul Pixel la maschera E' quel
     cerchio. Le frecce sono la parte piu' larga e stanno in alto e in basso,
     cioe' nel punto peggiore: perche' le loro punte restino dentro, il disegno
     non puo' superare il 53% del lato. Sembra piccolo guardando il file, ed e'
     giusto guardando il telefono.

Sul fondo: lo strato di sfondo dell'icona adattiva e' una TINTA PIENA, non
un'immagine. Prima era l'immagine rimpicciolita, e fuori restava trasparente:
il lanciatore ci metteva un suo colore, e intorno alla F compariva un anello
rosa. La tinta e' quella della carta del disegno, cosi' il quadrato di carta
del primo piano ci si posa sopra senza che si veda l'attaccatura.

Uscita:
  pwa/icons/                     l'icona della web app e le favicon
  assets/store/icon.png          il master 1024 per App Store e Play Store
  android/app/src/main/res/      i cinque formati dell'icona adattiva
"""
from PIL import Image, ImageDraw
import os

RADICE = os.path.dirname(os.path.abspath(__file__))
SORGENTE = os.path.join(RADICE, 'assets/logo/icona-catchup-F.jpg')
CARTA = (248, 248, 246)          # il fondo del disegno, campionato agli angoli

disegno = Image.open(SORGENTE).convert('RGB')

def sporgenza(im):
    """Quanto esce il disegno dal cerchio inscritto, in mezze-larghezze.

    Serve perche' le punte delle frecce stanno in alto e in basso ma sono anche
    larghe: sono il punto piu' lontano dal centro, piu' lontano degli angoli
    della F. Misurarlo e' l'unico modo di sapere quanto rimpicciolire per una
    maschera tonda. Sul file di Catch Up viene 1.22, cioe' il disegno sborda di
    un quinto dal cerchio: a occhio non si direbbe.
    """
    px = im.load(); w, h = im.size
    cx, cy = (w - 1) / 2, (h - 1) / 2
    massimo = 0
    for y in range(h):
        for x in range(w):
            r, g, b = px[x, y]
            if r > 225 and g > 225 and b > 220:      # carta: non e' disegno
                continue
            d = ((x - cx) ** 2 + (y - cy) ** 2) ** 0.5
            if d > massimo:
                massimo = d
    return massimo / cx

SPORGENZA = sporgenza(disegno)

# quanto del lato occupa il disegno, per ciascun sistema
DENTRO_IOS = 0.84                              # angoli tondi: ritagliano poco
# 30 e non 33: la zona sicura garantita e' un cerchio da 66 su 108, ma appoggiare
# le punte delle frecce esattamente sul bordo le fa sembrare tagliate lo stesso.
# Con 30 restano tre unita' d'aria e l'icona respira. Provato sull'emulatore.
DENTRO_ANDROID = round(2 * (30/108) / SPORGENZA, 3)
DENTRO_TONDA = round(2 * 0.47 / SPORGENZA, 3)         # dentro il cerchio, con un filo d'aria
print(f'sporgenza {SPORGENZA:.3f} -> android {DENTRO_ANDROID}, tonda {DENTRO_TONDA}')

def su_carta(lato, dentro):
    """Il disegno centrato su un quadrato pieno del colore della carta."""
    fondo = Image.new('RGB', (lato, lato), CARTA)
    d = round(lato * dentro)
    fondo.paste(disegno.resize((d, d), Image.LANCZOS), ((lato - d) // 2,) * 2)
    return fondo

def scrivi(percorso, immagine):
    os.makedirs(os.path.dirname(percorso), exist_ok=True)
    immagine.save(percorso)

# ---- iOS e web: quadrato pieno, disegno all'84% ----
master = su_carta(1024, DENTRO_IOS)
scrivi(os.path.join(RADICE, 'assets/store/icon.png'), master)
for nome, lato in [('icon-512.png', 512), ('icon-192.png', 192),
                   ('apple-touch-icon.png', 180), ('favicon-32.png', 32), ('favicon-16.png', 16)]:
    scrivi(os.path.join(RADICE, 'pwa/icons', nome), master.resize((lato, lato), Image.LANCZOS))
# maskable della PWA: Android ritaglia, valgono le misure di Android
scrivi(os.path.join(RADICE, 'pwa/icons/icon-maskable.png'), su_carta(512, DENTRO_ANDROID))

# ---- Android: icona adattiva, due strati ----
RES = os.path.join(RADICE, 'android/app/src/main/res')
DENSITA = [('ldpi', 36), ('mdpi', 48), ('hdpi', 72), ('xhdpi', 96), ('xxhdpi', 144), ('xxxhdpi', 192)]
for suffisso, lato in DENSITA:
    cartella = os.path.join(RES, 'mipmap-' + suffisso)
    tela = round(lato * 108 / 48)          # lo strato adattivo e' 108 dove l'icona e' 48

    # primo piano: solo il disegno, il resto trasparente. Lo sfondo lo mette la tinta.
    primo = Image.new('RGBA', (tela, tela), (0, 0, 0, 0))
    d = round(tela * DENTRO_ANDROID)
    primo.paste(disegno.resize((d, d), Image.LANCZOS), ((tela - d) // 2,) * 2)
    scrivi(os.path.join(cartella, 'ic_launcher_foreground.png'), primo)
    # lo strato di fondo resta un'immagine per i lanciatori che non leggono la tinta
    scrivi(os.path.join(cartella, 'ic_launcher_background.png'),
           Image.new('RGB', (tela, tela), CARTA))

    # icone vecchio stile, usate dai lanciatori che non conoscono quelle adattive
    scrivi(os.path.join(cartella, 'ic_launcher.png'), su_carta(lato, DENTRO_IOS))
    tonda = su_carta(lato, DENTRO_TONDA).convert('RGBA')
    maschera = Image.new('L', (lato, lato), 0)
    ImageDraw.Draw(maschera).ellipse([0, 0, lato - 1, lato - 1], fill=255)
    tonda.putalpha(maschera)
    scrivi(os.path.join(cartella, 'ic_launcher_round.png'), tonda)

# ---- Android: l'icona della schermata d'avvio ----
# Da Android 12 la schermata d'avvio la disegna il sistema, e non usa la nostra
# immagine col logo: mette l'icona dell'app al centro di una tinta. Se le diamo
# il disegno com'e', il suo quadrato di carta si stacca dal fondo dell'app, che
# e' piu' caldo. Quindi qui la carta la togliamo, e resta la sola F con le
# frecce, che si posa su qualunque colore.
# Le misure sono quelle dell'icona adattiva: il sistema mostra i due terzi
# centrali della tela e ritaglia in tondo.
def senza_carta(im):
    im = im.convert('RGB'); px = im.load(); w, h = im.size
    fuori = Image.new('RGBA', (w, h))
    out = fuori.load()
    for y in range(h):
        for x in range(w):
            r, g, b = px[x, y]
            luce = 0.299*r + 0.587*g + 0.114*b
            if luce >= 236:      a = 0                                  # carta
            elif luce <= 200:    a = 255                                # disegno
            else:                a = round(255 * (236 - luce) / 36)     # bordo sfumato
            out[x, y] = (r, g, b, a)
    return fuori

# Nella schermata d'avvio ci va il LOGO, non la F: e' il momento in cui l'app
# si presenta, e il logo dice il nome. La parola sotto (SEGNAPUNTI, SCORE...)
# non ci sta: sarebbe un'immagine, quindi una lingua sola, e su iOS non si puo'
# tradurre. Arriva un istante dopo, nell'intestazione dell'app, tradotta.
LOGO = os.path.join(RADICE, 'assets/logo/faraway-logo-ufficiale.png')
logo = Image.open(LOGO).convert('RGBA')
logo = logo.crop(logo.getchannel('A').getbbox())      # via i margini vuoti del file

def logo_largo(larghezza):
    return logo.resize((larghezza, round(logo.height * larghezza / logo.width)), Image.LANCZOS)

# iOS e il fondo di Android: il logo sulla carta, quadrato perche' viene
# ritagliato per ogni forma di schermo. 62% della larghezza.
for nome, fondo in [('splash.png', (239, 238, 243)), ('splash-dark.png', (239, 238, 243))]:
    tela = 2732
    im = Image.new('RGB', (tela, tela), fondo)
    l = logo_largo(round(tela * 0.62))
    im.paste(l, ((tela - l.width) // 2, (tela - l.height) // 2), l)
    scrivi(os.path.join(RADICE, 'assets/store', nome), im)

# Android 12 e oltre: il sistema mostra solo l'icona, in tondo. Il logo e' largo
# e basso, quindi il punto piu' lontano dal centro sono i suoi angoli: con le
# proporzioni del file (3.26 a 1) sporgono del 4.6%, e per restare nel cerchio
# non puo' superare il 53% della tela. Sullo schermo viene circa un terzo della
# larghezza: e' il massimo che Android concede, non una nostra scelta.
tela = 768
avvio = Image.new('RGBA', (tela, tela), (0, 0, 0, 0))
l = logo_largo(round(tela * 0.53))
avvio.paste(l, ((tela - l.width) // 2, (tela - l.height) // 2), l)
scrivi(os.path.join(RES, 'drawable/ic_avvio.png'), avvio)

# ---- le immagini d'avvio dentro i due progetti nativi ----
# Le scrivo qui, alla misura di quelle che ci sono gia', invece di lasciarle a
# @capacitor/assets: quello strumento rigenera anche le icone, con regole sue,
# e butta via tutto il lavoro fatto sopra sull'icona tonda di Android.
# Logo al 62% del lato corto: in verticale e' la larghezza dello schermo.
import glob
def avvio_su_carta(w, h):
    im = Image.new('RGB', (w, h), (246, 241, 228))
    l = logo_largo(round(min(w, h) * 0.62))
    im.paste(l, ((w - l.width) // 2, (h - l.height) // 2), l)
    return im

rifatte = 0
for percorso in (glob.glob(os.path.join(RES, 'drawable*/splash.png')) +
                 glob.glob(os.path.join(RADICE, 'ios/App/App/Assets.xcassets/Splash.imageset/*.png'))):
    w, h = Image.open(percorso).size
    avvio_su_carta(w, h).save(percorso)
    rifatte += 1
print(f'{rifatte} immagini d\'avvio rifatte')
print('icone rigenerate')
