#!python
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
# vi: set ft=python sts=4 ts=4 sw=4 et:
import click

from .instance import list_interfaces
from .utils import (
    CONTEXT_SETTINGS,
    UNKNOWN_OPTIONS,
    ExistingDirPath,
    ExistingFilePath,
    UnexistingFilePath,
    RegularExpression,
    PythonModule,
    check_not_none,
)

from .. import __version__


# declare the CLI group
@click.group(context_settings=CONTEXT_SETTINGS)
def cli():
    pass


@cli.command(context_settings=CONTEXT_SETTINGS)
@click.argument("logdir", type=ExistingDirPath, callback=check_not_none)
@click.option(
    "-r",
    "--regex",
    type=RegularExpression(),
    callback=check_not_none,
    help="Regular expression to be searched in each traceback.",
)
def search(logdir, regex):
    """Search for tracebacks content.

    Search for traceback inside a folder of nipype crash log files that match
    a given regular expression.

    Examples:\n
    nipypecli search nipype/wd/log -r '.*subject123.*'
    """
    from .crash_files import iter_tracebacks

    for file, trace in iter_tracebacks(logdir):
        if regex.search(trace):
            click.echo("-" * len(file))
            click.echo(file)
            click.echo("-" * len(file))
            click.echo(trace)


@cli.command(context_settings=CONTEXT_SETTINGS)
@click.argument("crashfile", type=ExistingFilePath, callback=check_not_none)
@click.option(
    "-r", "--rerun", is_flag=True, flag_value=True, help="Rerun crashed node."
)
@click.option(
    "-d",
    "--debug",
    is_flag=True,
    flag_value=True,
    help="Enable Python debugger when re-executing.",
)
@click.option(
    "-i",
    "--ipydebug",
    is_flag=True,
    flag_value=True,
    help="Enable IPython debugger when re-executing.",
)
@click.option(
    "-w", "--dir", type=ExistingDirPath, help="Directory where to run the node in."
)
def crash(crashfile, rerun, debug, ipydebug, dir):
    """Display Nipype crash files.

    For certain crash files, one can rerun a failed node in a temp directory.

    Examples:\n
    nipypecli crash crashfile.pklz\n
    nipypecli crash crashfile.pklz -r -i\n
    """
    from .crash_files import display_crash_file

    debug = "ipython" if ipydebug else debug
    if debug == "ipython":
        import sys
        from IPython.core import ultratb

        sys.excepthook = ultratb.FormattedTB(
            mode="Verbose", color_scheme="Linux", call_pdb=1
        )
    display_crash_file(crashfile, rerun, debug, dir)


@cli.command(context_settings=CONTEXT_SETTINGS)
@click.argument("pklz_file", type=ExistingFilePath, callback=check_not_none)
def show(pklz_file):
    """Print the content of Nipype node .pklz file.

    Examples:\n
    nipypecli show node.pklz
    """
    from pprint import pprint
    from ..utils.filemanip import loadpkl

    pkl_data = loadpkl(pklz_file)
    pprint(pkl_data)


@cli.command(context_settings=UNKNOWN_OPTIONS)
@click.argument("module", type=PythonModule(), required=False, callback=check_not_none)
@click.argument("interface", type=str, required=False)
@click.option(
    "--list",
    is_flag=True,
    flag_value=True,
    help="List the available Interfaces inside the given module.",
)
@click.option(
    "-h", "--help", is_flag=True, flag_value=True, help="Show help message and exit."
)
@click.pass_context
def run(ctx, module, interface, list, help):
    """Run a Nipype Interface.

    Examples:\n
    nipypecli run nipype.interfaces.nipy --list\n
    nipypecli run nipype.interfaces.nipy ComputeMask --help
    """
    import argparse
    from .utils import add_args_options
    from ..utils.nipype_cmd import run_instance

    # print run command help if no arguments are given
    module_given = bool(module)
    if not module_given:
        click.echo(ctx.command.get_help(ctx))

    # print the list of available interfaces for the given module
    elif (module_given and list) or (module_given and not interface):
        iface_names = list_interfaces(module)
        click.echo("Available Interfaces:")
        for if_name in iface_names:
            click.echo("    {}".format(if_name))

    # check the interface
    elif module_given and interface:
        # create the argument parser
        description = "Run {}".format(interface)
        prog = " ".join([ctx.command_path, module.__name__, interface] + ctx.args)
        iface_parser = argparse.ArgumentParser(description=description, prog=prog)

        # instantiate the interface
        node = getattr(module, interface)()
        iface_parser = add_args_options(iface_parser, node)

        if not ctx.args:
            # print the interface help
            try:
                iface_parser.print_help()
            except:
                print(
                    "An error ocurred when trying to print the full"
                    "command help, printing usage."
                )
            finally:
                iface_parser.print_usage()
        else:
            # run the interface
            args = iface_parser.parse_args(args=ctx.args)
            run_instance(node, args)


@cli.command(context_settings=CONTEXT_SETTINGS)
def version():
    """Print current version of Nipype."""
    click.echo(__version__)


@cli.group()
def convert():
    """Export nipype interfaces to other formats."""
    pass


@convert.command(context_settings=CONTEXT_SETTINGS)
@click.option(
    "-i",
    "--interface",
    type=str,
    required=True,
    help="Name of the Nipype interface to export.",
)
@click.option(
    "-m",
    "--module",
    type=PythonModule(),
    required=True,
    callback=check_not_none,
    help="Module where the interface is defined.",
)
@click.option(
    "-o",
    "--output",
    type=UnexistingFilePath,
    required=True,
    callback=check_not_none,
    help="JSON file name where the Boutiques descriptor will be " "written.",
)
@click.option(
    "-c",
    "--container-image",
    required=True,
    type=str,
    help="Name of the container image where the tool is installed.",
)
@click.option(
    "-p",
    "--container-type",
    required=True,
    type=str,
    help="Type of container image (Docker or Singularity).",
)
@click.option(
    "-x",
    "--container-index",
    type=str,
    help="Optional index where the image is available (e.g. "
    "http://index.docker.io).",
)
@click.option(
    "-g",
    "--ignore-inputs",
    type=str,
    multiple=True,
    help="List of interface inputs to not include in the descriptor.",
)
@click.option(
    "-v", "--verbose", is_flag=True, flag_value=True, help="Print information messages."
)
@click.option(
    "-a", "--author", type=str, help="Author of the tool (required for publishing)."
)
@click.option(
    "-t",
    "--tags",
    type=str,
    help="JSON string containing tags to include in the descriptor,"
    'e.g. "{"key1": "value1"}"',
)
def boutiques(
    module,
    interface,
    container_image,
    container_type,
    output,
    container_index,
    verbose,
    author,
    ignore_inputs,
    tags,
):
    """Nipype to Boutiques exporter.

    See Boutiques specification at https://github.com/boutiques/schema.
    """
    from nipype.utils.nipype2boutiques import generate_boutiques_descriptor

    # Generates JSON string and saves it to file
    generate_boutiques_descriptor(
        module,
        interface,
        container_image,
        container_type,
        container_index,
        verbose,
        True,
        output,
        author,
        ignore_inputs,
        tags,
    )
