#!/usr/bin/python3

import errno
import glob
import json
import os.path
import logging
from optparse import OptionParser
import shutil
import sys

from gi.repository import Click


WRAPPER="""\
#!/bin/sh
##TARGET={target}
#TMPDIR=/run/apps/{pkgname}
cd {pkgroot}
aa-exec -p {profile} -- {target} "$@"
"""


def get_wrapper_target(bin_wrapper):
    for line in open(bin_wrapper):
        line = line.strip()
        if line.startswith("##TARGET="):
            return line.split("=", 1)[1]
    return None


def get_aa_profile_for_app(app_tiple):
    # FIXME: make this nicer, terrible hardcoded etc.
    clickhook_path = '/var/lib/apparmor/clicks/{}.json'.format(app_triple)
    if os.path.exists(clickhook_path):
        return app_triple
    # FIXME: and this too
    custom_profile_path = '/var/lib/apparmor/profiles/profile_{}'.format(
        app_triple)
    if os.path.exists(custom_profile_path):
        return app_triple
    return "unconfined"


def _get_package_root_for_bin_path(bin_path):
    path = os.path.abspath(bin_path)
    while True:
        manifests = glob.glob(path+"/.click/info/*.manifest")
        if manifests:
            return path
        path = os.path.normpath(os.path.join(path, ".."))
        # can this happen?
        if path == "/":
            return None
    

def _get_manifest_for_bin_path(bin_path):
    package_root = _get_package_root_for_bin_path(bin_path)
    manifests = glob.glob(package_root+"/.click/info/*.manifest")
    with open(manifests[0]) as fp:
        manifest = json.load(fp)
    return manifest


def is_framework(bin_path):
    manifest = _get_manifest_for_bin_path(bin_path)
    hooks = manifest.get("hooks", {})
    for name, hook in hooks.items():
        for k, v in hook.items():
            if k == "framework":
                return True
    return False


def remove_all_wrappers_for(target, from_path):
    for f in os.listdir(from_path):
        p = os.path.join(from_path, f)
        if get_wrapper_target(p) == target:
            os.unlink(p)


def get_link_origin_for(target, from_path):
    for f in os.listdir(from_path):
        p = os.path.join(from_path, f)
        if os.path.realpath(p) == target:
            return p
    return None

def get_pkgname(bin_path):
    manifest = _get_manifest_for_bin_path(bin_path)
    return manifest["name"]


if __name__  == "__main__":
    parser = OptionParser()
    parser.add_option("-v", "--verbose", default=False, action="store_true",
                      help="verbose output")
    parser.add_option("", "--srcdir", default="~/.cache/snappy-bin-path/",
                      help="Source directory to use")
    parser.add_option("", "--dstdir", default="~/snappy-bin/",
                      help="Target directory to use")
    (options, args) = parser.parse_args()

    srcdir = os.path.expanduser(options.srcdir)
    dstdir = os.path.expanduser(options.dstdir)
    Click.ensuredir(dstdir)

    if options.verbose or os.environ.get("CLICK_BIN_PATH_DEBUG", ""):
        logging.basicConfig(level=logging.DEBUG)
        logging.info("Enable debugging")

    # if there is no srcdir (yet) there is nothing to do for us
    if not os.path.exists(srcdir):
        logging.debug("no srcdir, exiting")
        sys.exit(0)

    success = True
    src = set([os.path.realpath(os.path.join(srcdir, f))
               for f in os.listdir(srcdir)
               if os.path.islink(os.path.join(srcdir, f))])
    dst = set([get_wrapper_target(os.path.join(dstdir, f))
               for f in os.listdir(dstdir)
               if get_wrapper_target(os.path.join(dstdir, f))])
    logging.debug("src: %s" % src)
    logging.debug("dst: %s" % dst)

    for removed in dst - src:
        remove_all_wrappers_for(removed, from_path=dstdir)

    for new in src - dst:
        pkgname = get_pkgname(os.path.join(srcdir, new))
        if not is_framework(os.path.join(srcdir, new)):
            dest = os.path.join(dstdir, pkgname + "." + os.path.basename(new))
        else:
            dest =  os.path.join(dstdir, os.path.basename(new))
        if os.path.exists(dest):
            logging.warning("%s already exists, ignoring %s" % (dest, new))
            success = False
            continue
        with open(dest, "w") as fp:
            app_triple = os.path.basename(
                get_link_origin_for(new, from_path=srcdir))
            profile=get_aa_profile_for_app(app_triple)
            pkgroot=_get_package_root_for_bin_path(os.path.join(srcdir, new))
            s=WRAPPER.format(target=new,
                             pkgroot=pkgroot,
                             pkgname=pkgname,
                             profile=profile)
            fp.write(s)
        os.chmod(dest, 0o755)

    if not success:
        sys.exit(1)

