import six


def _flat_dict(cfg_dict, parent=None, notation="dict"):
    """Convert nested dictionary into flattened dict of key-chain
    value pairs where the key-chain is a tuple of nested keys.

    For example, {"key1": {"key11":{"key111": "value1"}}}
    evaluates to  {("key1", "key11", "key111"): "value1"}.

    Simplifies dictionary operations for other parser functions.

    Parameters
    ----------
    cfg_dict: dict
        Dictionary containing config data.
    parent: None, optional
        For nested, recursive calls remembers the parent of current level.
    notation: {"dict", "dot"}, optional
        Provide notation type into which the unflattened dictionary will be
        converted by _unflat_dict later on. This information is required here
        because keys which are not strings cannot be used as attribute names
        for the DotNotation. Therefore, dictionaries containing non-string
        keys are not converted to DotNotation.

    Return
    ------
    flattened: dict

    """

    if not parent:
        parent = []

    # check for non string keys for dot notation
    if notation == "dot":
        if not all([isinstance(x, six.string_types) for x in cfg_dict.keys()]):
            return {tuple(parent):cfg_dict}

    flattened = {}
    for key, value in cfg_dict.items():
        key_level = parent + [key]

        # unflat nested dictionaries
        if isinstance(value, dict):
            flattened.update(_flat_dict(value, key_level, notation))
        else:
            key_chain = tuple(key_level)
            flattened[key_chain] = value

    return flattened