# This source file is part of the Swift.org open source project
#
# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
# Licensed under Apache License v2.0 with Runtime Library Exception
#
# See https://swift.org/LICENSE.txt for license information
# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors


"""
Temporary module with functionaly used to migrate away from build-script-impl.
"""


from __future__ import absolute_import, unicode_literals

import itertools
import subprocess

import six
from six.moves import map

from swift_build_support.swift_build_support.targets import \
    StdlibDeploymentTarget


__all__ = [
    'UnknownSDKError',

    'check_impl_args',
    'migrate_swift_sdks',
    'parse_args',
]


_SDK_TARGETS = {
    'OSX': StdlibDeploymentTarget.OSX.targets,
    'IOS': StdlibDeploymentTarget.iOS.targets,
    'IOS_SIMULATOR': StdlibDeploymentTarget.iOSSimulator.targets,
    'TVOS': StdlibDeploymentTarget.AppleTV.targets,
    'TVOS_SIMULATOR': StdlibDeploymentTarget.AppleTVSimulator.targets,
    'WATCHOS': StdlibDeploymentTarget.AppleWatch.targets,
    'WATCHOS_SIMULATOR': StdlibDeploymentTarget.AppleWatchSimulator.targets,
}


# -----------------------------------------------------------------------------

class UnknownSDKError(Exception):
    """Error indicating an unknown SDK was encountered when migrating to target
    triples.
    """

    def __init__(self, sdk):
        self.sdk = sdk

        super(UnknownSDKError, self).__init__(
            'Unknown SDK: {}'.format(self.sdk))


def migrate_swift_sdks(args):
    """Migrate usages of the now deprecated `--swift-sdks` option to the new
    `--stdlib-deployment-targets` option, converting Swift SDKs to the
    corresponding targets.

    This function is a stop-gap to replacing all instances of `--swift-sdks`.
    """

    def _flatten(iterable):
        return itertools.chain.from_iterable(iterable)

    def _swift_sdk_to_stdlib_targets(sdk):
        targets = _SDK_TARGETS.get(sdk, None)
        if targets is None:
            raise UnknownSDKError(sdk)

        return targets

    def _migrate_swift_sdks_arg(arg):
        if not arg.startswith('--swift-sdks'):
            return arg

        sdks = arg.split('=')[1]
        sdk_list = [] if sdks == '' else sdks.split(';')

        targets = _flatten(map(_swift_sdk_to_stdlib_targets, sdk_list))
        target_names = [target.name for target in targets]

        return '--stdlib-deployment-targets={}'.format(' '.join(target_names))

    return list(map(_migrate_swift_sdks_arg, args))


# -----------------------------------------------------------------------------

def _process_disambiguation_arguments(args, unknown_args):
    """These arguments are only listed in the driver arguments to stop argparse
    from auto expanding arguments like --install-swift to the known argument
    --install-swiftevolve. Remove them from args and add them to unknown_args
    again.
    """

    if hasattr(args, 'impl_skip_test_swift'):
        if args.impl_skip_test_swift:
            unknown_args.append('--skip-test-swift')
        del args.impl_skip_test_swift

    if hasattr(args, 'impl_install_swift'):
        if args.impl_install_swift:
            unknown_args.append('--install-swift')
        del args.impl_install_swift

    return args, unknown_args


def parse_args(parser, args, namespace=None):
    """Parses a given argument list with the given argparse.ArgumentParser.

    Return a processed arguments object. Any unknown arguments are stored in
    `build_script_impl_args` attribute as a list. Ignores '--' to be compatible
    with old style argument list.
    """

    args = [arg for arg in args if arg != '--']
    args, unknown_args = parser.parse_known_args(args, namespace)
    args, unknown_args = _process_disambiguation_arguments(args, unknown_args)

    args.build_script_impl_args = unknown_args

    return args


# -----------------------------------------------------------------------------

def check_impl_args(build_script_impl, args):
    """Check whether given argv are all known arguments for
    `build-script-impl`.

    Raises a ValueError if any invalid argument is found. Return nothing
    otherwise.
    """

    pipe = subprocess.Popen(
        [build_script_impl, '--check-args-only=1'] + args,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE)

    _, err = pipe.communicate()

    if pipe.returncode != 0:
        raise ValueError(six.text_type(err.splitlines()[0].decode()))
