#!/usr/bin/python3
import os, subprocess, json, sqlite3, shutil, tempfile
from pathlib import Path

def readfile(path):
    fd = open(path, "r")
    result = fd.read()
    fd.close()
    return result

#
# GTK apps
# 
vals = {}
home = os.environ["HOME"]
appsdir=["/usr/share/applications", f"{home}/.local/share/applications"]
for appdir in appsdir:
    if not os.path.isdir(appdir):
        continue

    for appfile in os.listdir(appdir):
        if not appfile.endswith(".desktop"):
            continue

        path = appdir + "/" + appfile
        content = readfile(path)

        _name = None
        _exec = None
        _type = None
        _nodisplay = ""
        _hidden = ""
        for line in content.splitlines(content):
            line = line.strip()

            if line.startswith("Exec="):
                _exec = line[5:]
            elif line.startswith("Type="):
                _type = line[5:]
            elif line.startswith("Name=") and _name is None:
                _name = line[5:]
            elif line.startswith("NoDisplay="):
                _nodisplay = line[10:]
            elif line.startswith("Hidden="):
                _hidden = line[7:]


        if _name is None or _exec is None:
            continue
        if _type is None or _type != "Application":
            continue
        if _nodisplay == "true" or _hidden == "true":
            continue

        vals[_name] = {"kind": "app", "exec": _exec, "path": path, "name": appfile[:-8]}

#
# Bin
# 
bindir = f"{home}/bin"
for file in os.listdir(bindir):
    path = f"{bindir}/{file}"
    if os.path.isdir(path):
        continue
    if file.startswith("."):
        continue
    vals[file] = {"kind": "bin", "path": path}

#
# Chromium bookmarks
# 
def walk_bookmarks(node, folder=""):
    if isinstance(node, dict):
        if node.get("type") == "url" and node.get("url"):
            name = node.get("name") or node["url"]
            label = f"{folder}/{name}" if folder else name
            vals[label] = {"kind": "url", "url": node["url"]}
        elif "children" in node:
            name = node.get("name", "")
            next_folder = f"{folder}/{name}" if folder and name else name or folder
            for child in node.get("children", []):
                walk_bookmarks(child, next_folder)

bookmarkfile = f"{home}/.config/chromium/Default/Bookmarks"
if os.path.isfile(bookmarkfile):
    content = readfile(bookmarkfile)
    data = json.loads(content)

    roots = data.get("roots", {})
    for root in roots.values():
        walk_bookmarks(root)

#
# Firefox bookmarks
#
SEARCH_DIRS = [
    Path.home() / ".config" / "librewolf",
]

def find_places_databases():
    seen = set()
    for base in SEARCH_DIRS:
        if not base.exists():
            continue
        for db in base.glob("**/places.sqlite"):
            resolved = db.resolve()
            if resolved not in seen:
                seen.add(resolved)
                yield db

def read_firefox_bookmarks(db_path):
    # Copy the DB first so this also works while Firefox/LibreWolf is running.
    with tempfile.TemporaryDirectory() as tmp:
        tmp_db = Path(tmp) / "places.sqlite"
        shutil.copy2(db_path, tmp_db)

        for suffix in ("-wal", "-shm"):
            sidecar = Path(str(db_path) + suffix)
            if sidecar.exists():
                shutil.copy2(sidecar, Path(str(tmp_db) + suffix))

        con = sqlite3.connect(f"file:{tmp_db}?mode=ro", uri=True)
        cur = con.cursor()
        query = """
            WITH RECURSIVE folders(id, path) AS (
                SELECT id, COALESCE(title, '')
                FROM moz_bookmarks
                WHERE parent = 0

                UNION ALL

                SELECT b.id,
                       CASE
                           WHEN folders.path = '' THEN COALESCE(b.title, '')
                           WHEN COALESCE(b.title, '') = '' THEN folders.path
                           ELSE folders.path || '/' || b.title
                       END
                FROM moz_bookmarks b
                JOIN folders ON b.parent = folders.id
                WHERE b.type = 2
            )
            SELECT COALESCE(folders.path, ''), COALESCE(b.title, ''), p.url
            FROM moz_bookmarks b
            JOIN moz_places p ON b.fk = p.id
            LEFT JOIN folders ON b.parent = folders.id
            WHERE b.type = 1
              AND p.url IS NOT NULL
            ORDER BY b.dateAdded
        """

        for folder, title, url in cur.execute(query):
            name = title or url
            label = f"{folder}/{name}" if folder else name
            vals[label] = {"kind": "url", "url": url}

        con.close()

for it in find_places_databases():
    read_firefox_bookmarks(it)

entries = []
for it in vals.items():
    entries.append(it[0])
entries_string = "\n".join(entries)
result = subprocess.run(["wmenu", "-l", "20", "-i"], input=entries_string, text=True, stdout=subprocess.PIPE)

if result.returncode != 0:
    exit(result.returncode)

choice = result.stdout.strip()
val = vals[choice]

if val["kind"] == "bin":
    subprocess.run([val["path"]])
elif val["kind"] == "app":
    subprocess.run(["gtk-launch", val["name"]])
elif val["kind"] == "url":
    subprocess.run(["xdg-open", val["url"]])
