import click
import contextlib
import os
import re
import toml

import shakedown


def read_config(args):
    """ Read configuration options from ~/.shakedown (if exists)

        :param args: a dict of arguments
        :type args: dict

        :return: a dict of arguments
        :rtype: dict
    """

    configfile = os.path.expanduser('~/.shakedown')

    if os.path.isfile(configfile):
        with open(configfile, 'r') as f:
            config = toml.loads(f.read())

        for key in config:
            param = key.replace('-', '_')

            if not param in args or args[param] in [False, None]:
                args[param] = config[key]

    return args


def set_config_defaults(args):
    """ Set configuration defaults

        :param args: a dict of arguments
        :type args: dict

        :return: a dict of arguments
        :rtype: dict
    """

    defaults = {
        'fail': 'fast',
        'stdout': 'fail'
    }

    for key in defaults:
        if not args[key]:
            args[key] = defaults[key]

    return args


def fchr(char):
    """ Print a fancy character

        :param char: the shorthand character key
        :type char: str

        :return: a fancy character
        :rtype: str
    """

    return {
        'PP': chr(10003),
        'FF': chr(10005),
        'SK': chr(10073),
        '>>': chr(12299)
    }.get(char, '')


def banner():
    """ Display a product banner

        :return: a delightful Mesosphere logo rendered in unicode
        :rtype: str
    """

    banner_dict = {
        'a0': click.style(chr(9601), fg='magenta'),
        'a1': click.style(chr(9601), fg='magenta', bold=True),
        'b0': click.style(chr(9616), fg='magenta'),
        'c0': click.style(chr(9626), fg='magenta'),
        'c1': click.style(chr(9626), fg='magenta', bold=True),
        'd0': click.style(chr(9622), fg='magenta'),
        'd1': click.style(chr(9622), fg='magenta', bold=True),
        'e0': click.style(chr(9623), fg='magenta'),
        'e1': click.style(chr(9623), fg='magenta', bold=True),
        'f0': click.style(chr(9630), fg='magenta'),
        'f1': click.style(chr(9630), fg='magenta', bold=True),
        'g1': click.style(chr(9612), fg='magenta', bold=True),
        'h0': click.style(chr(9624), fg='magenta'),
        'h1': click.style(chr(9624), fg='magenta', bold=True),
        'i0': click.style(chr(9629), fg='magenta'),
        'i1': click.style(chr(9629), fg='magenta', bold=True),
        'j0': click.style(fchr('>>'), fg='magenta'),
        'k0': click.style(chr(9473), fg='magenta'),
        'l0': click.style('_', fg='magenta'),
        'l1': click.style('_', fg='magenta', bold=True),
        'v0': click.style('mesosphere', fg='magenta'),
        'x1': click.style('shakedown', fg='magenta', bold=True),
        'y0': click.style('v' + shakedown.VERSION, fg='magenta'),
        'z0': chr(32)
    }

    banner_map = [
        " %(z0)s%(z0)s%(l0)s%(l0)s%(l1)s%(l0)s%(l1)s%(l1)s%(l1)s%(l1)s%(l1)s%(l1)s%(l1)s%(l1)s",
        " %(z0)s%(b0)s%(z0)s%(c0)s%(z0)s%(d0)s%(z0)s%(z0)s%(z0)s%(z0)s%(e1)s%(z0)s%(f1)s%(z0)s%(g1)s",
        " %(z0)s%(b0)s%(z0)s%(z0)s%(c0)s%(z0)s%(h0)s%(e0)s%(d1)s%(i1)s%(z0)s%(f1)s%(z0)s%(z0)s%(g1)s%(z0)s%(j0)s%(v0)s %(x1)s %(y0)s",
        " %(z0)s%(b0)s%(z0)s%(z0)s%(f0)s%(c0)s%(i0)s%(z0)s%(z0)s%(h1)s%(f1)s%(c1)s%(z0)s%(z0)s%(g1)s%(z0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(k0)s%(z0)s%(k0)s%(k0)s%(z0)s%(z0)s%(k0)s",
        " %(z0)s%(i0)s%(f0)s%(h0)s%(z0)s%(z0)s%(c0)s%(z0)s%(z0)s%(f0)s%(z0)s%(z0)s%(i1)s%(c1)s%(h1)s",
        " %(z0)s%(z0)s%(z0)s%(z0)s%(z0)s%(z0)s%(z0)s%(c0)s%(f0)s",
    ]

    if 'TERM' in os.environ and os.environ['TERM'] in ('velocity', 'xterm', 'xterm-256color', 'xterm-color'):
        return echo("\n".join(banner_map) % banner_dict)
    else:
        return echo(fchr('>>') + 'mesosphere shakedown v' + shakedown.VERSION, b=True)


def decorate(text, style):
    """ Console decoration style definitions

        :param text: the text string to decorate
        :type text: str
        :param style: the style used to decorate the string
        :type style: str

        :return: a decorated string
        :rtype: str
    """

    return {
        'step-maj': click.style("\n" + '> ' + text, fg='yellow', bold=True),
        'step-min': click.style('  - ' + text + ' ', bold=True),
        'item-maj': click.style('    - ' + text + ' '),
        'item-min': click.style('      - ' + text + ' '),
        'quote-head-fail': click.style("\n" + chr(9485) + (chr(9480)*2) + ' ' + text, fg='red'),
        'quote-head-pass': click.style("\n" + chr(9485) + (chr(9480)*2) + ' ' + text, fg='green'),
        'quote-head-skip': click.style("\n" + chr(9485) + (chr(9480)*2) + ' ' + text, fg='yellow'),
        'quote-fail': re.sub('^', click.style(chr(9482) + ' ', fg='red'), text, flags=re.M),
        'quote-pass': re.sub('^', click.style(chr(9482) + ' ', fg='green'), text, flags=re.M),
        'quote-skip': re.sub('^', click.style(chr(9482) + ' ', fg='yellow'), text, flags=re.M),
        'fail': click.style(text + ' ', fg='red'),
        'pass': click.style(text + ' ', fg='green'),
        'skip': click.style(text + ' ', fg='yellow')
    }.get(style, '')


def echo(text, **kwargs):
    """ Print results to the console

        :param text: the text string to print
        :type text: str

        :return: a string
        :rtype: str
    """

    if shakedown.cli.quiet:
        return

    if not 'n' in kwargs:
        kwargs['n'] = True

    if 'd' in kwargs:
        text = decorate(text, kwargs['d'])

    if 'TERM' in os.environ and os.environ['TERM'] == 'velocity':
        if text:
            print(text, end="", flush=True)
        if kwargs.get('n'):
            print()
    else:
        click.echo(text, nl=kwargs.get('n'))


@contextlib.contextmanager
def stdchannel_redirected(stdchannel, dest_filename):
    """ A context manager to temporarily redirect stdout or stderr

        :param stdchannel: the channel to redirect
        :type stdchannel: sys.std
        :param dest_filename: the filename to redirect output to
        :type dest_filename: os.devnull

        :return: nothing
        :rtype: None
    """

    try:
        oldstdchannel = os.dup(stdchannel.fileno())
        dest_file = open(dest_filename, 'w')
        os.dup2(dest_file.fileno(), stdchannel.fileno())

        yield
    finally:
        if oldstdchannel is not None:
            os.dup2(oldstdchannel, stdchannel.fileno())
        if dest_file is not None:
            dest_file.close()