from __future__ import annotations

from typing import TYPE_CHECKING

import pyray as rl

from grim.fonts.small import SmallFontData, draw_small_text, measure_small_text_width
from grim.geom import Vec2

from ...frontend.high_scores_layout import (
    HS_LOCAL_CLOCK_X,
    HS_LOCAL_CLOCK_Y,
    HS_LOCAL_DATE_X,
    HS_LOCAL_DATE_Y,
    HS_LOCAL_FRAGS_X,
    HS_LOCAL_FRAGS_Y,
    HS_LOCAL_HIT_X,
    HS_LOCAL_HIT_Y,
    HS_LOCAL_LABEL_X,
    HS_LOCAL_LABEL_Y,
    HS_LOCAL_NAME_X,
    HS_LOCAL_NAME_Y,
    HS_LOCAL_RANK_X,
    HS_LOCAL_RANK_Y,
    HS_LOCAL_SCORE_LABEL_X,
    HS_LOCAL_SCORE_LABEL_Y,
    HS_LOCAL_SCORE_VALUE_X,
    HS_LOCAL_SCORE_VALUE_Y,
    HS_LOCAL_TIME_LABEL_X,
    HS_LOCAL_TIME_LABEL_Y,
    HS_LOCAL_TIME_VALUE_X,
    HS_LOCAL_TIME_VALUE_Y,
    HS_LOCAL_WEAPON_Y,
    HS_LOCAL_WICON_X,
    HS_LOCAL_WICON_Y,
    HS_RIGHT_CHECK_X,
    HS_RIGHT_CHECK_Y,
    HS_RIGHT_GAME_MODE_DROP_X,
    HS_RIGHT_GAME_MODE_DROP_Y,
    HS_RIGHT_GAME_MODE_VALUE_X,
    HS_RIGHT_GAME_MODE_VALUE_Y,
    HS_RIGHT_GAME_MODE_WIDGET_W,
    HS_RIGHT_GAME_MODE_WIDGET_X,
    HS_RIGHT_GAME_MODE_WIDGET_Y,
    HS_RIGHT_GAME_MODE_X,
    HS_RIGHT_GAME_MODE_Y,
    HS_RIGHT_NUMBER_PLAYERS_X,
    HS_RIGHT_NUMBER_PLAYERS_Y,
    HS_RIGHT_PLAYER_COUNT_DROP_X,
    HS_RIGHT_PLAYER_COUNT_DROP_Y,
    HS_RIGHT_PLAYER_COUNT_VALUE_X,
    HS_RIGHT_PLAYER_COUNT_VALUE_Y,
    HS_RIGHT_PLAYER_COUNT_WIDGET_W,
    HS_RIGHT_PLAYER_COUNT_WIDGET_X,
    HS_RIGHT_PLAYER_COUNT_WIDGET_Y,
    HS_RIGHT_SCORE_LIST_DROP_X,
    HS_RIGHT_SCORE_LIST_DROP_Y,
    HS_RIGHT_SCORE_LIST_VALUE_X,
    HS_RIGHT_SCORE_LIST_VALUE_Y,
    HS_RIGHT_SCORE_LIST_WIDGET_W,
    HS_RIGHT_SCORE_LIST_WIDGET_X,
    HS_RIGHT_SCORE_LIST_WIDGET_Y,
    HS_RIGHT_SCORE_LIST_X,
    HS_RIGHT_SCORE_LIST_Y,
    HS_RIGHT_SHOW_INTERNET_X,
    HS_RIGHT_SHOW_INTERNET_Y,
    HS_RIGHT_SHOW_SCORES_DROP_X,
    HS_RIGHT_SHOW_SCORES_DROP_Y,
    HS_RIGHT_SHOW_SCORES_VALUE_X,
    HS_RIGHT_SHOW_SCORES_VALUE_Y,
    HS_RIGHT_SHOW_SCORES_WIDGET_W,
    HS_RIGHT_SHOW_SCORES_WIDGET_X,
    HS_RIGHT_SHOW_SCORES_WIDGET_Y,
    HS_RIGHT_SHOW_SCORES_X,
    HS_RIGHT_SHOW_SCORES_Y,
    hs_right_local_card_x_shift,
    hs_right_options_x_shift,
)
from ...frontend.panels.hit_test import mouse_inside_rect_with_padding
from .shared import format_elapsed_mm_ss, format_score_date, ordinal

if TYPE_CHECKING:
    from .view import HighScoresView


def _saved_score_names(view: "HighScoresView") -> list[str]:
    slot_count = max(1, min(8, int(view.state.config.int_value("saved_name_index", 1))))
    names_blob = view.state.config.blob_value("saved_names", size=0x1B * 8, default=b"")  # 8 entries
    names: list[str] = []
    for i in range(slot_count):
        entry = names_blob[i * 0x1B : (i + 1) * 0x1B]
        label = entry.split(b"\x00", 1)[0].decode("latin-1", errors="ignore").strip()
        if not label:
            label = "default" if i == 0 else f"slot_{i}"
        names.append(label)
    return names


def _draw_dropdown(
    view: "HighScoresView",
    *,
    font: SmallFontData,
    widget_pos: Vec2,
    widget_w: float,
    items: list[str] | tuple[str, ...],
    selected_index: int,
    value_pos: Vec2,
    arrow_pos: Vec2,
    is_open: bool,
    enabled: bool,
    scale: float,
) -> None:
    item_count = max(0, len(items))
    header_h = 16.0 * scale
    row_h = 16.0 * scale
    full_h = (float(item_count) * 16.0 + 24.0) * scale
    rows_y0 = widget_pos.y + 17.0 * scale

    mouse = rl.get_mouse_position()
    hovered_header = bool(enabled) and mouse_inside_rect_with_padding(
        mouse,
        pos=widget_pos,
        width=widget_w,
        height=14.0 * scale,
    )

    widget_h = full_h if is_open else header_h
    rl.draw_rectangle(int(widget_pos.x), int(widget_pos.y), int(widget_w), int(widget_h), rl.WHITE)
    rl.draw_rectangle(
        int(widget_pos.x) + 1,
        int(widget_pos.y) + 1,
        max(0, int(widget_w) - 2),
        max(0, int(widget_h) - 2),
        rl.BLACK,
    )

    if (is_open or hovered_header) and enabled:
        line_h = max(1, int(1.0 * scale))
        rl.draw_rectangle(
            int(widget_pos.x),
            int(widget_pos.y + 15.0 * scale),
            int(widget_w),
            line_h,
            rl.Color(255, 255, 255, 128),
        )

    arrow_tex = view._drop_on if ((is_open or hovered_header) and enabled) else view._drop_off
    if arrow_tex is None:
        arrow_tex = view._drop_off
    if arrow_tex is not None:
        arrow_w = float(arrow_tex.width) * scale
        arrow_h = float(arrow_tex.height) * scale
        rl.draw_texture_pro(
            arrow_tex,
            rl.Rectangle(0.0, 0.0, float(arrow_tex.width), float(arrow_tex.height)),
            rl.Rectangle(arrow_pos.x, arrow_pos.y, arrow_w, arrow_h),
            rl.Vector2(0.0, 0.0),
            0.0,
            rl.WHITE,
        )

    if item_count <= 0:
        return

    selected_index = max(0, min(item_count - 1, int(selected_index)))
    header_alpha = 242 if ((is_open or hovered_header) and enabled) else 191
    draw_small_text(
        font,
        str(items[selected_index]),
        value_pos,
        1.0 * scale,
        rl.Color(255, 255, 255, header_alpha),
    )

    if not is_open:
        return

    for idx, label in enumerate(items):
        item_y = rows_y0 + row_h * float(idx)
        hovered = bool(enabled) and mouse_inside_rect_with_padding(
            mouse,
            pos=Vec2(widget_pos.x, item_y),
            width=widget_w,
            height=14.0 * scale,
        )
        alpha = 153
        if hovered:
            alpha = 242
        if idx == selected_index:
            alpha = max(alpha, 245)
        draw_small_text(
            font,
            str(label),
            Vec2(value_pos.x, item_y),
            1.0 * scale,
            rl.Color(255, 255, 255, alpha),
        )


def draw_right_panel(
    view: "HighScoresView",
    *,
    font: SmallFontData,
    right_top_left: Vec2,
    scale: float,
    highlight_rank: int | None,
) -> None:
    if highlight_rank is None:
        _draw_right_panel_quest_options(view, font=font, right_top_left=right_top_left, scale=scale)
        return
    _draw_right_panel_local_score(
        view,
        font=font,
        right_top_left=right_top_left,
        scale=scale,
        highlight_rank=highlight_rank,
    )


def _draw_right_panel_quest_options(
    view: "HighScoresView",
    *,
    font: SmallFontData,
    right_top_left: Vec2,
    scale: float,
) -> None:
    options_shift_x = hs_right_options_x_shift(float(view.state.config.screen_width))
    options_top_left = right_top_left + Vec2(options_shift_x * scale, 0.0)
    text_scale = 1.0 * scale
    text_color = rl.Color(255, 255, 255, int(255 * 0.8))

    # Checkbox: "Show internet scores"
    check_tex = view._check_on if view.state.config.score_load_gate else view._check_off
    if check_tex is not None:
        check_w = float(check_tex.width) * scale
        check_h = float(check_tex.height) * scale
        rl.draw_texture_pro(
            check_tex,
            rl.Rectangle(0.0, 0.0, float(check_tex.width), float(check_tex.height)),
            rl.Rectangle(
                options_top_left.x + HS_RIGHT_CHECK_X * scale,
                options_top_left.y + HS_RIGHT_CHECK_Y * scale,
                check_w,
                check_h,
            ),
            rl.Vector2(0.0, 0.0),
            0.0,
            rl.WHITE,
        )
    draw_small_text(
        font,
        "Show internet scores",
        options_top_left + Vec2(HS_RIGHT_SHOW_INTERNET_X * scale, HS_RIGHT_SHOW_INTERNET_Y * scale),
        text_scale,
        text_color,
    )
    draw_small_text(
        font,
        "Number of players",
        options_top_left + Vec2(HS_RIGHT_NUMBER_PLAYERS_X * scale, HS_RIGHT_NUMBER_PLAYERS_Y * scale),
        text_scale,
        text_color,
    )
    draw_small_text(
        font,
        "Game mode",
        options_top_left + Vec2(HS_RIGHT_GAME_MODE_X * scale, HS_RIGHT_GAME_MODE_Y * scale),
        text_scale,
        text_color,
    )
    draw_small_text(
        font,
        "Show scores:",
        options_top_left + Vec2(HS_RIGHT_SHOW_SCORES_X * scale, HS_RIGHT_SHOW_SCORES_Y * scale),
        text_scale,
        text_color,
    )
    draw_small_text(
        font,
        "Selected score list:",
        options_top_left + Vec2(HS_RIGHT_SCORE_LIST_X * scale, HS_RIGHT_SCORE_LIST_Y * scale),
        text_scale,
        text_color,
    )

    # Dropdown widgets (state_14 quest variant).
    show_scores_items = ("Best of all time", "Best of month", "Best of week", "Best of day")
    player_items = ("1 player", "2 players", "3 players", "4 players")
    mode_items: list[tuple[str, int]] = [("Quests", 3), ("Rush", 2), ("Survival", 1)]
    if int(view.state.status.quest_unlock_index) >= 0x28:
        mode_items.append(("Typ'o'Shooter", 4))
    names = _saved_score_names(view)

    player_count = max(1, min(4, view.state.config.player_count))
    player_selected = player_count - 1
    show_scores_selected = max(0, min(len(show_scores_items) - 1, view.state.config.highscore_date_mode))
    mode_id = view.state.config.game_mode
    mode_selected = 0
    for idx, (_label, _id) in enumerate(mode_items):
        if int(_id) == int(mode_id):
            mode_selected = idx
            break
    name_selected = max(0, min(len(names) - 1, int(view.state.config.int_value("selected_name_slot", 0))))

    dropdowns = (
        (
            view._player_count_open,
            Vec2(HS_RIGHT_PLAYER_COUNT_WIDGET_X, HS_RIGHT_PLAYER_COUNT_WIDGET_Y),
            float(HS_RIGHT_PLAYER_COUNT_WIDGET_W),
            list(player_items),
            player_selected,
            Vec2(HS_RIGHT_PLAYER_COUNT_VALUE_X, HS_RIGHT_PLAYER_COUNT_VALUE_Y),
            Vec2(HS_RIGHT_PLAYER_COUNT_DROP_X, HS_RIGHT_PLAYER_COUNT_DROP_Y),
            not (view._game_mode_open or view._show_scores_open or view._score_list_open),
        ),
        (
            view._game_mode_open,
            Vec2(HS_RIGHT_GAME_MODE_WIDGET_X, HS_RIGHT_GAME_MODE_WIDGET_Y),
            float(HS_RIGHT_GAME_MODE_WIDGET_W),
            [label for label, _id in mode_items],
            mode_selected,
            Vec2(HS_RIGHT_GAME_MODE_VALUE_X, HS_RIGHT_GAME_MODE_VALUE_Y),
            Vec2(HS_RIGHT_GAME_MODE_DROP_X, HS_RIGHT_GAME_MODE_DROP_Y),
            not (view._player_count_open or view._show_scores_open or view._score_list_open),
        ),
        (
            view._show_scores_open,
            Vec2(HS_RIGHT_SHOW_SCORES_WIDGET_X, HS_RIGHT_SHOW_SCORES_WIDGET_Y),
            float(HS_RIGHT_SHOW_SCORES_WIDGET_W),
            list(show_scores_items),
            show_scores_selected,
            Vec2(HS_RIGHT_SHOW_SCORES_VALUE_X, HS_RIGHT_SHOW_SCORES_VALUE_Y),
            Vec2(HS_RIGHT_SHOW_SCORES_DROP_X, HS_RIGHT_SHOW_SCORES_DROP_Y),
            not (view._player_count_open or view._game_mode_open or view._score_list_open),
        ),
        (
            view._score_list_open,
            Vec2(HS_RIGHT_SCORE_LIST_WIDGET_X, HS_RIGHT_SCORE_LIST_WIDGET_Y),
            float(HS_RIGHT_SCORE_LIST_WIDGET_W),
            names,
            name_selected,
            Vec2(HS_RIGHT_SCORE_LIST_VALUE_X, HS_RIGHT_SCORE_LIST_VALUE_Y),
            Vec2(HS_RIGHT_SCORE_LIST_DROP_X, HS_RIGHT_SCORE_LIST_DROP_Y),
            not (view._player_count_open or view._game_mode_open or view._show_scores_open),
        ),
    )
    # Active list must render last so overlapping widgets don't occlude open options.
    for is_open, widget_offset, widget_w, items, selected_index, value_offset, arrow_offset, enabled in dropdowns:
        if is_open:
            continue
        _draw_dropdown(
            view,
            font=font,
            widget_pos=options_top_left + widget_offset * scale,
            widget_w=widget_w * scale,
            items=items,
            selected_index=selected_index,
            value_pos=options_top_left + value_offset * scale,
            arrow_pos=options_top_left + arrow_offset * scale,
            is_open=is_open,
            enabled=bool(enabled),
            scale=scale,
        )
    for is_open, widget_offset, widget_w, items, selected_index, value_offset, arrow_offset, enabled in dropdowns:
        if not is_open:
            continue
        _draw_dropdown(
            view,
            font=font,
            widget_pos=options_top_left + widget_offset * scale,
            widget_w=widget_w * scale,
            items=items,
            selected_index=selected_index,
            value_pos=options_top_left + value_offset * scale,
            arrow_pos=options_top_left + arrow_offset * scale,
            is_open=is_open,
            enabled=bool(enabled),
            scale=scale,
        )


def _draw_right_panel_local_score(
    view: "HighScoresView",
    *,
    font: SmallFontData,
    right_top_left: Vec2,
    scale: float,
    highlight_rank: int | None,
) -> None:
    local_shift_x = hs_right_local_card_x_shift(float(view.state.config.screen_width))
    card_top_left = right_top_left + Vec2(local_shift_x * scale, 0.0)
    if not view._records:
        return
    idx = int(highlight_rank) if highlight_rank is not None else int(view._scroll_index)
    if idx < 0:
        idx = 0
    if idx >= len(view._records):
        idx = len(view._records) - 1
    entry = view._records[idx]

    text_scale = 1.0 * scale
    text_color = rl.Color(int(255 * 0.9), int(255 * 0.9), int(255 * 0.9), int(255 * 0.8))
    value_color = rl.Color(int(255 * 0.9), int(255 * 0.9), 255, 255)
    game_time_color = rl.Color(255, 255, 255, int(255 * 0.8))
    lower_section_color = rl.Color(int(255 * 0.9), int(255 * 0.9), int(255 * 0.9), int(255 * 0.7))
    separator_color = rl.Color(149, 175, 198, int(255 * 0.7))

    name = str(entry.name())
    if not name:
        name = "???"
    draw_small_text(
        font,
        name,
        card_top_left + Vec2(HS_LOCAL_NAME_X * scale, HS_LOCAL_NAME_Y * scale),
        text_scale,
        text_color,
    )
    draw_small_text(
        font,
        "Local score",
        card_top_left + Vec2(HS_LOCAL_LABEL_X * scale, HS_LOCAL_LABEL_Y * scale),
        text_scale,
        text_color,
    )
    rl.draw_line(
        int(card_top_left.x + 78.0 * scale),
        int(card_top_left.y + 57.0 * scale),
        int(card_top_left.x + 117.0 * scale),
        int(card_top_left.y + 57.0 * scale),
        separator_color,
    )

    date_text = format_score_date(entry)
    if date_text:
        draw_small_text(
            font,
            date_text,
            card_top_left + Vec2(HS_LOCAL_DATE_X * scale, HS_LOCAL_DATE_Y * scale),
            text_scale,
            text_color,
        )
    rl.draw_line(
        int(card_top_left.x + 74.0 * scale),
        int(card_top_left.y + 72.0 * scale),
        int(card_top_left.x + 266.0 * scale),
        int(card_top_left.y + 72.0 * scale),
        separator_color,
    )

    draw_small_text(
        font,
        "Score",
        card_top_left + Vec2(HS_LOCAL_SCORE_LABEL_X * scale, HS_LOCAL_SCORE_LABEL_Y * scale),
        text_scale,
        text_color,
    )

    mode_id = int(getattr(entry, "game_mode_id", 0) or 0)
    elapsed_ms = int(getattr(entry, "survival_elapsed_ms", 0) or 0)
    score_xp = int(getattr(entry, "score_xp", 0) or 0)

    draw_small_text(
        font,
        "Experience" if mode_id == 3 else "Game time",
        card_top_left + Vec2(HS_LOCAL_TIME_LABEL_X * scale, HS_LOCAL_TIME_LABEL_Y * scale),
        text_scale,
        game_time_color,
    )
    rl.draw_line(
        int(card_top_left.x + 170.0 * scale),
        int(card_top_left.y + 90.0 * scale),
        int(card_top_left.x + 170.0 * scale),
        int(card_top_left.y + 138.0 * scale),
        separator_color,
    )

    # Native highscore card:
    # - Rush/Quest: score is survival time in seconds (ms * 0.001), rendered with 2 decimals.
    # - Others: score is XP (u32).
    if mode_id in (2, 3):
        score_value = f"{max(0, elapsed_ms) * 0.001:.2f} secs"
    else:
        score_value = f"{score_xp}"
    score_value_pos = Vec2(HS_LOCAL_SCORE_VALUE_X * scale, HS_LOCAL_SCORE_VALUE_Y * scale)
    if mode_id in (2, 3):
        # Quest/Rush scores are variable-width second labels ("%.2f secs") and are
        # centered in the left score column in native.
        score_label_w = measure_small_text_width(font, "Score", text_scale)
        score_value_w = measure_small_text_width(font, score_value, text_scale)
        score_col_center_x = HS_LOCAL_SCORE_LABEL_X * scale + score_label_w * 0.5
        score_value_pos = Vec2(score_col_center_x - score_value_w * 0.5, HS_LOCAL_SCORE_VALUE_Y * scale)
    draw_small_text(
        font,
        score_value,
        card_top_left + score_value_pos,
        text_scale,
        value_color,
    )

    if mode_id == 3:
        draw_small_text(
            font,
            f"{score_xp}",
            card_top_left + Vec2(HS_LOCAL_TIME_VALUE_X * scale, HS_LOCAL_TIME_VALUE_Y * scale),
            text_scale,
            game_time_color,
        )
    else:
        _draw_clock_gauge(
            view,
            elapsed_ms=elapsed_ms,
            pos=card_top_left + Vec2(HS_LOCAL_CLOCK_X * scale, HS_LOCAL_CLOCK_Y * scale),
            scale=scale,
        )
        draw_small_text(
            font,
            format_elapsed_mm_ss(elapsed_ms),
            card_top_left + Vec2(HS_LOCAL_TIME_VALUE_X * scale, HS_LOCAL_TIME_VALUE_Y * scale),
            text_scale,
            game_time_color,
        )

    draw_small_text(
        font,
        f"Rank: {ordinal(idx + 1)}",
        card_top_left + Vec2(HS_LOCAL_RANK_X * scale, HS_LOCAL_RANK_Y * scale),
        text_scale,
        text_color,
    )

    frags = int(getattr(entry, "creature_kill_count", 0) or 0)

    shots_fired = int(getattr(entry, "shots_fired", 0) or 0)
    shots_hit = int(getattr(entry, "shots_hit", 0) or 0)
    hit_pct = 0
    if shots_fired > 0:
        hit_pct = int((shots_hit * 100) // shots_fired)
    rl.draw_line(
        int(card_top_left.x + 74.0 * scale),
        int(card_top_left.y + 142.0 * scale),
        int(card_top_left.x + 266.0 * scale),
        int(card_top_left.y + 142.0 * scale),
        separator_color,
    )

    weapon_id = int(getattr(entry, "most_used_weapon_id", 0) or 0)
    weapon_name, icon_index = _weapon_label_and_icon(view, weapon_id)
    if icon_index is not None:
        _draw_wicon(
            view,
            icon_index,
            pos=card_top_left + Vec2(HS_LOCAL_WICON_X * scale, HS_LOCAL_WICON_Y * scale),
            scale=scale,
        )
    weapon_name_x = HS_LOCAL_WICON_X * scale + max(
        0.0,
        32.0 * scale - measure_small_text_width(font, weapon_name, text_scale) * 0.5,
    )
    draw_small_text(
        font,
        weapon_name,
        card_top_left + Vec2(weapon_name_x, HS_LOCAL_WEAPON_Y * scale),
        text_scale,
        lower_section_color,
    )
    draw_small_text(
        font,
        f"Frags: {frags}",
        card_top_left + Vec2(HS_LOCAL_FRAGS_X * scale, HS_LOCAL_FRAGS_Y * scale),
        text_scale,
        lower_section_color,
    )
    draw_small_text(
        font,
        f"Hit %: {hit_pct}%",
        card_top_left + Vec2(HS_LOCAL_HIT_X * scale, HS_LOCAL_HIT_Y * scale),
        text_scale,
        lower_section_color,
    )
    rl.draw_line(
        int(card_top_left.x + 74.0 * scale),
        int(card_top_left.y + 194.0 * scale),
        int(card_top_left.x + 266.0 * scale),
        int(card_top_left.y + 194.0 * scale),
        separator_color,
    )


def _draw_clock_gauge(
    view: "HighScoresView",
    *,
    elapsed_ms: int,
    pos: Vec2,
    scale: float,
) -> None:
    table_tex = view._clock_table_tex
    pointer_tex = view._clock_pointer_tex
    if table_tex is None or pointer_tex is None:
        return
    draw_w = 32.0 * scale
    draw_h = 32.0 * scale
    dst = rl.Rectangle(pos.x, pos.y, draw_w, draw_h)
    src_table = rl.Rectangle(0.0, 0.0, float(table_tex.width), float(table_tex.height))
    src_pointer = rl.Rectangle(0.0, 0.0, float(pointer_tex.width), float(pointer_tex.height))
    rl.draw_texture_pro(
        table_tex,
        src_table,
        dst,
        rl.Vector2(0.0, 0.0),
        0.0,
        rl.WHITE,
    )
    seconds = max(0, int(elapsed_ms) // 1000)
    rotation_deg = float(seconds) * 6.0
    center = Vec2(pos.x + draw_w * 0.5, pos.y + draw_h * 0.5)
    rl.draw_texture_pro(
        pointer_tex,
        src_pointer,
        rl.Rectangle(center.x, center.y, draw_w, draw_h),
        rl.Vector2(draw_w * 0.5, draw_h * 0.5),
        rotation_deg,
        rl.WHITE,
    )


def _draw_wicon(
    view: "HighScoresView",
    icon_index: int,
    *,
    pos: Vec2,
    scale: float,
) -> None:
    tex = view._wicons_tex
    if tex is None:
        return
    idx = int(icon_index)
    if idx < 0 or idx > 31:
        return
    grid = 8
    cell_w = float(tex.width) / float(grid)
    cell_h = float(tex.height) / float(grid)
    frame = idx * 2
    src_x = float(frame % grid) * cell_w
    src_y = float(frame // grid) * cell_h
    icon_w = cell_w * 2.0
    icon_h = cell_h
    rl.draw_texture_pro(
        tex,
        rl.Rectangle(src_x, src_y, icon_w, icon_h),
        rl.Rectangle(pos.x, pos.y, icon_w * scale, icon_h * scale),
        rl.Vector2(0.0, 0.0),
        0.0,
        rl.WHITE,
    )


def _weapon_label_and_icon(view: "HighScoresView", weapon_id: int) -> tuple[str, int | None]:
    from ...weapons import WEAPON_BY_ID, weapon_display_name

    weapon = WEAPON_BY_ID.get(int(weapon_id))
    if weapon is None:
        return f"Weapon {int(weapon_id)}", None
    name = weapon_display_name(int(weapon.weapon_id), preserve_bugs=bool(view.state.preserve_bugs))
    return name, weapon.icon_index


__all__ = ["draw_right_panel"]
