#! /usr/bin/python3
#
# Based on: https://gitlab.freedesktop.org/xdg/desktop-file-utils/-/blob/1926ae7021a2f8e842ad566a49f3a947c02cec92/src/update-desktop-database.c
#
# Relies on debian/triggers (which calls into debian/postinst), to configure
# system-global default mime types automatically.

from __future__ import annotations

import configparser
import fnmatch
import glob
import mimetypes
import os
import os.path
import typing
import xml.etree.ElementTree

XDG_DATA_DIRS = os.getenv("XDG_DATA_DIRS", "/usr/local/share/:/usr/share/").split(":")


def iter_mimetypes() -> typing.Iterator[str]:
    mimetypes.init()
    yield from mimetypes.types_map.values()
    yield from mimetypes.common_types.values()

    ns = {
        "mime": "http://www.freedesktop.org/standards/shared-mime-info",
    }

    for data_dir in XDG_DATA_DIRS:
        mime_dir = os.path.join(data_dir, "mime", "packages")
        for root, dirs, files in os.walk(mime_dir):
            for file in files:
                et = xml.etree.ElementTree.parse(os.path.join(root, file))
                for el in et.findall("mime:mime-type", ns):
                    if mime := el.attrib.get("type"):
                        yield mime


# Sane fallbacks to use when nothing else is more-preferred.
#
# Ordered by preference, most-preferred last.
fallbacks = {
    "audio/*": "vlc.desktop",
    "image/*": "org.gnome.eog.desktop",
    "text/*": "org.gnome.gedit.desktop",
    "video/*": "vlc.desktop",
}

# Ordered by preference, most-preferred last.
#
# The only applications that should appear here are ones for which multiple
# applications handle the same type, but of which a specific application is
# preferred.
preferred = [
    "org.gnome.Nautilus.desktop",
    "google-chrome.desktop",
    "prusa-slicer.desktop",
    "org.gnome.gedit.desktop",
    "org.gnome.Evince.desktop",
    "org.gnome.eog.desktop",
    "libreoffice-writer.desktop",
    "libreoffice-calc.desktop",
    "libreoffice-impress.desktop",
    "vlc.desktop",
]

applications = (
    (desktop_file, path)
    for desktop_file in preferred
    for data_dir in XDG_DATA_DIRS
    for path in glob.iglob(
        os.path.join(data_dir, "applications", "**", desktop_file),
        recursive=True,
    )
)

# System-wide default applications
defaults: dict[str, str] = {}

for mime in iter_mimetypes():
    for pattern, desktop_file in fallbacks.items():
        if fnmatch.fnmatch(mime, pattern):
            defaults[mime] = desktop_file

for desktop_file, path in applications:
    desk = configparser.ConfigParser()
    desk.read(path)

    for mime in desk.get("Desktop Entry", "MimeType", fallback="").split(";"):
        if mime := mime.strip():
            defaults[mime] = desktop_file

out = configparser.ConfigParser()
out.add_section("Default Applications")

for mime, app in sorted(defaults.items()):
    out.set("Default Applications", mime, app)

with open("/etc/xdg/mimeapps.list", "w") as f:
    # xdg-{open,mime} are sensitive to spaces around "=" in the output
    out.write(f, space_around_delimiters=False)
