#!/usr/bin/env python3
"""
gen_banners.py — Generate playlist banner images + live schedule banner.

Playlist banners:    videos/<playlist-id>/banner.png    (transparent, 44px tall)
Live schedule banner: videos/live-banner.png            (dark bg, 720x240)
Video banners:       videos/<playlist>/<id>.banner.png  (transparent, 36px tall)

Usage:
  python3 gen_banners.py                    # playlist banners only
  python3 gen_banners.py --live             # also generate live banner
  python3 gen_banners.py --live-only        # live banner only
  python3 gen_banners.py --banners          # also generate per-video banners (skip existing)
  python3 gen_banners.py --banners-only     # per-video banners only
"""

import json, os, sys
from PIL import Image, ImageDraw, ImageFont

BASE        = '/var/www/html/media'
PLAYLISTS   = f'{BASE}/data/playlists.json'
VIDEOS_DIR  = f'{BASE}/videos'

FONT_EN  = '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf'
FONT_EN_REG = '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf'
FONT_AR  = '/usr/share/fonts/truetype/noto/NotoSansArabic-SemiCondensedMedium.ttf'

# ── Playlist banner constants ─────────────────────────────────────────────────
TARGET_H  = 44
PADDING_X = 16
TEXT_EN   = (255, 255, 255, 255)
TEXT_AR   = (220, 200, 140, 255)
SEP_COL   = (180, 140,  60, 255)

# ── Live banner constants ─────────────────────────────────────────────────────
LB_W      = 720
LB_H      = 80
LB_BG     = (0, 0, 0, 0)            # transparent
LB_ACCENT = (180, 140, 60, 255)     # gold accent line
LB_WHITE  = (255, 255, 255, 255)
LB_GOLD   = (220, 200, 140, 255)
LB_GRAY   = (160, 160, 180, 255)
LB_TIME   = (100, 160, 255, 255)    # blue for time labels
LB_PAD    = 24


# ── Helpers ───────────────────────────────────────────────────────────────────

def load_playlist_titles():
    """Return {id: {en, ar}} from playlists.json."""
    with open(PLAYLISTS) as f:
        data = json.load(f)
    return {
        pl['id']: {
            'en': pl.get('title', pl['id']),
            'ar': pl.get('arabicTitle', ''),
        }
        for pl in data['playlists']
    }



# ── Playlist banner ───────────────────────────────────────────────────────────

def make_banner(title_en, title_ar):
    for size in range(TARGET_H, 8, -1):
        font_en = ImageFont.truetype(FONT_EN, size)
        font_ar = ImageFont.truetype(FONT_AR, size)
        h_en = font_en.getbbox(title_en)[3] - font_en.getbbox(title_en)[1]
        h_ar = font_ar.getbbox(title_ar)[3] - font_ar.getbbox(title_ar)[1] if title_ar else 0
        if max(h_en, h_ar) <= TARGET_H:
            break

    sep    = '  |  '
    bb_en  = font_en.getbbox(title_en)
    bb_sep = font_en.getbbox(sep)
    bb_ar  = font_ar.getbbox(title_ar) if title_ar else (0, 0, 0, 0)

    w_en  = bb_en[2]  - bb_en[0]
    w_sep = bb_sep[2] - bb_sep[0]
    w_ar  = bb_ar[2]  - bb_ar[0] if title_ar else 0

    total_w = w_en + (w_sep + w_ar if title_ar else 0)
    img_w   = total_w + PADDING_X * 2
    img_h   = TARGET_H

    img  = Image.new('RGBA', (img_w, img_h), (0, 0, 0, 0))
    draw = ImageDraw.Draw(img)

    def y_center(bbox):
        return (TARGET_H - (bbox[3] - bbox[1])) // 2 - bbox[1]

    x = PADDING_X
    draw.text((x, y_center(bb_en)),  title_en, font=font_en, fill=TEXT_EN)
    x += w_en

    if title_ar:
        draw.text((x, y_center(bb_sep)), sep,      font=font_en, fill=SEP_COL)
        x += w_sep
        draw.text((x, y_center(bb_ar)),  title_ar, font=font_ar, fill=TEXT_AR)

    return img


# ── Live schedule banner ──────────────────────────────────────────────────────


def make_live_banner():
    """
    Generate a 720x240 transparent banner.
    Layout:
      Single row: "Watch Live" (left, white) + "شاهد مباشر" (right, gold)
    """
    img  = Image.new('RGBA', (LB_W, LB_H), LB_BG)
    draw = ImageDraw.Draw(img)

    header_en = 'Watch Live '
    header_ar = 'شاهد مباشر'

    AR_SIZE = 38
    GAP     = 24
    font_ar = ImageFont.truetype(FONT_AR, AR_SIZE)
    w_ar_fixed = font_ar.getbbox(header_ar)[2] - font_ar.getbbox(header_ar)[0]
    for title_size in range(28, 12, -1):
        font_title = ImageFont.truetype(FONT_EN, title_size)
        w_en_t = font_title.getbbox(header_en)[2] - font_title.getbbox(header_en)[0]
        if w_en_t + GAP + w_ar_fixed <= LB_W - LB_PAD * 2:
            break

    bb_title = font_title.getbbox(header_en)
    h_title  = bb_title[3] - bb_title[1]
    cy       = LB_H // 2   # vertically centered in the full banner

    draw.text((LB_PAD, cy - h_title // 2 - bb_title[1]),
              header_en, font=font_title, fill=LB_WHITE)

    bb_ar = font_ar.getbbox(header_ar)
    w_ar  = bb_ar[2] - bb_ar[0]
    h_ar  = bb_ar[3] - bb_ar[1]
    draw.text((LB_W - LB_PAD - w_ar, cy - h_ar // 2 - bb_ar[1]),
              header_ar, font=font_ar, fill=LB_GOLD)

    return img


# ── Entry points ──────────────────────────────────────────────────────────────

def gen_playlist_banners():
    with open(PLAYLISTS) as f:
        data = json.load(f)

    for pl in data['playlists']:
        pid      = pl['id']
        title_en = pl.get('title', pid)
        title_ar = pl.get('arabicTitle', '')
        out_dir  = os.path.join(VIDEOS_DIR, pid)
        out_path = os.path.join(out_dir, 'banner.png')

        if not os.path.isdir(out_dir):
            print(f'  SKIP {pid} — no videos directory')
            continue

        img = make_banner(title_en, title_ar)
        img.save(out_path, 'PNG')
        print(f'  OK   {pid}  ({img.width}x{img.height}) → banner.png')


# ── Video thumbnail overlay ───────────────────────────────────────────────────

import re, shutil

def extract_arabic(s):
    """Extract Arabic-script characters from a mixed title string."""
    ar = re.sub(r'[^\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF ]', '', s)
    return ' '.join(ar.split())   # collapse whitespace

def strip_arabic(s):
    """Remove Arabic-script characters from a mixed title string, keeping English."""
    en = re.sub(r'[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]', '', s)
    return ' '.join(en.split())


def make_thumb_overlay(thumb_path, title_en, title_ar):
    """
    Open thumbnail, draw a semi-transparent gradient bar at the bottom,
    render English (white) and Arabic (gold) title lines, return composited image.
    Original is RGB; we work in RGBA then convert back.
    """
    base = Image.open(thumb_path).convert('RGBA')
    W, H = base.size

    # ── Gradient overlay ──────────────────────────────────────────────────────
    GRAD_H = min(220, H // 3)
    overlay = Image.new('RGBA', (W, H), (0, 0, 0, 0))
    for y in range(GRAD_H):
        alpha = int(200 * (y / GRAD_H))          # 0 → 200 (top → bottom of band)
        for x in range(W):
            overlay.putpixel((x, H - GRAD_H + y), (8, 10, 24, alpha))

    composited = Image.alpha_composite(base, overlay)
    draw = ImageDraw.Draw(composited)

    # ── Fonts (auto-size to fit width) ───────────────────────────────────────
    PAD = 28
    max_w = W - PAD * 2

    def fit_font(path, text, max_size, min_size=14):
        for sz in range(max_size, min_size - 1, -1):
            f = ImageFont.truetype(path, sz)
            bb = f.getbbox(text)
            if (bb[2] - bb[0]) <= max_w:
                return f, bb
        return ImageFont.truetype(path, min_size), ImageFont.truetype(path, min_size).getbbox(text)

    font_ar, bb_ar = fit_font(FONT_AR,     title_ar, 52) if title_ar else (None, None)
    font_en, bb_en = fit_font(FONT_EN_REG, title_en, 44) if title_en else (None, None)

    # ── Layout: Arabic bottom (right-justified), English line above it ────────
    LINE_GAP = 8
    BOTTOM   = H - 18

    y = BOTTOM
    if font_ar and title_ar:
        h_ar  = bb_ar[3] - bb_ar[1]
        w_ar  = bb_ar[2] - bb_ar[0]
        y -= h_ar
        x_ar  = W - PAD - w_ar          # right-justify
        draw.text((x_ar, y - bb_ar[1]), title_ar, font=font_ar, fill=LB_GOLD)
        y -= LINE_GAP

    if font_en and title_en:
        h_en = bb_en[3] - bb_en[1]
        y -= h_en
        draw.text((PAD, y - bb_en[1]), title_en, font=font_en, fill=LB_WHITE)

    return composited.convert('RGB')


def gen_video_overlays():
    """
    For every videos/<playlist>/<id>.jpg:
      1. Backup to <id>-orig.jpg if not already backed up
      2. Apply title overlay and overwrite <id>.jpg
    """
    with open(PLAYLISTS) as f:
        data = json.load(f)
    playlist_ids = [pl['id'] for pl in data['playlists']]

    total_ok = total_skip = 0

    for pid in playlist_ids:
        pl_dir = os.path.join(VIDEOS_DIR, pid)
        if not os.path.isdir(pl_dir):
            continue

        for json_file in sorted(f for f in os.listdir(pl_dir) if f.endswith('.json')):
            vid = json_file[:-5]
            jpg = os.path.join(pl_dir, f'{vid}.jpg')
            orig = os.path.join(pl_dir, f'{vid}-orig.jpg')

            if not os.path.exists(jpg):
                continue

            try:
                with open(os.path.join(pl_dir, json_file)) as jf:
                    meta = json.load(jf)
                raw_title = meta.get('title', '')
                title_en  = strip_arabic(raw_title).strip(' |')
                title_ar  = extract_arabic(raw_title)

                # Backup original once
                if not os.path.exists(orig):
                    shutil.copy2(jpg, orig)

                img = make_thumb_overlay(orig, title_en, title_ar)
                img.save(jpg, 'JPEG', quality=90)
                total_ok += 1
                print(f'  OK   {pid}/{vid}.jpg')
            except Exception as e:
                total_skip += 1
                print(f'  SKIP {pid}/{vid}: {e}')

    print(f'Overlays: {total_ok} updated, {total_skip} skipped.')


# ── Per-video banner files ───────────────────────────────────────────────────

VBANNER_H = 36
VBANNER_PAD = 12

def make_video_banner(title_en, title_ar):
    """
    Create a transparent banner PNG sized to fit under the playlist banner
    in the Roku overlay. English (white) | Arabic (gold), auto-width.
    """
    # Fit fonts to target height
    for sz in range(VBANNER_H, 10, -1):
        font_en = ImageFont.truetype(FONT_EN_REG, sz)
        font_ar = ImageFont.truetype(FONT_AR, sz)
        h_en = font_en.getbbox(title_en)[3] - font_en.getbbox(title_en)[1] if title_en else 0
        h_ar = font_ar.getbbox(title_ar)[3] - font_ar.getbbox(title_ar)[1] if title_ar else 0
        if max(h_en, h_ar) <= VBANNER_H:
            break

    sep = '  |  '
    bb_en  = font_en.getbbox(title_en) if title_en else (0, 0, 0, 0)
    bb_sep = font_en.getbbox(sep)
    bb_ar  = font_ar.getbbox(title_ar) if title_ar else (0, 0, 0, 0)

    w_en  = bb_en[2]  - bb_en[0] if title_en else 0
    w_sep = bb_sep[2] - bb_sep[0]
    w_ar  = bb_ar[2]  - bb_ar[0] if title_ar else 0

    has_both = title_en and title_ar
    total_w = w_en + (w_sep + w_ar if has_both else w_ar) if (title_en or title_ar) else 0
    img_w = total_w + VBANNER_PAD * 2

    img  = Image.new('RGBA', (img_w, VBANNER_H), (0, 0, 0, 0))
    draw = ImageDraw.Draw(img)

    def y_center(bbox):
        return (VBANNER_H - (bbox[3] - bbox[1])) // 2 - bbox[1]

    x = VBANNER_PAD
    if title_en:
        draw.text((x, y_center(bb_en)), title_en, font=font_en, fill=TEXT_EN)
        x += w_en

    if has_both:
        draw.text((x, y_center(bb_sep)), sep, font=font_en, fill=SEP_COL)
        x += w_sep

    if title_ar:
        draw.text((x, y_center(bb_ar)), title_ar, font=font_ar, fill=TEXT_AR)

    return img


def gen_video_banners():
    """
    For every videos/<playlist>/<id>.json, create <id>.banner.jpg
    if it doesn't already exist. English + Arabic title on dark background.
    """
    with open(PLAYLISTS) as f:
        data = json.load(f)
    playlist_ids = [pl['id'] for pl in data['playlists']]

    total_ok = total_skip = total_exist = 0

    for pid in playlist_ids:
        pl_dir = os.path.join(VIDEOS_DIR, pid)
        if not os.path.isdir(pl_dir):
            continue

        for json_file in sorted(f for f in os.listdir(pl_dir) if f.endswith('.json')):
            vid = json_file[:-5]
            banner_path = os.path.join(pl_dir, f'{vid}.banner.jpg')

            json_path = os.path.join(pl_dir, json_file)
            if os.path.exists(banner_path):
                # Regenerate only if the JSON metadata is newer than the banner
                if os.path.getmtime(banner_path) >= os.path.getmtime(json_path):
                    total_exist += 1
                    continue

            try:
                with open(json_path) as jf:
                    meta = json.load(jf)
                raw_title = meta.get('title', '')
                title_en  = strip_arabic(raw_title).strip(' |')
                title_ar  = extract_arabic(raw_title)

                img = make_video_banner(title_en, title_ar)
                # Flatten RGBA onto dark background for JPEG output
                bg = Image.new('RGB', img.size, (8, 10, 24))
                bg.paste(img, mask=img.split()[3])
                bg.save(banner_path, 'JPEG', quality=90)
                total_ok += 1
                print(f'  OK   {pid}/{vid}.banner.jpg')
            except Exception as e:
                total_skip += 1
                print(f'  SKIP {pid}/{vid}: {e}')

    print(f'Banners: {total_ok} created, {total_exist} already exist, {total_skip} skipped.')


def gen_live_banner():
    out_path = os.path.join(VIDEOS_DIR, 'live-banner.png')
    img = make_live_banner()
    img.save(out_path, 'PNG')
    print(f'  OK   live-banner  ({img.width}x{img.height}) → {out_path}')


def main():
    args = sys.argv[1:]

    live_only     = '--live-only'     in args
    do_live       = live_only or '--live' in args
    do_overlays   = '--overlays'      in args
    overlays_only = '--overlays-only' in args
    do_banners    = '--banners'       in args
    banners_only  = '--banners-only'  in args

    if not live_only and not overlays_only and not banners_only:
        gen_playlist_banners()

    if do_live:
        gen_live_banner()

    if do_overlays or overlays_only:
        gen_video_overlays()

    if do_banners or banners_only:
        gen_video_banners()

    print('Done.')


if __name__ == '__main__':
    main()
