"""Tools common to multiple modules.
"""
import sys
import inspect


# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
class Msg(object):
    """
    Outputs messages based on verbosity.
    prefix ~ text put at the start of each message
    outfile ~ option to direct the output elsewhere i.e. sys.stderr
    """
    def __init__(self, verbose=True, prefix="", file=sys.stdout):
        """
        Initialise

        Parameters
        ----------

        verbose : :obj:`bool` (optional)
            The verbosity of the message, if False then any calls
            to msg are not displayed
        prefix : :obj:`str` (optional)
            A prefix to add to every message when msg is called. The
            default is no prefix
        file : :obj:`FileLikeoObject`
            Where the message should be directed. The default is STDOUT
            but can be changed to STDERR by passing sys.stderr. Also can
            be redirected to a file if needed
        """

        # Make sure verbose is boolean
        if not isinstance(verbose, bool):
            raise TypeError('verbose should be true or false')

        self.verbose = verbose
        self.prefix = prefix
        self.file = file

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def msg(self, m, verbose=None, prefix=None, file=None):
        """
        Print the message if verbosity is True

        Parameters
        ----------
        m : :obj:`str`
            The message to print
        prefix : :obj:`str`, optional
            An optional prefix to temporarily overide any global prefix
            set when the object was created. This saves adjusting the
            prefix for small adjustments
        outfile : :obj:`file`, optional
            An optional outfile to temporarily overide any global outfile
            set when the object was created, i.e. sys.stderr
        verbose : :obj:`Bool`, optional
            An optional verbosity to temporarily overide any global
            verbosity set when the object was created
        """

        # If the optional verbose argument has not been set then we set to the
        # verbosity of the object
        if verbose is None:
            verbose = self.verbose

        # If we are being verbose
        if verbose is True:
            # Use local or global prefix and/or outfile
            prefix = prefix or self.prefix
            file = file or self.file

            # If prefix is defined add a single space
            if prefix:
                prefix = prefix + " "

            # Output the message
            print("%s%s" % (prefix, m), file=file)

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def msg_atts(self, obj, **kwargs):
        """
        Print the attributes of an object message if verbosity is True

        Parameters
        ----------
        atts : :obj:`str`
            An object who's attributes can be obtained with getattr
        prefix : :obj:`str`, optional
            An optional prefix to temporarily overide any global prefix
            set when the object was created. This saves adjusting the
            prefix for small adjustments
        outfile : :obj:`file`, optional
            An optional outfile to temporarily overide any global outfile
            set when the object was created, i.e. sys.stderr
        verbose : :obj:`Bool`, optional
            An optional verbosity to temporarily overide any global
            verbosity set when the object was created
        prog_name : :obj:`str`, optional
            A program name argument, if passed then a mini program name banner
            will be displayed before outputting the obj attributes
        versiob : :obj:`float`, optional
            The version number for prog_name, only supply if prog_name is given
        """

        try:
            # See if we have the prog_name and version arguments. If so then
            # msg them out and also remove from kwargs so they do not cause an
            # error when passed to msg
            prog_name = kwargs.pop('prog_name')
            version = ''
            if 'version' in kwargs:
                version = " v%s" % str(kwargs.pop('version'))
            self.msg("= %s%s =" % (prog_name, version), **kwargs)
        except KeyError:
            pass

        # Loop through the objects attributes
        for i in inspect.getmembers(obj):
            # Ignores anything starting with underscore
            # (that is, private and protected attributes)
            if not i[0].startswith('_'):
                # Ignores methods
                if not inspect.ismethod(i[1]):
                    attribute = str(i[1])
                    modifier = "value"
                    # For lists, dicts and tuples we report the length
                    if isinstance(i[1], list) is True or\
                       isinstance(i[1], dict) is True or\
                       isinstance(i[1], tuple) is True:
                        attribute = str(len(i[1]))
                        modifier = "length"
                    # Output the message, here we use clean password of lazy
                    # SQLAlchemy to make sure any passwords are stripped
                    # from connection URLS
                    self.msg("%s %s: %s" % (i[0],
                                            modifier,
                                            attribute),
                             **kwargs)
