import SCons.Util, os
import subprocess
import re
import shutil

def which(program_name):
    if hasattr(shutil, "which"):
        return shutil.which(program_name)
    else:
        path = os.getenv('PATH')
        for p in path.split(os.path.pathsep):
            p = os.path.join(p,program_name)
            if os.path.exists(p) and os.access(p,os.X_OK):
                return p

def generate(env) :
    def captureWinDeployQtMapping(release = True):
        p = False

        qt_bin_folder = ""
        if "QTDIR" in env:
            qt_bin_folder = env["QTDIR"] + "\\bin;"

        environ = {"PATH": qt_bin_folder + os.getenv("PATH"), "TEMP": os.getenv("TEMP"), "TMP": os.getenv("TMP")}

        if release:
            p = subprocess.Popen(['windeployqt', '--release', '--dry-run', '--list', 'mapping', 'Swift.exe'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=environ)
        else:
            p = subprocess.Popen(['windeployqt', '--debug', '--dry-run', '--list', 'mapping', 'Swift.exe'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=environ)

        if p:
            stdout, stderr = p.communicate()

            mappings = []

            p = re.compile(ur'"([^\"]*)" "([^\"]*)"')

            matches = re.findall(p, stdout)
            for match in matches:
                mappings.append(match)
            return mappings
        else:
            return False

    def createWindowsBundleManual(env, bundle, resources = {}, qtplugins = {}, qtlibs = [], qtversion = '4') :
        all_files = []
        all_files += env.Install(bundle, bundle + ".exe")
        for lib in qtlibs :
            all_files += env.Install(bundle, os.path.join(env["QTDIR"], "bin", lib + ".dll"))
        plugins_suffix = '4'
        if qtversion == '5' :
            plugins_suffix = ''
        for plugin_type in qtplugins:
            all_files += env.Install(os.path.join(bundle, plugin_type), [os.path.join(env["QTDIR"], "plugins", plugin_type, "q" + plugin + plugins_suffix + ".dll") for plugin in qtplugins[plugin_type]])
        for dir, resourceFiles in resources.items() :
            for resource in resourceFiles :
                e = env.Entry(resource)
                if e.isdir() :
                    for subresource in env.Glob(str(e) + "/*") :
                        all_files += env.Install(os.path.join(bundle, dir, e.name), subresource)
                else :
                    all_files += env.Install(os.path.join(bundle, dir), resource)
        return all_files

    # This version of uses windeployqt tool
    def createWindowsBundleWithWinDeployQt(env, bundle, resources = {}, qtplugins = {},  qtlibs = [],  qtversion = '4') :
        assert(qtversion == '5')
        all_files = []

        # add swift executable
        all_files += env.Install(bundle, bundle + ".exe")

        # adding resources (swift sounds/images/translations)
        for dir, resourceFiles in resources.items() :
            for resource in resourceFiles :
                e = env.Entry(resource)
                if e.isdir() :
                    for subresource in env.Glob(str(e) + "/*") :
                        all_files += env.Install(os.path.join(bundle, dir, e.name), subresource)
                else :
                    all_files += env.Install(os.path.join(bundle, dir), resource)

        qtmappings = captureWinDeployQtMapping()
        assert(qtmappings)

        # handle core DLLs
        qt_corelib_regex = re.compile(ur".*bin.*\\(.*)\.dll")

        for qtlib in qtlibs:
            if qtlib.startswith("Qt5"):
                (src_path, target_path) = next(((src_path, target_path) for (src_path, target_path) in qtmappings if  qt_corelib_regex.match(src_path) and qt_corelib_regex.match(src_path).group(1) == qtlib), (None, None))
                if src_path != None:
                    all_files += env.Install(bundle, src_path)

        # handle core dependencies
        for (src_path, target_path) in qtmappings:
            if qt_corelib_regex.match(src_path) and not qt_corelib_regex.match(src_path).group(1).startswith("Qt5"):
                all_files += env.Install(bundle, src_path)

        # handle plugins
        qt_plugin_regex = re.compile(ur".*plugins.*\\(.*)\\(.*)\.dll")
        for (src_path, target_path) in qtmappings:
            if qt_plugin_regex.match(src_path):
                plugin_folder, filename = qt_plugin_regex.match(src_path).groups()
                try:
                    if plugin_folder in ["audio"] or filename[1:] in qtplugins[plugin_folder]:
                        all_files += env.Install(os.path.join(bundle, plugin_folder), src_path)
                except:
                    pass
        return all_files

    def createWindowsBundle(env, bundle, resources = {}, qtplugins = {}, qtlibs = [], qtversion = '4'):
        if which("windeployqt.exe"):
            return createWindowsBundleWithWinDeployQt(env, bundle, resources, qtplugins, qtlibs, qtversion)
        else:
            return createWindowsBundleManual(env, bundle, resources, qtplugins, qtlibs, qtversion)

    env.AddMethod(createWindowsBundle, "WindowsBundle")

def exists(env) :
    return env["PLATFORM"] == "win32"