import re
import string
import os.path
from waflib import Utils
from distutils.version import StrictVersion

def __run(cmd):
    try:
        output = Utils.subprocess.check_output(cmd, stderr=Utils.subprocess.STDOUT, universal_newlines=True)
        return output.strip()
    except Exception:
        return ""

def __add_swift_flags(ctx):
    ctx.env.SWIFT_FLAGS = [
        "-frontend", "-c", "-sdk", ctx.env.MACOS_SDK,
        "-enable-objc-interop", "-emit-objc-header", "-parse-as-library",
        "-target", "x86_64-apple-macosx10.10"
    ]

    verRe = re.compile("(?i)version\s?([\d.]+)")
    ctx.env.SWIFT_VERSION = verRe.search(__run([ctx.env.SWIFT, '-version'])).group(1)

    # prevent possible breakages with future swift versions
    if StrictVersion(ctx.env.SWIFT_VERSION) >= StrictVersion("6.0"):
        ctx.env.SWIFT_FLAGS.extend([ "-swift-version", "5" ])

    if ctx.is_debug_build():
        ctx.env.SWIFT_FLAGS.append("-g")

    if ctx.is_optimization():
        ctx.env.SWIFT_FLAGS.append("-O")

def __add_static_swift_library_linking_flags(ctx, swift_library):
    ctx.env.append_value('LINKFLAGS', [
        '-L%s' % swift_library,
        '-Xlinker', '-force_load_swift_libs', '-lc++',
    ])

def __add_dynamic_swift_library_linking_flags(ctx, swift_library):
    ctx.env.append_value('LINKFLAGS', [ '-L%s' % swift_library ])

    #ABI compatibility
    if StrictVersion(ctx.env.SWIFT_VERSION) >= StrictVersion("5.0"):
        ctx.env.append_value('LINKFLAGS', [
            '-Xlinker', '-rpath', '-Xlinker', '/usr/lib/swift',
            '-L/usr/lib/swift',
        ])

    ctx.env.append_value('LINKFLAGS', [
        '-Xlinker', '-rpath', '-Xlinker', swift_library,
    ])

def __find_swift_library(ctx):
    swift_libraries = {}
    #look for set lib paths in passed environment variables
    if 'SWIFT_LIB_DYNAMIC' in ctx.environ:
        swift_libraries['SWIFT_LIB_DYNAMIC'] = ctx.environ['SWIFT_LIB_DYNAMIC']
    if 'SWIFT_LIB_STATIC' in ctx.environ:
        swift_libraries['SWIFT_LIB_STATIC'] = ctx.environ['SWIFT_LIB_STATIC']

    #search for swift libs relative to the swift compiler executable
    swift_library_relative_paths = {
        'SWIFT_LIB_DYNAMIC': '../../lib/swift/macosx',
        'SWIFT_LIB_STATIC': '../../lib/swift_static/macosx'
    }

    for lib_type, path in swift_library_relative_paths.items():
        if lib_type not in swift_libraries:
            lib_path = os.path.join(ctx.env.SWIFT, path)
            swift_library = ctx.root.find_dir(lib_path)
            if swift_library is not None:
                swift_libraries[lib_type] = swift_library.abspath()

    #fall back to xcode-select path
    swift_library_paths = {
        'SWIFT_LIB_DYNAMIC': [
            'Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx',
            'usr/lib/swift/macosx'
        ],
        'SWIFT_LIB_STATIC': [
            'Toolchains/XcodeDefault.xctoolchain/usr/lib/swift_static/macosx',
            'usr/lib/swift_static/macosx'
        ]
    }
    dev_path = __run(['xcode-select', '-p'])[1:]

    for lib_type, paths in swift_library_paths.items():
        for path in paths:
            if lib_type not in swift_libraries:
                swift_library = ctx.root.find_dir([dev_path, path])
                if swift_library is not None:
                    swift_libraries[lib_type] = swift_library.abspath()
                    break
            else:
                break

    #check if library paths were found
    ctx.start_msg('Checking for dynamic Swift Library')
    if 'SWIFT_LIB_DYNAMIC' in swift_libraries:
        ctx.end_msg(swift_libraries['SWIFT_LIB_DYNAMIC'])
    else:
        ctx.end_msg(False)

    ctx.start_msg('Checking for static Swift Library')
    if 'SWIFT_LIB_STATIC' in swift_libraries:
        ctx.end_msg(swift_libraries['SWIFT_LIB_STATIC'])
        ctx.env['SWIFT_LIB_STATIC'] = swift_libraries['SWIFT_LIB_STATIC']
    else:
        ctx.end_msg(False)

    enableStatic = getattr(ctx.options, 'enable_swift-static')
    if (enableStatic) and 'SWIFT_LIB_STATIC' in swift_libraries:
        __add_static_swift_library_linking_flags(ctx, swift_libraries['SWIFT_LIB_STATIC'])
    else:
        __add_dynamic_swift_library_linking_flags(ctx, swift_libraries['SWIFT_LIB_DYNAMIC'])

def __find_macos_sdk(ctx):
    ctx.start_msg('Checking for macOS SDK')
    sdk = None
    sdk_build_version = None
    sdk_version = None

    #look for set macOS SDK paths and version in passed environment variables
    if 'MACOS_SDK' in ctx.environ:
        sdk = ctx.environ['MACOS_SDK']
    if 'MACOS_SDK_VERSION' in ctx.environ:
        ctx.env.MACOS_SDK_VERSION = ctx.environ['MACOS_SDK_VERSION']

    #find macOS SDK paths and version
    if not sdk:
        sdk = __run(['xcrun', '--sdk', 'macosx', '--show-sdk-path'])
    if not ctx.env.MACOS_SDK_VERSION:
        #show-sdk-build-version: is not available on older command line tools, but return a build version (eg 17A360)
        #show-sdk-version: is always available, but on older dev tools it's only the major version
        sdk_build_version = __run(['xcrun', '--sdk', 'macosx', '--show-sdk-build-version' ])
        sdk_version = __run(['xcrun', '--sdk', 'macosx', '--show-sdk-version' ])

    if sdk:
        ctx.env.MACOS_SDK = sdk
        build_version = '10.10.0'

        if not ctx.env.MACOS_SDK_VERSION:
            #convert build version to a version string
            #first 2 two digits are the major version, starting with 15 which is 10.11 (offset of 4)
            #1 char is the minor version, A => 0, B => 1 and ongoing
            #las digits are bugfix version, which are nor relevant for us
            #eg 16E185 => 10.12.4, 17A360 => 10.13, 18B71 => 10.14.1
            if sdk_build_version and isinstance(sdk_build_version, str):
                verRe = re.compile("(\d+)(\D+)(\d+)")
                version_parts = verRe.search(sdk_build_version)
                major = int(version_parts.group(1))-4
                minor = string.ascii_lowercase.index(version_parts.group(2).lower())
                build_version = '10.' + str(major) + '.' + str(minor)

            if not isinstance(sdk_version, str):
                sdk_version = '10.10.0'

            #pick the higher version, always pick sdk over build if newer
            if StrictVersion(build_version) > StrictVersion(sdk_version):
                ctx.env.MACOS_SDK_VERSION = build_version
            else:
                ctx.env.MACOS_SDK_VERSION = sdk_version

        ctx.end_msg(sdk + ' (version found: ' + ctx.env.MACOS_SDK_VERSION + ')')
    else:
        ctx.end_msg(False)

def __find_swift_compiler(ctx):
    ctx.start_msg('Checking for swift (Swift compiler)')
    swift = ''

    #look for set swift paths in passed environment variables
    if 'SWIFT' in ctx.environ:
        swift = ctx.environ['SWIFT']

    #find swift executable
    if not swift:
        swift = __run(['xcrun', '-find', 'swift'])

    if swift:
        ctx.end_msg(swift)
        ctx.env.SWIFT = swift
        __add_swift_flags(ctx)
        __find_swift_library(ctx)
    else:
        ctx.end_msg(False)

def configure(ctx):
    if ctx.env.DEST_OS == "darwin":
        __find_macos_sdk(ctx)
        if ctx.options.enable_swift is not False:
            __find_swift_compiler(ctx)
