#!/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
# !!!never remove this line!!!
##TARGET={target}

set -e

TMPDIR="/tmp/snapps/{pkg_name}/{pkg_version}/tmp"
if [ ! -d "$TMPDIR" ]; then
    mkdir -p -m1777 "$TMPDIR"
fi
export TMPDIR
export TEMPDIR="$TMPDIR"

# app paths (deprecated)
export SNAPP_APP_PATH="{pkg_path}"
export SNAPP_APP_DATA_PATH="/var/lib/{pkg_path}"
export SNAPP_APP_USER_DATA_PATH="$HOME/{pkg_path}"
export SNAPP_APP_TMPDIR="$TMPDIR"
export SNAPP_OLD_PWD="$(pwd)"

# app paths
export SNAP_APP_PATH="{pkg_path}"
export SNAP_APP_DATA_PATH="/var/lib/{pkg_path}"
export SNAP_APP_USER_DATA_PATH="$HOME/{pkg_path}"
export SNAP_APP_TMPDIR="$TMPDIR"

# export old pwd
export SNAP_OLD_PWD="$(pwd)"

# FIXME: this will need to become snappy arch or something
export SNAP_APP_ARCH="$(dpkg --print-architecture)"

if [ ! -d "$SNAP_APP_USER_DATA_PATH" ]; then
   mkdir -p "$SNAP_APP_USER_DATA_PATH"
fi
export HOME="$SNAP_APP_USER_DATA_PATH"

cd {pkg_path}
aa-exec -p {profile} -- {target} "$@"
"""


class ManifestNotFoundError(Exception):
    pass


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_triple):
    # 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)
    if not package_root:
        raise ManifestNotFoundError
    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"]


def get_pkgversion(bin_path):
    manifest = _get_manifest_for_bin_path(bin_path)
    return manifest["version"]


if __name__  == "__main__":
    parser = OptionParser()
    parser.add_option("-v", "--verbose", default=False, action="store_true",
                      help="verbose output")
    parser.add_option("", "--srcdir", default="/var/lib/click/bin/",
                      help="Source directory to use")
    parser.add_option("", "--dstdir", default="/apps/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)) and
                   os.path.exists(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).union(src & dst):
        try:
            pkgname = get_pkgname(os.path.join(srcdir, new))
            pkgversion = get_pkgversion(os.path.join(srcdir, new))
        except ManifestNotFoundError as e:
            logging.warning("Manifest for '%s' not found", new)
            continue
        if not is_framework(os.path.join(srcdir, new)):
            dest = os.path.join(dstdir, os.path.basename(new) + "." + pkgname)
        else:
            dest =  os.path.join(dstdir, os.path.basename(new))
        if os.path.exists(dest):
            logging.debug("%s already exists, overwriting %s" % (dest, new))
        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)
            pkg_path=_get_package_root_for_bin_path(os.path.join(srcdir, new))
            
            s=WRAPPER.format(target=new,
                             pkg_path=pkg_path,
                             pkg_name=pkgname,
                             pkg_version=pkgversion,
                             profile=profile)
            fp.write(s)
        os.chmod(dest, 0o755)

    if not success:
        sys.exit(1)

