"""`pyramid_settings` is simplest package (2 functions to be fair) that enables
you to load python modules as pyramid settings files.

"""

import sys
import logging
from importlib import import_module


# sorry for lame name
def load_settings(parent_module_name, settings, config=None, file_name=None):
    """Load module contents into ``request.registry.settings``.

    You need to supply `pysettings` executing argument like this:

        $ pserve development.ini pysettings=settings.py

    This function can be used multiple times to load manually some settings
    files you specify by `file_name` parameter.

    Returns nothing, fills existing settings in place.

    :param parent_module_name: string with name of your module (__name__
        variable will be enough).
    :param settings: ``request.registry.settings`` object that will be filled
        with data loaded from additional settings .py files.
    :param *config: OPTIONAL basic `global_config` object generated by paster
        while starting application. This object is storing execution arguments
        of pserv and we try to grab 'settings' argument that will store
        additional settings module name.
    :param *file_name: OPTIONAL `string` in which you can pass manually module
        name of your additional settings.

    """
    log = logging.getLogger(parent_module_name)

    settings_list = []

    if config:
        try:
            settings_string = config['pysettings']
        except KeyError:
            # This goes out in silence just to not cause issues to user.
            # TODO: Argue with Ignacy about silent catches.
            log.debug('Failed to load settings from executing arguments.')

        else:
            settings_list = settings_string.split(',')

    if file_name:
        settings_list.append(file_name)

    log.debug('Logging additional settings modules...')

    for setting_file_name in settings_list:
        # We clean files from .py extensions just in case.
        # Also - if you wonder why dot at the beggining - we are importing
        # single module that is local for our application.
        name = '.{0}'.format(setting_file_name.replace('.py', ''))

        log.debug('    Trying to load {0}'.format(name))

        # Die hard if you gave us non existing module name.
        imported_settings = import_module(name, package=parent_module_name)

        # Now from ``imported_settings`` object we get all public attributes
        # and parse them nicely into our ``settings`` dict.

        # dir will give us all attributes names.
        for attr_name in dir(imported_settings):

            # we need to just ignore private or debug attributes
            if attr_name.startswith('_'):
                continue

            # while getting all values from public attributes
            attr_val = getattr(imported_settings, attr_name)

            # parsing new values and updating settings.
            settings.update(_parse_item(attr_name, attr_val))


def _parse_item(name, value):
    """Parse value depending on type into dict of values.

    Returns dictionary object that should be used to update
    ``request.registry.settings``.

    :param name: string name of current attribute.
    :param value: value of current attribute, for now we handle strings,
        dictionaries, lists and tuples.

    """
    dict_to_return = {}

    # If value is dictionary, we need to parse it recursively while joining
    # keys. So for example pyramid = {'reload_templates': 'true'} will become:
    # 'pyramid.reload_templates': 'true'. This was IMHO cleanest way to
    # implement dots in setting keys (and that kind of namespaces are expected
    # by pyramid).
    if isinstance(value, dict):
        for key, val in value.items():
            full_key = '.'.join((name, key))
            dict_to_return.update(_parse_item(full_key, val))

    # TODO: Need to figure out lowest common type for both list and tuple.
    # List and tuple are just joined by line break to mirror paster behaviour.
    elif isinstance(value, (list, tuple)):
        # We only can join strings.
        try:
            string_list = [str(val) for val in value]
        except (TypeError, ValueError):
            string_list = []

        dict_to_return[name] = '\n'.join(string_list)

    else:
        dict_to_return[name] = value

    return dict_to_return
