# AUTOGENERATED! DO NOT EDIT! File to edit: core.ipynb (unless otherwise specified).

__all__ = ['DEFAULT_HELP_ARGUMENTS', 'import_modules', 'list_subclasses', 'all_subclasses', 'parse_known_help',
           'parse_known_args_with_help', 'assign_arg_prefix', 'input_feedback']


# Cell
import re
from importlib import import_module, resources


# Cell
DEFAULT_HELP_ARGUMENTS = ['-h', '--help']


# Cell
def import_modules(package):
    """
    Dynamically import all modules but not submodules in a package.
    `package`: the package to import all modules
    """
    files = resources.contents(package)
    modules = [f[:-3] for f in files if f.endswith(".py") and f[0] != "_"]
    for module in modules:
        import_module(f"{package.__name__}.{module}")


# Cell
def list_subclasses(package, base_class):
    """
    Dynamically import all modules in a package and scan for all subclasses of a base class.
    `package`: the package to import
    `base_class`: the base class to scan for subclasses
    return: a dictionary of possible subclasses with class name as key and class type information as value
    """
    import_modules(package)
    subclasses = all_subclasses(base_class)
    return dict(zip(map(lambda c: c.__name__, subclasses), subclasses))


# Cell
def all_subclasses(base_class):
    """
    Scan recursively for all subclasses of a base class.
    `base_class`: the base class to scan for subclasses
    return: a set of subclasses type information
    """
    return set(base_class.__subclasses__()).union([s for c in base_class.__subclasses__() for s in all_subclasses(c)])


# Cell
def parse_known_help(argv):
    """
    Parse the command line arguments for help parameter and decide,
    if the help parameter should be assigned to current argument parser.
    `argv`: the command line arguments to parse
    `root_parser`: True if the arguments are assigned to the root parser, else False
    return: a tuple containing [known, rest], where known are the arguments assigned to current parser
    """
    non_help_argv = list(filter(lambda a: a not in DEFAULT_HELP_ARGUMENTS, argv))
    help_argv = list(filter(lambda a: a in DEFAULT_HELP_ARGUMENTS, argv))
    only_help = len(non_help_argv) == 1

    known = non_help_argv + help_argv if only_help else non_help_argv
    rest = [] if only_help else help_argv
    return known, rest


# Cell
def parse_known_args_with_help(parser, argv):
    """
    Parse the command line arguments and validate, if help parameter should be assigned to current argument parser.
    If the help parameter is not assigned to current parser, then keep it in the remaining argv.
    `parser`: the argument parser to handle
    return: a tuple containing [args, argv], where args are the consumed arguments and argv are the remaining arguments
    """
    argv, argv_help_rest = parse_known_help(argv)
    args, rest_args = parser.parse_known_args(argv)
    argv = rest_args + argv_help_rest
    return args, argv


# Cell
def assign_arg_prefix(arg_name, prefix=None):
    """
    Assign a parameter prefix to a given argument name. (e.g --prefix_<arg_name>)
    `arg_name`: the argument name to prefix
    `prefix`: the prefix
    return: the prefixed argument name
    """
    return arg_name if prefix is None else re.sub(r'^(-{0,2})([\w-]+)$', r'\1{}_\2'.format(prefix), arg_name)


# Cell
def input_feedback(msg, choice, choices):
    """
    User input request wrapper.
    `msg`: the message to display
    `choice`: if previous choice exist
    `choices`: the possible choices
    :return: the choice input
    """

    # if decision is already made for all contents, skip feedback
    if not (choice and choice.isupper()):
        prompt = '{} \n choices: {} '.format(msg, ', '.join(['{} ({})'.format(k, v) for k, v in choices.items()]))
        while True:
            choice = input(prompt)
            if choice in choices:
                break
    return choice
