#!/usr/bin/env python
# coding=utf-8
"""
A complex example demonstrating a variety of methods to load CommandSets using a mix of command decorators
with examples of how to integrate tab completion with argparse-based commands.
"""
import argparse
from typing import (
    Dict,
    Iterable,
    List,
    Optional,
)

from cmd2 import (
    Cmd,
    Cmd2ArgumentParser,
    CommandSet,
    CompletionItem,
    with_argparser,
)
from cmd2.utils import (
    CompletionError,
    basic_complete,
)
from modular_commands.commandset_basic import (  # noqa: F401
    BasicCompletionCommandSet,
)
from modular_commands.commandset_complex import (  # noqa: F401
    CommandSetA,
)
from modular_commands.commandset_custominit import (
    CustomInitCommandSet,  # noqa: F401
)

# Data source for argparse.choices
food_item_strs = ['Pizza', 'Ham', 'Ham Sandwich', 'Potato']


def choices_function() -> List[str]:
    """Choices functions are useful when the choice list is dynamically generated (e.g. from data in a database)"""
    return ['a', 'dynamic', 'list', 'goes', 'here']


def completer_function(text: str, line: str, begidx: int, endidx: int) -> List[str]:
    """
    A tab completion function not dependent on instance data. Since custom tab completion operations commonly
    need to modify cmd2's instance variables related to tab completion, it will be rare to need a completer
    function. completer_method should be used in those cases.
    """
    match_against = ['a', 'dynamic', 'list', 'goes', 'here']
    return basic_complete(text, line, begidx, endidx, match_against)


def choices_completion_item() -> List[CompletionItem]:
    """Return CompletionItem instead of strings. These give more context to what's being tab completed."""
    items = \
        {
            1: "My item",
            2: "Another item",
            3: "Yet another item"
        }
    return [CompletionItem(item_id, description) for item_id, description in items.items()]


def choices_arg_tokens(arg_tokens: Dict[str, List[str]]) -> List[str]:
    """
    If a choices or completer function/method takes a value called arg_tokens, then it will be
    passed a dictionary that maps the command line tokens up through the one being completed
    to their argparse argument name.  All values of the arg_tokens dictionary are lists, even if
    a particular argument expects only 1 token.
    """
    # Check if choices_function flag has appeared
    values = ['choices_function', 'flag']
    if 'choices_function' in arg_tokens:
        values.append('is {}'.format(arg_tokens['choices_function'][0]))
    else:
        values.append('not supplied')
    return values


class WithCommandSets(Cmd):
    def __init__(self, command_sets: Optional[Iterable[CommandSet]] = None):
        super().__init__(command_sets=command_sets)
        self.sport_item_strs = ['Bat', 'Basket', 'Basketball', 'Football', 'Space Ball']

    def choices_method(self) -> List[str]:
        """Choices methods are useful when the choice list is based on instance data of your application"""
        return self.sport_item_strs

    def choices_completion_error(self) -> List[str]:
        """
        CompletionErrors can be raised if an error occurs while tab completing.

        Example use cases
            - Reading a database to retrieve a tab completion data set failed
            - A previous command line argument that determines the data set being completed is invalid
        """
        if self.debug:
            return self.sport_item_strs
        raise CompletionError("debug must be true")

    # Parser for example command
    example_parser = Cmd2ArgumentParser(description="Command demonstrating tab completion with argparse\n"
                                                    "Notice even the flags of this command tab complete")

    # Tab complete from a list using argparse choices. Set metavar if you don't
    # want the entire choices list showing in the usage text for this command.
    example_parser.add_argument('--choices', choices=food_item_strs, metavar="CHOICE",
                                help="tab complete using choices")

    # Tab complete from choices provided by a choices function and choices method
    example_parser.add_argument('--choices_function', choices_function=choices_function,
                                help="tab complete using a choices_function")
    example_parser.add_argument('--choices_method', choices_method=choices_method,
                                help="tab complete using a choices_method")

    # Tab complete using a completer function and completer method
    example_parser.add_argument('--completer_function', completer_function=completer_function,
                                help="tab complete using a completer_function")
    example_parser.add_argument('--completer_method', completer_method=Cmd.path_complete,
                                help="tab complete using a completer_method")

    # Demonstrate raising a CompletionError while tab completing
    example_parser.add_argument('--completion_error', choices_method=choices_completion_error,
                                help="raise a CompletionError while tab completing if debug is False")

    # Demonstrate returning CompletionItems instead of strings
    example_parser.add_argument('--completion_item', choices_function=choices_completion_item, metavar="ITEM_ID",
                                descriptive_header="Description",
                                help="demonstrate use of CompletionItems")

    # Demonstrate use of arg_tokens dictionary
    example_parser.add_argument('--arg_tokens', choices_function=choices_arg_tokens,
                                help="demonstrate use of arg_tokens dictionary")

    @with_argparser(example_parser)
    def do_example(self, _: argparse.Namespace) -> None:
        """The example command"""
        self.poutput("I do nothing")


if __name__ == '__main__':
    import sys

    print("Starting")
    my_sets = [CustomInitCommandSet('First argument', 'Second argument')]
    app = WithCommandSets(command_sets=my_sets)
    sys.exit(app.cmdloop())
