# Copyright 2014 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse
import os
import sys

from catkin_tools.argument_parsing import add_cmake_and_make_and_catkin_make_args
from catkin_tools.argument_parsing import add_context_args

from catkin_tools.context import Context

from catkin_tools.terminal_color import ColorMapper
from catkin_tools.terminal_color import sanitize
from catkin_tools.terminal_color import fmt

color_mapper = ColorMapper()
clr = color_mapper.clr


def prepare_arguments(parser):

    parser.description = "This verb is used to configure a catkin workspace's\
    configuration and layout. Calling `catkin config` with no arguments will\
    display the current config and affect no changes if a config already exists\
    for the current workspace and profile."

    # Workspace / profile args
    add_context_args(parser)

    behavior_group = parser.add_argument_group('Behavior', 'Options affecting argument handling.')
    add = behavior_group.add_mutually_exclusive_group().add_argument
    add('--append-args', '-a', action='store_true', default=False,
        help='For list-type arguments, append elements.')
    add('--remove-args', '-r', action='store_true', default=False,
        help='For list-type arguments, remove elements.')

    context_group = parser.add_argument_group('Workspace Context', 'Options affecting the context of the workspace.')
    add = context_group.add_argument
    add('--init', action='store_true', default=False,
        help='Initialize a workspace if it does not yet exist.')
    add = context_group.add_mutually_exclusive_group().add_argument
    add('--extend', '-e', dest='extend_path', type=str,
        help='Explicitly extend the result-space of another catkin workspace, '
        'overriding the value of $CMAKE_PREFIX_PATH.')
    add('--no-extend', dest='extend_path', action='store_const', const='',
        help='Un-set the explicit extension of another workspace as set by --extend.')
    add = context_group.add_argument
    add('--mkdirs', action='store_true', default=False,
        help='Create directories required by the configuration (e.g. source space) if they do not already exist.')

    create_group = parser.add_argument_group(
        'Package Create Defaults', 'Information of default authors/maintainers of created packages')
    add = create_group.add_mutually_exclusive_group().add_argument
    add('--authors', metavar=('NAME', 'EMAIL'), dest='authors', nargs='+', required=False, type=str, default=None,
        help='Set the default authors of created packages')
    add('--maintainers', metavar=('NAME', 'EMAIL'), dest='maintainers', nargs='+',
        required=False, type=str, default=None,
        help='Set the default maintainers of created packages')
    add('--licenses', metavar='LICENSE', dest='licenses', nargs='+', required=False, type=str, default=None,
        help='Set the default licenses of created packages')

    lists_group = parser.add_argument_group(
        'Package Build Defaults', 'Packages to include or exclude from default build behavior.')
    add = lists_group.add_mutually_exclusive_group().add_argument
    add('--buildlist', metavar="PKG", dest='buildlist', nargs="+", required=False, type=str,
        default=None, help='Set the packages on the buildlist. If the buildlist is non-empty, '
        'only the packages on the buildlist are built with a bare call to `catkin build`.')
    add('--no-buildlist', dest='buildlist', action='store_const', const=[], default=None,
        help='Clear all packages from the buildlist.')
    add('--whitelist', metavar="PKG", dest='buildlist', nargs="+", required=False, type=str,
        default=None, help=argparse.SUPPRESS)
    add('--no-whitelist', dest='buildlist', action='store_const', const=[], default=None,
        help=argparse.SUPPRESS)
    add = lists_group.add_mutually_exclusive_group().add_argument
    add('--skiplist', metavar="PKG", dest='skiplist', nargs="+", required=False, type=str, default=None,
        help='Set the packages on the skiplist. Packages on the skiplist are '
        'not built with a bare call to `catkin build`.')
    add('--no-skiplist', dest='skiplist', action='store_const', const=[], default=None,
        help='Clear all packages from the skiplist.')
    add('--blacklist', metavar="PKG", dest='skiplist', nargs="+", required=False, type=str, default=None,
        help=argparse.SUPPRESS)
    add('--no-blacklist', dest='skiplist', action='store_const', const=[], default=None,
        help=argparse.SUPPRESS)

    spaces_group = parser.add_argument_group('Spaces', 'Location of parts of the catkin workspace.')
    Context.setup_space_keys()
    for space, space_dict in Context.SPACES.items():
        add = spaces_group.add_mutually_exclusive_group().add_argument
        flags = ['--{}-space'.format(space)]
        flags.extend([space_dict['short_flag']] if 'short_flag' in space_dict else [])
        add(*flags, default=None,
            help='The path to the {} space.'.format(space))
        add('--default-{}-space'.format(space),
            action='store_const', dest='{}_space'.format(space), default=None, const=space_dict['default'],
            help='Use the default path to the {} space ("{}")'.format(space, space_dict['default']))
    add = spaces_group.add_argument
    add('-x', '--space-suffix',
        help='Suffix for build, devel, and install space if they are not otherwise explicitly set.')

    devel_group = parser.add_argument_group(
        'Devel Space', 'Options for configuring the structure of the devel space.')
    add = devel_group.add_mutually_exclusive_group().add_argument
    add('--link-devel', dest='devel_layout', action='store_const', const='linked', default=None,
        help='Build products from each catkin package into isolated spaces,'
        ' then symbolically link them into a merged devel space.')
    add('--merge-devel', dest='devel_layout', action='store_const', const='merged', default=None,
        help='Build products from each catkin package into a single merged devel spaces.')
    add('--isolate-devel', dest='devel_layout', action='store_const', const='isolated', default=None,
        help='Build products from each catkin package into isolated devel spaces.')

    install_group = parser.add_argument_group(
        'Install Space', 'Options for configuring the structure of the install space.')
    add = install_group.add_mutually_exclusive_group().add_argument
    add('--install', action='store_true', default=None,
        help='Causes each package to be installed to the install space.')
    add('--no-install', dest='install', action='store_false', default=None,
        help='Disables installing each package into the install space.')

    add = install_group.add_mutually_exclusive_group().add_argument
    add('--isolate-install', action='store_true', default=None,
        help='Install each catkin package into a separate install space.')
    add('--merge-install', dest='isolate_install', action='store_false', default=None,
        help='Install each catkin package into a single merged install space.')

    build_group = parser.add_argument_group('Build Options', 'Options for configuring the way packages are built.')
    add_cmake_and_make_and_catkin_make_args(build_group)

    return parser


def main(opts):
    try:
        sysargs = sys.argv[1:]

        # Deprecated options
        deprecated_args = [
                ('--blacklist',    '--skiplist',),
                ('--no-blacklist', '--no-skiplist'),
                ('--whitelist',    '--buildlist'),
                ('--no-whitelist', '--no-buildlist')]

        used_deprecated_args = [(old, new) for old, new in deprecated_args if old in sysargs]

        if any(used_deprecated_args):
            print(fmt('@!@{rf}WARNING:@| Some arguments are deprecated and will be'
                      ' removed in a future release.\n'))
            print('Please switch to using their replacements as follows:')
            for old_arg, new_arg in used_deprecated_args:
                print(" - '{}' is deprecated, use '{}' instead".format(old_arg, new_arg))
            print()

        # Determine if the user is trying to perform some action, in which
        # case, the workspace should be automatically initialized
        ignored_opts = ['main', 'verb']
        actions = [v for k, v in vars(opts).items() if k not in ignored_opts]
        no_action = not any(actions)

        # Handle old argument names necessary for Context.load
        if opts.buildlist:
            opts.whitelist = opts.buildlist
            del opts.buildlist
        if opts.skiplist:
            opts.blacklist = opts.skiplist
            del opts.skiplist

        # Try to find a metadata directory to get context defaults
        # Otherwise use the specified directory
        context = Context.load(
            opts.workspace,
            opts.profile,
            opts,
            append=opts.append_args,
            remove=opts.remove_args)

        do_init = opts.init or not no_action
        summary_notes = []

        if not context.initialized() and do_init:
            summary_notes.append(clr('@!@{cf}Initialized new catkin workspace in `%s`@|' % sanitize(context.workspace)))

        if context.initialized() or do_init:
            Context.save(context)

        if opts.mkdirs and not context.source_space_exists():
            os.makedirs(context.source_space_abs)

        print(context.summary(notes=summary_notes))

    except IOError as exc:
        # Usually happens if workspace is already underneath another catkin_tools workspace
        print('error: could not configure catkin workspace: %s' % exc.message)
        return 1

    return 0
