"""Screen rendering helpers for the SDL2 UI."""

from __future__ import annotations

import ctypes
from typing import TYPE_CHECKING

import sdl2

try:
    import sdl2.sdlttf as ttf
except ImportError:
    ttf = None  # type: ignore[assignment]

from r36s_dlna_browser.ui import theme

if TYPE_CHECKING:
    from r36s_dlna_browser.dlna.browser_state import BrowserState


def _color(rgba: tuple) -> sdl2.SDL_Color:
    return sdl2.SDL_Color(rgba[0], rgba[1], rgba[2], rgba[3] if len(rgba) > 3 else 255)


def _render_text(
    renderer: ctypes.c_void_p,
    font: ctypes.c_void_p,
    text: str,
    x: int,
    y: int,
    color: tuple,
    max_w: int = 0,
) -> None:
    if not text or not font or not ttf:
        return
    encoded = text.encode("utf-8")
    surf = ttf.TTF_RenderUTF8_Blended(font, encoded, _color(color))
    if not surf:
        return
    tex = sdl2.SDL_CreateTextureFromSurface(renderer, surf)
    w, h = surf.contents.w, surf.contents.h
    sdl2.SDL_FreeSurface(surf)
    if not tex:
        return
    if max_w and w > max_w:
        w = max_w
    dst = sdl2.SDL_Rect(x, y, w, h)
    src = sdl2.SDL_Rect(0, 0, w, h)
    sdl2.SDL_RenderCopy(renderer, tex, src, dst)
    sdl2.SDL_DestroyTexture(tex)


def _fit_text(font: ctypes.c_void_p, text: str, max_w: int) -> str:
    if not text or not font or not ttf or max_w <= 0:
        return text

    width = ctypes.c_int()
    height = ctypes.c_int()
    encoded = text.encode("utf-8")
    if ttf.TTF_SizeUTF8(font, encoded, ctypes.byref(width), ctypes.byref(height)) == 0 and width.value <= max_w:
        return text

    ellipsis = "..."
    ellipsis_width = ctypes.c_int()
    if ttf.TTF_SizeUTF8(font, ellipsis.encode("utf-8"), ctypes.byref(ellipsis_width), ctypes.byref(height)) != 0:
        return text
    if ellipsis_width.value >= max_w:
        return ""

    trimmed = text
    while trimmed:
        trimmed = trimmed[:-1]
        candidate = (trimmed.rstrip() + ellipsis).encode("utf-8")
        if ttf.TTF_SizeUTF8(font, candidate, ctypes.byref(width), ctypes.byref(height)) == 0 and width.value <= max_w:
            return trimmed.rstrip() + ellipsis

    return ellipsis if ellipsis_width.value <= max_w else ""


def _fill_rect(renderer, x: int, y: int, w: int, h: int, color: tuple) -> None:
    sdl2.SDL_SetRenderDrawBlendMode(renderer, sdl2.SDL_BLENDMODE_BLEND)
    sdl2.SDL_SetRenderDrawColor(renderer, color[0], color[1], color[2],
                                color[3] if len(color) > 3 else 255)
    rect = sdl2.SDL_Rect(x, y, w, h)
    sdl2.SDL_RenderFillRect(renderer, rect)


def _draw_rect(renderer, x: int, y: int, w: int, h: int, color: tuple) -> None:
    sdl2.SDL_SetRenderDrawBlendMode(renderer, sdl2.SDL_BLENDMODE_BLEND)
    sdl2.SDL_SetRenderDrawColor(renderer, color[0], color[1], color[2],
                                color[3] if len(color) > 3 else 255)
    rect = sdl2.SDL_Rect(x, y, w, h)
    sdl2.SDL_RenderDrawRect(renderer, rect)


def _render_icon(renderer, texture, x: int, y: int, size: int) -> None:
    """Blit a pre-loaded SDL texture as a square icon."""
    if not texture:
        return
    dst = sdl2.SDL_Rect(x, y, size, size)
    sdl2.SDL_RenderCopy(renderer, texture, None, dst)


def _format_time(seconds: float) -> str:
    total = max(0, int(seconds))
    mins, secs = divmod(total, 60)
    hours, mins = divmod(mins, 60)
    if hours:
        return f"{hours}:{mins:02d}:{secs:02d}"
    return f"{mins:02d}:{secs:02d}"


def _draw_progress_bar(renderer, x: int, y: int, w: int, h: int, progress: float) -> None:
    _fill_rect(renderer, x, y, w, h, theme.PLAYBACK_OVERLAY_DIM)
    fill_w = max(0, min(w, int(w * max(0.0, min(1.0, progress)))))
    if fill_w:
        _fill_rect(renderer, x, y, fill_w, h, theme.PLAYBACK_OVERLAY_ACCENT)


# ── Public drawing functions ─────────────────────────────────────


def draw_header(renderer, font, title: str, layout: theme.Layout) -> None:
    _fill_rect(renderer, 0, 0, layout.width, layout.header_h, theme.HEADER_BG)
    _render_text(renderer, font, title, layout.list_pad_left, max(6, layout.header_h // 5),
                 theme.TEXT_COLOR, layout.width - layout.list_pad_left * 2)


def draw_status_bar(renderer, font, text: str, layout: theme.Layout) -> None:
    y = layout.height - layout.status_h
    _fill_rect(renderer, 0, y, layout.width, layout.status_h, theme.STATUS_BG)
    _render_text(renderer, font, text, layout.list_pad_left, y + max(4, layout.status_h // 5),
                 theme.DIM_TEXT, layout.width - layout.list_pad_left * 2)


def draw_server_list(renderer, font, state: "BrowserState", layout: theme.Layout, icons: dict | None = None) -> None:
    draw_header(renderer, font, "DLNA Servers", layout)
    if not state.servers:
        msg = "Searching..." if state.loading else "No servers found.  Press A to refresh."
        _render_text(renderer, font, msg, layout.list_pad_left, layout.list_top + max(20, layout.list_item_h // 2), theme.DIM_TEXT)
        draw_status_bar(renderer, font, "A=Select  Start=Quit", layout)
        return

    srv_icon = icons.get("server") if icons else None
    icon_gap = layout.icon_size + 4 if srv_icon else 0
    text_x = layout.list_pad_left + icon_gap

    vis = layout.visible_items
    offset = state.server_scroll_offset
    for i in range(vis):
        idx = offset + i
        if idx >= len(state.servers):
            break
        srv = state.servers[idx]
        y = layout.list_top + i * layout.list_item_h
        if idx == state.server_cursor:
            _fill_rect(renderer, 0, y, layout.width, layout.list_item_h, theme.HIGHLIGHT_BG)
            color = theme.HIGHLIGHT_TEXT
        else:
            color = theme.TEXT_COLOR
        icon_y = y + (layout.list_item_h - layout.icon_size) // 2
        _render_icon(renderer, srv_icon, layout.list_pad_left, icon_y, layout.icon_size)
        _render_text(renderer, font, str(srv), text_x, y + max(3, layout.list_item_h // 7),
                     color, layout.width - text_x - layout.list_pad_right)

    draw_status_bar(renderer, font, f"A=Browse  B=Refresh  Start=Quit  ({len(state.servers)} servers)", layout)


def draw_browse_list(renderer, font, state: "BrowserState", layout: theme.Layout, icons: dict | None = None) -> None:
    lv = state.current_level
    title = "Browsing"
    if lv:
        title = f"/{lv.object_id}" if lv.object_id != "0" else "/"
    draw_header(renderer, font, title, layout)

    items = state.current_items
    if not items:
        msg = "Loading..." if state.loading else "Empty folder."
        _render_text(renderer, font, msg, layout.list_pad_left, layout.list_top + max(20, layout.list_item_h // 2), theme.DIM_TEXT)
        draw_status_bar(renderer, font, "B=Back", layout)
        return

    has_icons = bool(icons)
    icon_gap = layout.icon_size + 4 if has_icons else 0
    text_x = layout.list_pad_left + icon_gap

    vis = layout.visible_items
    offset = state.scroll_offset
    cursor = state.cursor
    for i in range(vis):
        idx = offset + i
        if idx >= len(items):
            break
        item = items[idx]
        y = layout.list_top + i * layout.list_item_h
        if idx == cursor:
            _fill_rect(renderer, 0, y, layout.width, layout.list_item_h, theme.HIGHLIGHT_BG)
            color = theme.HIGHLIGHT_TEXT
        else:
            color = theme.TEXT_COLOR
        icon_y = y + (layout.list_item_h - layout.icon_size) // 2
        if has_icons:
            ico = icons.get(item.item_type)  # type: ignore[call-overload]
            _render_icon(renderer, ico, layout.list_pad_left, icon_y, layout.icon_size)
        _render_text(renderer, font, item.title, text_x, y + max(3, layout.list_item_h // 7),
                     color, layout.width - text_x - layout.list_pad_right)

    draw_status_bar(
        renderer, font,
        f"A=Open  B=Back  L/R=Page  Start=Quit  ({cursor+1}/{len(items)})",
        layout,
    )


def draw_playback(renderer, font, state: "BrowserState", layout: theme.Layout, icons: dict | None = None) -> None:
    if not state.playback_hud_visible:
        return

    top_h = layout.playback_hud_top
    bottom_h = layout.playback_hud_bottom
    width = layout.width
    height = layout.height
    left = layout.playback_hud_side
    right = layout.playback_hud_side
    status = "Paused" if state.playback_paused else "Playing"
    total = max(0.0, state.playback_duration)
    current = max(0.0, min(state.playback_position, total if total else state.playback_position))
    progress = (current / total) if total > 0 else 0.0
    progress_x = left
    progress_w = width - left - right

    status_icon = icons.get("hud-pause" if state.playback_paused else "hud-play") if icons else None
    volume_icon = icons.get("hud-volume") if icons else None
    seek_icon = icons.get("hud-seek") if icons else None
    stop_icon = icons.get("hud-stop") if icons else None
    hud_icon = icons.get("hud-display") if icons else None

    hud_mode = {
        theme.PLAYBACK_HUD_AUTO: "AUTO",
        theme.PLAYBACK_HUD_PINNED: "FIXED",
        theme.PLAYBACK_HUD_HIDDEN: "HIDDEN",
    }.get(state.playback_hud_mode, "AUTO")
    time_text = f"{_format_time(current)} / {_format_time(total)}"
    time_w = layout.playback_time_w
    time_x = width - left - time_w
    title_x = left + layout.playback_status_w + layout.playback_hud_icon_size + 8
    title_w = max(0, time_x - title_x - 12)
    title_text = _fit_text(font, state.playback_title or "...", title_w)

    _fill_rect(renderer, 0, 0, width, top_h, theme.PLAYBACK_OVERLAY_BG)
    _fill_rect(renderer, 0, height - bottom_h, width, bottom_h, theme.PLAYBACK_OVERLAY_BG)
    _render_icon(renderer, status_icon, left, max(5, top_h // 5), layout.playback_hud_icon_size)
    _render_text(renderer, font, status, left + layout.playback_hud_icon_size + 6, max(4, top_h // 6), theme.TEXT_COLOR, layout.playback_status_w)
    _render_text(renderer, font, title_text,
                 title_x, 5, theme.TEXT_COLOR, title_w)
    _render_text(renderer, font, time_text,
                 time_x, 5, theme.DIM_TEXT, time_w)

    progress_y = height - bottom_h + 10
    _draw_progress_bar(renderer, progress_x, progress_y, progress_w, layout.playback_progress_h, progress)

    info_y = height - bottom_h + max(20, bottom_h // 2 - layout.playback_hud_icon_size // 2)
    _render_icon(renderer, volume_icon, left, info_y + 1, layout.playback_hud_icon_size)
    _render_text(renderer, font, f"{state.playback_volume}%", left + 22, info_y - 1, theme.TEXT_COLOR, 64)

    buffer_text = f"Buffer {state.playback_buffer_percent}%" if state.playback_buffer_percent < 100 else state.playback_resolution or ""
    _render_text(renderer, font, buffer_text, left + max(88, layout.playback_status_w + 18), info_y - 1, theme.DIM_TEXT, layout.playback_buffer_w)
    _render_text(renderer, font, hud_mode, width - 70, info_y - 1, theme.DIM_TEXT, 60)

    controls_y = height - layout.playback_bottom_margin
    cx = left
    _render_icon(renderer, volume_icon, cx, controls_y - 1, layout.playback_hud_icon_size)
    _render_text(renderer, font, "U/D", cx + 20, controls_y - 2, theme.TEXT_COLOR, 38)
    cx += 70
    _render_icon(renderer, status_icon, cx, controls_y - 1, layout.playback_hud_icon_size)
    _render_text(renderer, font, "A", cx + 20, controls_y - 2, theme.TEXT_COLOR, 18)
    cx += 44
    _render_icon(renderer, seek_icon, cx, controls_y - 1, layout.playback_hud_icon_size)
    _render_text(renderer, font, "L/R", cx + 20, controls_y - 2, theme.TEXT_COLOR, 34)
    cx += 62
    _render_icon(renderer, stop_icon, cx, controls_y - 1, layout.playback_hud_icon_size)
    _render_text(renderer, font, "B", cx + 20, controls_y - 2, theme.TEXT_COLOR, 18)
    cx += 42
    _render_icon(renderer, hud_icon, cx, controls_y - 1, layout.playback_hud_icon_size)
    _render_text(renderer, font, "Y", cx + 20, controls_y - 2, theme.TEXT_COLOR, 18)


def draw_error(renderer, font, state: "BrowserState", layout: theme.Layout) -> None:
    draw_header(renderer, font, "Error", layout)
    _render_text(renderer, font, state.error or "Unknown error",
                 layout.list_pad_left, layout.height // 2 - 20, theme.ERROR_COLOR,
                 layout.width - layout.list_pad_left * 2)
    draw_status_bar(renderer, font, "A=OK", layout)
