"""
Stolos can be invoked in one of two ways:
    - as an application runner, stolos starts from the command-line
    - as an api, stolos gets imported and run like code.

In both scenarios, Stolos may need users to define specific configuration
options.  This module provides a uniform way for the api and runner to
access configuration options.

Users of Stolos's api should refer directly to stolos.api rather than accessing
this module directly

Developer note:  The only code that should import from this file is
`stolos.api` and `stolos.runner`.
"""
import argparse
import importlib
import stolos
from stolos import argparse_shared as at
from stolos import log


def _get_parent_parsers(objects):
    """Internal function to call m.build_arg_parser() for each m in objects"""
    for m in set(objects):
        if isinstance(m, argparse.ArgumentParser):
            p = m
        else:
            p = m.build_arg_parser()
        if not isinstance(p, argparse.ArgumentParser):
            msg = (
                "Failed to initialize Stolos because the initializer"
                " epected an instance of argparse.ArgumentParser but received"
                " something else")
            log.error(msg, extra=dict(parent_argparser=p))
            raise TypeError("%s.  Received: %s" % (msg, type(p)))
        yield p


def initialize_configuration_backend(cbackend, parser, add_help):
    """
    get options for the chosen configuration_backend.
    ensure they don't conflict with previously defined ones
    """
    cb = importlib.import_module(cbackend.__module__).build_arg_parser()
    newparser = at.build_arg_parser(parents=[parser, cb], add_help=add_help)
    return newparser


def initialize(objects, args=None, parse_known_args=False,
               **argument_parser_kwargs):
    """
    Initialize Stolos such that we ensure all required configuration settings
    are unified in one central place before we do anything with Stolos.
    Raises error if any parsers define conflicting argument options.

    This function is called by user-facing or application-level code.
    All internal stolos libraries should call to stolos.get_NS() when they need
    to access configuration.  Internal libraries should not call this function.

    Returns (argparse.ArgumentParser(...), argparse.Namespace(...))

    `objects` - is a list of build_arg_parser functions or objects
        (ie Stolos modules) containing a callable build_arg_parser attribute.
    `args` - (optional).  Define command-line arguments to use.
        Default to sys.argv (which is what argparse does).
        Explicitly pass args=[] to not read command-line arguments, and instead
        expect that all arguments are passed in as environment variables.
        To guarantee NO arguments are read from sys.argv, set args=[]
        Example:  args=['--option1', 'val', ...]
    `parse_known_args` - if True, parse only known commandline arguments and
        do not add_help (ie don't recognize '-h').  Assume you will
        post-process the argument parser and add a --help option later.
        If False, add_help (ie recognize '-h') and fail if anything on
        command-line is not recognized by the argument parser
    `argument_parser_kwargs` - (optional) passed to the ArgumentParser(...)
    """
    # partially initialize a parser to get selected configuration backend
    parser = at.build_arg_parser(
        description="Initialize Stolos, whether running it or calling its api",
        parents=list(_get_parent_parsers(objects)),
        **argument_parser_kwargs)
    if args is not None:
        ns, _ = parser.parse_known_args(args)
    else:
        ns, _ = parser.parse_known_args()
    # get a new parser updated with options for chosen configuration backend
    parser = initialize_configuration_backend(
        ns.configuration_backend, parser, add_help=not bool(parse_known_args))

    if not parse_known_args:
        ns = parser.parse_args(args)
    else:
        ns, _ = parser.parse_known_args()
    stolos.NS = ns
    try:
        del stolos.Uninitialized
    except AttributeError:
        log.warn(
            "Stolos was re-initialized.  You may have imported the api and"
            " then done something weird like re-import it or manually"
            " call Stolos's initializer."
        )
    return parser, ns

@@ -1339,38 +1339,24 @@
 tialize_
-configuration_
 backend(
 cbackend
@@ -1347,17 +1347,16 @@
 backend(
-c
 backend,
@@ -1418,30 +1418,15 @@
 sen 
-configuration_
 backend
-.
 %0A   
@@ -1494,18 +1494,23 @@
 %22%22%22%0A    
-c
 b
+ackend
  = impor
@@ -1528,17 +1528,16 @@
 _module(
-c
 backend.
@@ -1603,16 +1603,25 @@
 _parser(
+%0A        
 parents=
@@ -1633,10 +1633,15 @@
 er, 
-c
 b
+ackend
 %5D, a
@@ -3678,24 +3678,25 @@
 nown_args()%0A
+%0A
     # get a 
@@ -3735,37 +3735,97 @@
 for 
+each 
 chosen 
-configuration backend%0A
+backend%0A    for backend in %5Bns.configuration_backend, ns.queue_backend%5D:%0A    
     
@@ -3844,30 +3844,16 @@
 tialize_
-configuration_
 backend(
@@ -3861,33 +3861,20 @@
         
-ns.configuration_
+    
 backend,

generalize initializer to load argparse options for chosen queue backend (in same way as was done for configuration backend)