# Copyright (C) 2008-2011 Dejan Muhamedagic <dmuhamedagic@suse.de>
# See COPYING for license information.

import sys
import re

# from: http://code.activestate.com/recipes/475116/

# A module that can be used to portably generate formatted output to
# a terminal.
# Defines a set of instance variables whose
# values are initialized to the control sequence necessary to
# perform a given action.  These can be simply included in normal
# output to the terminal:
#     >>> print 'This is '+term.colors.GREEN+'green'+term.colors.NORMAL
# Alternatively, the `render()` method can used, which replaces
# '${action}' with the string required to perform 'action':
#     >>> print term.render('This is ${GREEN}green${NORMAL}')
# If the terminal doesn't support a given action, then the value of
# the corresponding instance variable will be set to ''.  As a
# result, the above code will still work on terminals that do not
# support color, except that their output will not be colored.
# Also, this means that you can test whether the terminal supports a
# given action by simply testing the truth value of the
# corresponding instance variable:
#     >>> if term.colors.CLEAR_SCREEN:
#     ...     print 'This terminal supports clearning the screen.'
# Finally, if the width and height of the terminal are known, then
# they will be stored in the `COLS` and `LINES` attributes.


class colors(object):
    # Cursor movement:
    BOL = ''             #: Move the cursor to the beginning of the line
    UP = ''              #: Move the cursor up one line
    DOWN = ''            #: Move the cursor down one line
    LEFT = ''            #: Move the cursor left one char
    RIGHT = ''           #: Move the cursor right one char
    # Deletion:
    CLEAR_SCREEN = ''    #: Clear the screen and move to home position
    CLEAR_EOL = ''       #: Clear to the end of the line.
    CLEAR_BOL = ''       #: Clear to the beginning of the line.
    CLEAR_EOS = ''       #: Clear to the end of the screen
    # Output modes:
    BOLD = ''            #: Turn on bold mode
    BLINK = ''           #: Turn on blink mode
    DIM = ''             #: Turn on half-bright mode
    REVERSE = ''         #: Turn on reverse-video mode
    UNDERLINE = ''       #: Turn on underline mode
    NORMAL = ''          #: Turn off all modes
    # Cursor display:
    HIDE_CURSOR = ''     #: Make the cursor invisible
    SHOW_CURSOR = ''     #: Make the cursor visible
    # Terminal size:
    COLS = None          #: Width of the terminal (None for unknown)
    LINES = None         #: Height of the terminal (None for unknown)
    # Foreground colors:
    BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = ''
    # Background colors:
    BG_BLACK = BG_BLUE = BG_GREEN = BG_CYAN = ''
    BG_RED = BG_MAGENTA = BG_YELLOW = BG_WHITE = ''
    RLIGNOREBEGIN = '\001'
    RLIGNOREEND = '\002'

_STRING_CAPABILITIES = """
BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1
CLEAR_SCREEN=clear CLEAR_EOL=el CLEAR_BOL=el1 CLEAR_EOS=ed BOLD=bold
BLINK=blink DIM=dim REVERSE=rev UNDERLINE=smul NORMAL=sgr0
HIDE_CURSOR=cinvis SHOW_CURSOR=cnorm""".split()

_COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split()
_ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split()


def init():
    """
    Initialize attributes with appropriate values for the current terminal.

    `_term_stream` is the stream that will be used for terminal
    output; if this stream is not a tty, then the terminal is
    assumed to be a dumb terminal (i.e., have no capabilities).
    """
    def _tigetstr(cap_name):
        import curses
        cap = curses.tigetstr(cap_name) or ''

        # String capabilities can include "delays" of the form "$<2>".
        # For any modern terminal, we should be able to just ignore
        # these, so strip them out.
        # terminof(5) states that:
        #   A "/" suffix indicates that the padding is mandatory and forces a
        #   delay of the given number of milliseconds even on devices for which
        #   xon is present to indicate flow control.
        # So let's respect that. But not the optional ones.
        cap = re.sub(r'\$<\d+>[*]?', '', cap)

        # To switch back to "NORMAL", we use sgr0, which resets "everything" to defaults.
        # That on some terminals includes ending "alternate character set mode".
        # Which is usually done by appending '\017'.  Unfortunately, less -R
        # does not eat that, but shows it as an annoying inverse '^O'
        # Instead of falling back to less -r, which would have other implications as well,
        # strip off that '\017': we don't use the alternative character set,
        # so we won't need to reset it either.
        if cap_name == 'sgr0':
            cap = re.sub(r'\017$', '', cap)

        return cap

    _term_stream = sys.stdout
    # Curses isn't available on all platforms
    try:
        import curses
    except:
        sys.stderr.write("INFO: no curses support: you won't see colors\n")
        return
    # If the stream isn't a tty, then assume it has no capabilities.
    from . import config
    if not _term_stream.isatty() and 'color-always' not in config.color.style:
        return
    # Check the terminal type.  If we fail, then assume that the
    # terminal has no capabilities.
    try:
        curses.setupterm()
    except:
        return

    # Look up numeric capabilities.
    colors.COLS = curses.tigetnum('cols')
    colors.LINES = curses.tigetnum('lines')
    # Look up string capabilities.
    for capability in _STRING_CAPABILITIES:
        (attrib, cap_name) = capability.split('=')
        setattr(colors, attrib, _tigetstr(cap_name) or '')
    # Colors
    set_fg = _tigetstr('setf')
    if set_fg:
        for i, color in zip(range(len(_COLORS)), _COLORS):
            setattr(colors, color, curses.tparm(set_fg, i) or '')
    set_fg_ansi = _tigetstr('setaf')
    if set_fg_ansi:
        for i, color in zip(range(len(_ANSICOLORS)), _ANSICOLORS):
            setattr(colors, color, curses.tparm(set_fg_ansi, i) or '')
    set_bg = _tigetstr('setb')
    if set_bg:
        for i, color in zip(range(len(_COLORS)), _COLORS):
            setattr(colors, 'BG_'+color, curses.tparm(set_bg, i) or '')
    set_bg_ansi = _tigetstr('setab')
    if set_bg_ansi:
        for i, color in zip(range(len(_ANSICOLORS)), _ANSICOLORS):
            setattr(colors, 'BG_'+color, curses.tparm(set_bg_ansi, i) or '')


def render(template):
    """
    Replace each $-substitutions in the given template string with
    the corresponding terminal control string (if it's defined) or
    '' (if it's not).
    """
    def render_sub(match):
        s = match.group()
        return getattr(colors, s[2:-1].upper(), '')
    return re.sub(r'\${\w+}', render_sub, template)


def is_color(s):
    return hasattr(colors, s.upper())


# vim:ts=4:sw=4:et:
