#!/usr/bin/env python3
"""Give apps in Steam's VR "+" menu their icons.

Steam looks icons up only in the hicolor theme and pixmaps, so apps whose icon only KDE's Breeze
theme has (Konsole, System Settings, ...) show without one. This copies those Breeze icons into
~/.local/share/icons/hicolor/scalable/apps/. Desktop mode keeps using Breeze (hicolor is only
the fallback theme), so nothing changes there.

  menu-icons install    copy the missing icons (and list them)
  menu-icons remove     remove what install copied
  menu-icons status
"""
import configparser, os, shutil, sys
from pathlib import Path

sys.path.insert(0, os.environ.get("FRAME_TWEAKS_PYLIB") or str(Path.home() / ".local/share/frame-tweaks/i18n"))   # (lib/common.sh install_i18n)
try:
    from i18n import _, N_, ngettext
except ImportError:                       # (not installed by Frame Tweaks: English)
    def _(text):
        return text

    def ngettext(singular, plural, n):
        return singular if n == 1 else plural
    N_ = _

HOME = Path.home()
DEST = HOME / ".local/share/icons/hicolor/scalable/apps"
LIST = HOME / ".local/share/frame-tweaks/menu-icons.list"
BREEZE = Path("/usr/share/icons/breeze")
ALWAYS = ["utilities-terminal", "preferences-system"]       # Konsole, System Settings
APP_DIRS = [Path("/usr/share/applications"), HOME / ".local/share/applications",
            Path("/var/lib/flatpak/exports/share/applications"),
            HOME / ".local/share/flatpak/exports/share/applications"]
HICOLOR = [Path("/usr/share/icons/hicolor"), HOME / ".local/share/icons/hicolor",
           Path("/var/lib/flatpak/exports/share/icons/hicolor"),
           HOME / ".local/share/flatpak/exports/share/icons/hicolor"]
PIXMAPS = [Path("/usr/share/pixmaps"), HOME / ".local/share/pixmaps"]


def menu_icons():
    """Icon names of apps that can appear in the menu."""
    names = set(ALWAYS)
    for d in APP_DIRS:
        for f in d.glob("*.desktop") if d.is_dir() else []:
            cp = configparser.RawConfigParser(strict=False, interpolation=None)
            try:
                cp.read(f, encoding="utf-8")
                e = cp["Desktop Entry"]
            except Exception:
                continue
            if e.get("NoDisplay", "").lower() == "true" or e.get("Type", "Application") != "Application":
                continue
            icon = e.get("Icon", "").strip()
            if icon and "/" not in icon:
                names.add(icon)
    return names


def in_hicolor(name, ours):
    for root in HICOLOR:
        for p in root.glob(f"*/apps/{name}.*") if root.is_dir() else []:
            if p not in ours:
                return True
    return any(p.is_dir() and any(p.glob(f"{name}.*")) for p in PIXMAPS)


def breeze_svg(name):
    hits = sorted(BREEZE.glob(f"apps/*/{name}.svg"), key=lambda p: p.parent.name != "48") or \
        sorted(BREEZE.glob(f"*/*/{name}.svg"))
    return hits[0] if hits else None


def ours():
    try:
        return [Path(l) for l in LIST.read_text().split("\n") if l]
    except OSError:
        return []


def install():
    mine = set(ours())
    added = []
    for name in sorted(menu_icons()):
        src = breeze_svg(name)
        if src is None or in_hicolor(name, mine):
            continue
        DEST.mkdir(parents=True, exist_ok=True)
        dst = DEST / f"{name}.svg"
        shutil.copyfile(src.resolve(), dst)
        added.append(dst)
    LIST.parent.mkdir(parents=True, exist_ok=True)
    LIST.write_text("".join(f"{p}\n" for p in sorted(set(added) | {p for p in mine if p.exists()})))
    for d in (HOME / ".local/share/icons/hicolor",              # theme caches notice
              HOME / ".local/share/applications"):              # Steam rescans its "+" menu
        if d.is_dir():
            os.utime(d, None)
    for p in added:
        print(_("icon: {name}").format(name=p.stem))


def remove():
    for p in ours():
        p.unlink(missing_ok=True)
    LIST.unlink(missing_ok=True)


def status():
    n = len([p for p in ours() if p.exists()])
    print(n)
    return 0 if n else 1


if __name__ == "__main__":
    fn = {"install": install, "remove": remove, "status": status}.get(sys.argv[1] if len(sys.argv) > 1 else "")
    if fn is None:
        sys.exit(__doc__)
    sys.exit(fn() or 0)
