import numpy as np
from PIL import Image, ImageDraw, ImageFont
import os

# ---------- Configuration (smaller size) ----------
W, H = 480, 210  # lower resolution
TOTAL_DURATION = 3.0
FPS = 15  # lower fps
TEXT = "dLLM"
# TEXT_COLOR = (235, 235, 235)
TEXT_COLOR = (0, 0, 0)
OUTPUT = "logo.gif"
LAST_FRAME_PNG = "logo.png"

DIFFUSION_PORTION = 0.3  # fewer diffusion frames
SEED = 8


# ---------- Auto font size ----------
def load_font_auto_size(text, w, h, target_width_ratio=0.95, target_height_ratio=0.95):
    lo, hi = 10, 2000
    best_font, best_size = None, lo
    while lo <= hi:
        mid = (lo + hi) // 2
        try:
            font = ImageFont.truetype(
                "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", size=mid
            )
        except:
            font = ImageFont.load_default()
        dummy = Image.new("L", (w, h), 0)
        d = ImageDraw.Draw(dummy)
        bbox = d.textbbox((0, 0), text, font=font)
        tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]

        width_ok = tw <= w * target_width_ratio
        height_ok = th <= h * target_height_ratio

        if width_ok and height_ok:
            best_font, best_size = font, mid
            lo = mid + 1
        else:
            hi = mid - 1

    return best_font if best_font is not None else font


# ---------- Text rendering ----------
def render_text_mask(w, h, text, font):
    img = Image.new("L", (w, h), 0)
    d = ImageDraw.Draw(img)
    bbox = d.textbbox((0, 0), text, font=font)
    tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]
    x = (w - tw) // 2 - bbox[0]
    y = (h - th) // 2 - bbox[1]
    d.text((x, y), text, font=font, fill=255)
    return np.asarray(img, np.float32) / 255.0


# ---------- Initialization ----------
font = load_font_auto_size(TEXT, W, H)
mask = render_text_mask(W, H, TEXT, font)

num_frames = int(TOTAL_DURATION * FPS)
diffusion_frames = max(1, int(num_frames * DIFFUSION_PORTION))
hold_ms = int((TOTAL_DURATION - diffusion_frames / FPS) * 1000)

rng = np.random.default_rng(SEED)
frames = []

# ---------- Diffusion stage ----------
for i in range(diffusion_frames):
    t = i / max(1, diffusion_frames - 1)
    progress = t**0.9
    noise_sigma = (1.0 - progress) ** 2.2

    noise = rng.standard_normal((H, W, 1)).astype(np.float32)
    noise_img = 1.0 - noise_sigma * 0.5 * np.abs(noise)
    np.clip(noise_img, 0.0, 1.0, out=noise_img)

    alpha = progress**2.0
    alpha_map = (mask * alpha).astype(np.float32)[..., None]

    text_rgb = np.zeros((H, W, 3), dtype=np.float32)
    for c in range(3):
        text_rgb[..., c] = (mask > 0).astype(np.float32) * (TEXT_COLOR[c] / 255.0)

    frame = (1.0 - alpha_map) * noise_img + alpha_map * text_rgb
    frame = (np.clip(frame, 0.0, 1.0) * 255).astype(np.uint8)
    frames.append(Image.fromarray(frame, mode="RGB"))

# ---------- Last frame ----------
final_frame = frames[-1]

# ---------- Save last frame as PNG ----------
final_frame.save(LAST_FRAME_PNG)
print(f"🖼️  Last frame saved as: {LAST_FRAME_PNG}")

# ---------- Quantization (reduce size) ----------
pal_frames = [f.convert("P", palette=Image.ADAPTIVE, colors=64) for f in frames]
pal_final = final_frame.convert("P", palette=Image.ADAPTIVE, colors=64)

# ---------- Save GIF ----------
normal_ms = int(1000 / FPS)
durations = [normal_ms] * len(pal_frames) + [hold_ms]

pal_frames[0].save(
    OUTPUT,
    save_all=True,
    append_images=pal_frames[1:] + [pal_final],
    duration=durations,
    loop=0,
    optimize=True,
)

print(f"✅ GIF saved: {OUTPUT}")
print(
    f"Frames (diffusion only): {len(pal_frames)} at {FPS} FPS, final hold {hold_ms} ms, resolution {W}x{H}"
)
