#!/usr/bin/python3
# -*- coding: utf-8 -*-

import os
import xdg.DesktopEntry as dent
import os.path as path
import subprocess
import re
from os import environ as env
from sys import argv
from collections import OrderedDict
import ipc

def which(f):
    if path.isabs(f) and path.isfile(f) and os.access(f, os.X_OK):
        return f
    for d in env.get("PATH").split(':'):
        p = path.join(d, f)
        if path.isabs(p) and path.isfile(p) and os.access(p, os.X_OK):
            return p
    return None


def uniq_list(l):
    return list(OrderedDict.fromkeys(l))


def quote(s):
    res = re.sub(r'[`$\\]', lambda match: '\\'+match.group(0) , s)
    res = u"\"" + res + u"\"" if res!=s or s==u"" else s
    return res


def main(argv):
    localdir = env.get("XDG_DATA_HOME", None) or path.join(env["HOME"], ".local", "share")
    globaldir = (env.get("XDG_DATA_DIRS", None) or "/usr/local/share/:/usr/share/").split(':')
    searchdirs = uniq_list([path.join(d, "applications") for d in [localdir] + globaldir])

    desktops = []
    for d in searchdirs:
        for base, dirs, files in os.walk(d):
            for name in files:
                if name.endswith(".desktop"):
                    desktops.append(path.join(base, name))

    pick = {}
    for f in desktops:
        dsk = dent.DesktopEntry(f)
        if not(dsk.getType()=="Application" or dsk.getExec()):
            continue
        if dsk.getNoDisplay() or dsk.getHidden():
            continue
        tryexec = dsk.getTryExec()
        if tryexec and not which(tryexec):
            continue

        generic = dsk.getGenericName()
        if not isinstance(generic, str):
            generic = generic.decode("utf-8")
        name = dsk.getName()
        if not isinstance(name, str):
            name = name.decode("utf-8")
        dsk.name = name # ugly, sure
        label = name
        if generic:
            label = name + u" - " + generic
        i = 2
        while label in pick:
            label = u"%s [%d]" % (name, i)
            i += 1

        pick[label] = dsk

    dmenu = subprocess.Popen(['dmenu']+argv[1:], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    dmenulist = "\n".join(sorted(pick.keys())).encode("utf-8")
    dmenustdout = dmenu.communicate(dmenulist)[0]
    picked = dmenustdout.rstrip(b"\n").decode("utf-8")
    if picked:
        try:
            dsk = pick[picked]
            cmd = dsk.getExec()
            cmd = re.sub(r'%[fFuUdDnNivm]', '', cmd)
            cmd = cmd.replace(u'%c', quote(dsk.name))
            cmd = cmd.replace(u'%k', quote(dsk.filename))
            cmd = cmd.replace(u'%%', u'%')
        except KeyError:
            cmd = quote(picked)

        ipc.i3().command(ipc.COMMAND, "exec %s" % cmd)


if __name__=="__main__":
    main(argv)