#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPLv3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>

import os
import plistlib
from glob import glob

from bypy.macos_sign import codesign, create_entitlements_file, make_certificate_useable, notarize_app, verify_signature
from bypy.utils import current_dir

entitlements = {
    # MAP_JIT is used by libpcre which is bundled with Qt
    'com.apple.security.cs.allow-jit': True,

    # v8 and therefore WebEngine need this as they don't use MAP_JIT
    'com.apple.security.cs.allow-unsigned-executable-memory': True,

    # calibre itself does not use DYLD env vars, but don't know about its
    # dependencies.
    'com.apple.security.cs.allow-dyld-environment-variables': True,

    # Allow loading of unsigned plugins or frameworks
    # 'com.apple.security.cs.disable-library-validation': True,
}


def files_in(folder):
    for record in os.walk(folder):
        for f in record[-1]:
            yield os.path.join(record[0], f)


def expand_dirs(items, exclude=lambda x: x.endswith('.so')):
    items = set(items)
    dirs = set(x for x in items if os.path.isdir(x))
    items.difference_update(dirs)
    for x in dirs:
        items.update({y for y in files_in(x) if not exclude(y)})
    return items


def get_executable(info_path):
    with open(info_path, 'rb') as f:
        return plistlib.load(f)['CFBundleExecutable']


def find_sub_apps(contents_dir='.'):
    for app in glob(os.path.join(contents_dir, '*.app')):
        cdir = os.path.join(app, 'Contents')
        for sapp in find_sub_apps(cdir):
            yield sapp
        yield app


def sign_MacOS(contents_dir='.'):
    # Sign everything in MacOS except the main executable
    # which will be signed automatically by codesign when
    # signing the app bundles
    with current_dir(os.path.join(contents_dir, 'MacOS')):
        exe = get_executable('../Info.plist')
        items = {x for x in os.listdir('.') if x != exe and not os.path.islink(x)}
        if items:
            codesign(items)


def do_sign_app(appdir):
    appdir = os.path.abspath(appdir)
    with current_dir(os.path.join(appdir, 'Contents')):
        sign_MacOS()
        # Sign the sub application bundles
        sub_apps = list(find_sub_apps())
        sub_apps.append('Frameworks/QtWebEngineCore.framework/Versions/Current/Helpers/QtWebEngineProcess.app')
        for sa in sub_apps:
            sign_MacOS(os.path.join(sa, 'Contents'))
        codesign(sub_apps)

        # Sign all .so files
        so_files = {x for x in files_in('.') if x.endswith('.so')}
        codesign(so_files)

        # Sign everything in PlugIns
        with current_dir('PlugIns'):
            items = set(os.listdir('.'))
            codesign(expand_dirs(items))

        # Sign everything else in Frameworks
        with current_dir('Frameworks'):
            fw = set(glob('*.framework'))
            codesign(fw)
            items = set(os.listdir('.')) - fw
            codesign(expand_dirs(items))

    # Now sign the main app
    codesign(appdir)
    verify_signature(appdir)
    return 0


def sign_app(appdir, notarize):
    create_entitlements_file(entitlements)
    with make_certificate_useable():
        do_sign_app(appdir)
        if notarize:
            notarize_app(appdir, 'calibre')
