# Make coding more python3-ish
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

import os.path
import platform
import re
import textwrap

from ansible import __version__
from ansible.module_utils._text import to_text
from ansible.module_utils.six import string_types

def system(vagrant_version=None):
    # Get most recent Trellis CHANGELOG entry
    changelog_msg = ''
    ansible_config_path = os.getenv('ANSIBLE_CONFIG')
    ansible_path = os.path.dirname(ansible_config_path) if ansible_config_path else os.getcwd()
    changelog = os.path.join(ansible_path, 'CHANGELOG.md')

    if os.path.isfile(changelog):
        with open(changelog) as f:
            str = f.read(200)

        # Retrieve release number if it is most recent entry
        release = re.search(r'^###\s((?!HEAD).*)', str)
        if release is not None:
            changelog_msg = '\n  Trellis {0}'.format(release.group(1))

        # Retrieve most recent changelog entry
        else:
            change = re.search(r'^\*\s?(\[BREAKING\])?([^\(\n\[]+)', str, re.M|re.I)
            if change is not None:
                changelog_msg = '\n  Trellis version (per changelog): "{0}"'.format(change.group(2).strip())

    # Vagrant info, if available
    vagrant = ' Vagrant {0};'.format(vagrant_version) if vagrant_version else ''

    # Assemble components and return
    return 'System info:\n  Ansible {0};{1} {2}{3}'.format(__version__, vagrant, platform.system(), changelog_msg)

def reset_task_info(obj, task=None):
    obj.action = None if task is None else task._get_parent_attribute('action')
    obj.first_host = True
    obj.first_item = True
    obj.task_failed = False

# Display dict key only, instead of full json dump
def replace_item_with_key(obj, result):
    item = '_ansible_item_label' if '_ansible_item_label' in result._result else 'item'
    should_replace = (
        not obj._display.verbosity
        and 'label' not in result._task._ds.get('loop_control', {})
        and item in result._result
    )

    if should_replace:
        if 'key' in result._result[item]:
            result._result[item] = result._result[item]['key']
        elif type(result._result[item]) is dict:
            subitem = '_ansible_item_label' if '_ansible_item_label' in result._result[item] else 'item'
            if 'key' in result._result[item].get(subitem, {}):
                result._result[item] = result._result[item][subitem]['key']
            elif '_ansible_item_label' in result._result[item]:
                result._result[item] = result._result[item]['_ansible_item_label']

def display(obj, result):
    msg = ''
    result = result._result
    display = obj._display.display
    wrap_width = 77
    first = obj.first_host and obj.first_item

    # Only display msg if debug module or if failed (some modules have undesired 'msg' on 'ok')
    if 'msg' in result and (obj.task_failed or obj.action == 'debug'):
        msg = result.pop('msg', '')

        # Disable Ansible's verbose setting for debug module to avoid the CallbackBase._dump_results()
        if '_ansible_verbose_always' in result:
            del result['_ansible_verbose_always']

    # Display additional info when failed
    if obj.task_failed:
        items = (item for item in ['reason', 'module_stderr', 'module_stdout', 'stderr'] if item in result and to_text(result[item]) != '')
        for item in items:
            msg = result[item] if msg == '' else '\n'.join([msg, result.pop(item, '')])

        # Add blank line between this fail message and the json dump Ansible displays next
        msg = '\n'.join([msg, ''])

    # Must pass unicode strings to Display.display() to prevent UnicodeError tracebacks
    if isinstance(msg, list):
        msg = '\n'.join([to_text(x) for x in msg])
    elif not isinstance(msg, string_types):
        msg = to_text(msg)

    # Wrap text
    msg = '\n'.join([textwrap.fill(line, wrap_width, replace_whitespace=False)
                     for line in msg.splitlines()])

    # Display system info and msg, with horizontal rule between hosts/items
    hr = '-' * int(wrap_width*.67)

    if obj.task_failed and first:
        display(system(obj.vagrant_version), 'bright gray')
        display(hr, 'bright gray')

    if msg == '':
        if obj.task_failed and not first:
            display(hr, 'bright gray')
        else:
            return
    else:
        if not first:
            display(hr, 'bright gray')
        display(msg, 'red' if obj.task_failed else 'bright purple')

def display_host(obj, result):
    if 'results' not in result._result:
        display(obj, result)
        obj.first_host = False

def display_item(obj, result):
    display(obj, result)
    obj.first_item = False

@@ -4217,32 +4217,50 @@
 ), 'bright gray'
+, screen_only=True
 )%0A        displa
@@ -4278,16 +4278,34 @@
 ht gray'
+, screen_only=True
 )%0A%0A    i
@@ -4388,32 +4388,50 @@
 r, 'bright gray'
+, screen_only=True
 )%0A        else:%0A
@@ -4473,32 +4473,32 @@
   if not first:%0A
-
             disp
@@ -4518,16 +4518,34 @@
 ht gray'
+, screen_only=True
 )%0A      

Fix display output in logs