#!/usr/bin/env python3
"""Generate textures for the search dialog redesign (search/DialogWcsSearch.xml).

Outputs (resources/media/):
    search_card_fill.png        - 9-slice card background (radius 30)
    search_card_stroke.png      - 9-slice card border
    search_row_fill.png         - 9-slice stream row background (radius 20)
    search_row_stroke.png       - 9-slice stream row border
    search_button_fill.png      - 9-slice rounded rect, radius 28
                                  (search_button_stroke.png frames the poster)
    search_button_stroke.png    - 9-slice rounded rect border, radius 28
    search_card_button_fill.png   - 9-slice action button of the left card, radius 20
    search_card_button_stroke.png   (2/3 of the card radius 30, like the stream rows)
    search_circle_fill.png      - close button circle (96x96)
    search_circle_stroke.png    - close button circle border
    search_badge_fill.png       - provider badge background (rounded square)
    search_badge_w.png          - Webshare badge composed from brand/webshare_logo_official.jpg
    search_poster_mask.png      - diffuse mask for the poster: white RGB, rounded
                                  shape in ALPHA (Kodi multiplies colour and alpha;
                                  black opaque corners would render black)
    search_play_idle.png        - idle row play button (grey rounded square + triangle)
    search_play_focus.png       - focused row play button (blue rounded square + triangle)
    search_action_idle.png      - row action background without glyph (download mode,
                                  glyph = Kodi skin icon icons/sidemenu/download.png)
    search_action_focus.png     - focused row action background without glyph

Metadata chips are rendered per row at runtime (wcs/ui/chip_strip.py).
    search_header_play.png      - header play circle
"""
import os
import sys

from PIL import Image, ImageDraw

OUT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__)))
SS = 4

_KODI_FONT = os.environ.get(
    'KODI_FONT',
    '/Applications/Kodi.app/Contents/Resources/Kodi/addons/skin.estuary/fonts/NotoSans-Regular.ttf',
)

BLUE = (74, 153, 253, 255)
PLAY_IDLE_FILL = (61, 65, 74, 255)
HEADER_FILL = (56, 57, 63, 255)
WHITE = (255, 255, 255, 255)


def save(img, name):
    path = os.path.join(OUT_DIR, name)
    img.save(path, 'PNG')
    print(f"  {name} {img.size[0]}x{img.size[1]}")


def down(img, size):
    return img.resize(size, Image.LANCZOS)


def rounded_rect(name, w, h, radius, stroke=0, outline_inset=0.5):
    img = Image.new('RGBA', (w * SS, h * SS), (0, 0, 0, 0))
    d = ImageDraw.Draw(img)
    if stroke:
        half = stroke * SS / 2 + outline_inset * SS
        d.rounded_rectangle([half, half, w * SS - 1 - half, h * SS - 1 - half],
                            radius=radius * SS - half, outline=WHITE, width=stroke * SS)
    else:
        d.rounded_rectangle([0, 0, w * SS - 1, h * SS - 1], radius=radius * SS, fill=WHITE)
    save(down(img, (w, h)), name)


def provider_badge(letter, color, name):
    # Badge 80x80: vetsi zaobleni (radius 30), mirne pruhledne cerne pozadi,
    # pismeno vycentrovane podle skutecneho inkoustu (bbox), ne podle glyph boxu.
    size, radius = 80, 30
    bg = (19, 25, 32, 230)  # ~90 % kryti
    img = Image.new('RGBA', (size * SS, size * SS), (0, 0, 0, 0))
    d = ImageDraw.Draw(img)
    d.rounded_rectangle([0, 0, size * SS - 1, size * SS - 1], radius=radius * SS, fill=bg)
    from PIL import ImageFont
    font_path = os.path.join(os.path.dirname(__file__), '..', '..',
                             'scripts', 'dialog_harness', 'fonts', 'NotoSans-Bold.ttf')
    font_path = os.path.normpath(os.path.join(OUT_DIR, font_path))
    if not os.path.exists(font_path):
        font_path = KODI_FONT
    font = ImageFont.truetype(font_path, 52 * SS)
    left, top, right, bottom = d.textbbox((0, 0), letter, font=font)
    ink_w, ink_h = right - left, bottom - top
    x = (size * SS - ink_w) / 2 - left
    y = (size * SS - ink_h) / 2 - top
    d.text((x, y), letter, font=font, fill=color)
    save(down(img, (size, size)), name)


# Poster 235x363 skin px, same corner radius as the action buttons (28).
# The XML uses <aspectratio scalediffuse="false">scale</aspectratio>: Kodi then maps the
# mask onto the control rect (GUITexture::CalculateSize), not onto the scaled/cropped
# image, so the corners never get cropped. Mask is 2x for crisp edges above 1080p;
# Kodi samples it by UV, so any GUI resolution maps it exactly. Addon textures get no
# mipmaps, so at 720p the GPU samples the 2x mask 3:1 bilinearly: a 1-texel Gaussian
# edge keeps the corner anti-aliased there (simulated 720p/1080p/1440p/2160p).
POSTER_W, POSTER_H, POSTER_RADIUS, POSTER_MASK_SCALE = 235, 363, 28, 2


WEBSHARE_LOGO = os.path.join(OUT_DIR, 'brand', 'webshare_logo_official.jpg')
WEBSHARE_BG = (5, 42, 61)          # navy background of the official logo image
WEBSHARE_EMBLEM_X = (25, 298)      # column ranges of the two parts in that image
WEBSHARE_TEXT_X = (336, 1040)      # "Webshare" without ".cz" (readable at 80 px)


def _logo_part(alpha, x0, x1):
    import numpy as np
    sub = alpha[:, x0:x1 + 1]
    rows = np.where(sub.max(axis=1) > 0.05)[0]
    cols = np.where(sub.max(axis=0) > 0.05)[0]
    crop = sub[rows.min():rows.max() + 1, cols.min():cols.max() + 1]
    part = Image.new('RGBA', (crop.shape[1], crop.shape[0]), WHITE)
    part.putalpha(Image.fromarray((crop * 255).astype('uint8')))
    return part


def webshare_badge(size=160):
    """Provider badge from the official Webshare.cz logo: emblem on top, wordmark below,
    both centred on their ink. White ink is converted to alpha from the navy background,
    so antialiasing of the original is kept. 2x size for crisp edges above 1080p."""
    import numpy as np
    src = np.asarray(Image.open(WEBSHARE_LOGO).convert('RGB')).astype(float)
    bg = np.array(WEBSHARE_BG, float)
    alpha = np.clip(((src - bg) / (255 - bg)).max(axis=2), 0, 1)
    emblem = _logo_part(alpha, *WEBSHARE_EMBLEM_X)
    text = _logo_part(alpha, *WEBSHARE_TEXT_X)
    w = size * SS
    img = Image.new('RGBA', (w, w), (0, 0, 0, 0))
    ImageDraw.Draw(img).rounded_rectangle([0, 0, w - 1, w - 1], radius=int(w * 30 / 80), fill=(19, 25, 32, 230))
    eh = int(w * 0.46)
    ew = int(emblem.width * eh / emblem.height)
    tw = int(w * 0.78)
    th = int(text.height * tw / text.width)
    gap = int(w * 0.06)
    top = (w - (eh + gap + th)) // 2
    img.alpha_composite(emblem.resize((ew, eh), Image.LANCZOS), ((w - ew) // 2, top))
    img.alpha_composite(text.resize((tw, th), Image.LANCZOS), ((w - tw) // 2, top + eh + gap))
    save(down(img, (size, size)), 'search_badge_w.png')


def poster_mask():
    w, h = POSTER_W * POSTER_MASK_SCALE, POSTER_H * POSTER_MASK_SCALE
    alpha = Image.new('L', (w * SS, h * SS), 0)
    ImageDraw.Draw(alpha).rounded_rectangle([0, 0, w * SS - 1, h * SS - 1],
                                            radius=POSTER_RADIUS * POSTER_MASK_SCALE * SS, fill=255)
    from PIL import ImageFilter
    mask = Image.new('RGBA', (w, h), WHITE)
    mask.putalpha(down(alpha, (w, h)).filter(ImageFilter.GaussianBlur(1.0)))
    save(mask, 'search_poster_mask.png')


def triangle(d, cx, cy, w, h, color=WHITE):
    d.polygon([(cx - w / 2, cy - h / 2), (cx - w / 2, cy + h / 2), (cx + w / 2, cy)], fill=color)


def play_button(name, size, box_fill, radius=None, stroke_color=None, stroke_width=2):
    img = Image.new('RGBA', (size * SS, size * SS), (0, 0, 0, 0))
    d = ImageDraw.Draw(img)
    if radius is None:
        d.ellipse([0, 0, size * SS - 1, size * SS - 1], fill=box_fill)
        if stroke_color:
            half = stroke_width * SS / 2 + 1 * SS
            d.ellipse([half, half, size * SS - 1 - half, size * SS - 1 - half],
                      outline=stroke_color, width=stroke_width * SS)
    else:
        d.rounded_rectangle([0, 0, size * SS - 1, size * SS - 1], radius=radius * SS, fill=box_fill)
        if stroke_color:
            half = stroke_width * SS / 2 + 1 * SS
            d.rounded_rectangle([half, half, size * SS - 1 - half, size * SS - 1 - half],
                                radius=radius * SS - half, outline=stroke_color, width=stroke_width * SS)
    triangle(d, size * SS * 0.545, size * SS / 2, size * SS * 0.30, size * SS * 0.36)
    save(down(img, (size, size)), name)


def action_background(name, fill):
    size, radius = 80, 24
    img = Image.new('RGBA', (size * SS, size * SS), (0, 0, 0, 0))
    ImageDraw.Draw(img).rounded_rectangle([0, 0, size * SS - 1, size * SS - 1], radius=radius * SS, fill=fill)
    save(down(img, (size, size)), name)


def icon_close():
    """X with rounded caps (reference: ~3.5 px stroke)."""
    size = 30
    img = Image.new('RGBA', (size * SS, size * SS), (0, 0, 0, 0))
    d = ImageDraw.Draw(img)
    stroke = 3.6 * SS
    lo, hi = 5 * SS, size * SS - 5 * SS
    for (x0, y0, x1, y1) in ((lo, lo, hi, hi), (hi, lo, lo, hi)):
        d.line([x0, y0, x1, y1], fill=WHITE, width=int(stroke))
        for x, y in ((x0, y0), (x1, y1)):
            d.ellipse([x - stroke / 2, y - stroke / 2, x + stroke / 2, y + stroke / 2], fill=WHITE)
    save(down(img, (size, size)), 'search_icon_close.png')


def main():
    print("Generating search dialog textures:")
    rounded_rect('search_card_fill.png', 240, 240, 30)
    rounded_rect('search_card_stroke.png', 240, 240, 30, stroke=2)
    rounded_rect('search_row_fill.png', 200, 110, 20)
    rounded_rect('search_row_stroke.png', 200, 110, 20, stroke=2)
    rounded_rect('search_button_fill.png', 280, 95, 28)
    rounded_rect('search_button_stroke.png', 280, 95, 28, stroke=2)
    rounded_rect('search_card_button_fill.png', 280, 95, 20)
    rounded_rect('search_card_button_stroke.png', 280, 95, 20, stroke=2)
    rounded_rect('search_circle_fill.png', 96, 96, 48)
    rounded_rect('search_circle_stroke.png', 96, 96, 48, stroke=2)
    poster_mask()
    webshare_badge()
    provider_badge('K', (255, 105, 180, 255), 'search_badge_k.png')
    play_button('search_play_idle.png', 80, PLAY_IDLE_FILL, radius=24)
    play_button('search_play_focus.png', 80, BLUE, radius=24)
    action_background('search_action_idle.png', PLAY_IDLE_FILL)
    action_background('search_action_focus.png', BLUE)
    play_button('search_header_play.png', 90, HEADER_FILL, stroke_color=(255, 255, 255, 38), stroke_width=2)
    icon_close()
    print("Done.")


if __name__ == '__main__':
    sys.exit(main())
