#!/usr/bin/python
# -*- coding: utf-8 -*-

""" Copyright 2005 - 2008 Gunnar Wrobel
              2011 - Brian Dolbec
 Distributed under the terms of the GNU General Public License v2
"""

from __future__ import unicode_literals
from __future__ import print_function

__version__ = "0.1"


import sys

from layman.constants import codes, INFO_LEVEL, WARN_LEVEL, NOTE_LEVEL, DEBUG_LEVEL, OFF
from layman.compatibility import encode

# py3.2
if sys.hexversion >= 0x30200f0:
    from io import IOBase as BUILTIN_FILE_TYPE
else:
    BUILTIN_FILE_TYPE=file

class MessageBase(object):
    """Base Message class helper functions and variables
    """

    def __init__(self,
                 out = sys.stdout,
                 err = sys.stderr,
                 info_level = INFO_LEVEL,
                 warn_level = WARN_LEVEL,
                 note_level = NOTE_LEVEL,
                 col = True,
                 error_callback=None
                 ):
        # Where should the error output go? This can also be a file
        if isinstance(err, BUILTIN_FILE_TYPE):
            self.error_out = err
        else:
            raise Exception("MessageBase: input parameter 'err' must be of type: file")

        # Where should the normal output go? This can also be a file
        if isinstance(out, BUILTIN_FILE_TYPE):
            self.std_out = out
        else:
            raise Exception("MessageBase: input parameter 'out' must be of type: file")

        # The higher the level the more information you will get
        self.warn_lev = warn_level

        # The higher the level the more information you will get
        self.info_lev = info_level

        # The higher the level the more information you will get
        self.note_lev = note_level

        # Should the output be colored?
        self.color_func = None
        self.set_colorize(col)

        self.debug_lev = OFF

        # callback function that gets passed any error messages
        # that have shown up.
        self.error_callback = error_callback
        self.block_callback = False


    def _color (self, col, text):
        return codes[col] + text + codes['reset']


    def _no_color (self, col, text):
        return text


    def set_colorize(self, state):
        if state:
            self.color_func = self._color
        else:
            self.color_func = self._no_color


    def set_info_level(self, info_level = INFO_LEVEL):
        self.info_lev = info_level


    def set_warn_level(self, warn_level = WARN_LEVEL):
        self.warn_lev = warn_level


    def set_note_level(self, note_level = NOTE_LEVEL):
        self.note_lev = note_level


    def set_debug_level(self, debugging_level = DEBUG_LEVEL):
        self.debug_lev = debugging_level

    def do_error_callback(self, error):
        """runs the error_callback function with the error
        that occurred
        """
        if self.error_callback is not None and not self.block_callback:
            self.error_callback(error)


class Message(MessageBase):
    """Primary Message output methods
    """
    #FIXME: Think about some simple doctests before you modify this class the
    #       next time.

    def __init__(self,
                 out = sys.stdout,
                 err = sys.stderr,
                 info_level = INFO_LEVEL,
                 warn_level = WARN_LEVEL,
                 note_level = NOTE_LEVEL,
                 col = True,
                 error_callback = None
                ):

        MessageBase.__init__(self, out, err, info_level, warn_level,
            note_level, col, error_callback)


    ## Output Functions

    def debug(self, info, level = OFF):
        """empty debug function, does nothing,
        declared here for compatibility with DebugMessage
        """
        info = encode(info)

        if level > self.debug_lev:
            return

        for i in info.split('\n'):
            print(self.color_func('yellow', 'DEBUG: ') + i, file=self.std_out)


    def notice (self, note, level = NOTE_LEVEL):

        if level > self.note_lev:
            return

        print(note, file=self.std_out)


    def info (self, info, level = INFO_LEVEL):

        info = encode(info)

        if level > self.info_lev:
            return

        for i in info.split('\n'):
            print(" %s %s" % (self.color_func('green', '*'),i), file=self.std_out)


    def status (self, message, status, info = 'ignored'):

        message = encode(message)

        lines = message.split('\n')

        if not len(lines):
            return

        for i in lines[0:-1]:
            print(" %s %s" % (self.color_func('green', '*'),i), file=self.std_out)

        i = lines[-1]

        if len(i) > 58:
            i = i[0:57]

        if status == 1:
            result = '[' + self.color_func('green', 'ok') + ']'
        elif status == 0:
            result = '[' + self.color_func('red', 'failed') + ']'
        else:
            result = '[' + self.color_func('yellow', info) + ']'

        print(file=" %s %s %s %S" % (self.color_func('green', '*'), i,
            ('.' * (58 - len(i))), result))


    def warn (self, warn, level = WARN_LEVEL):

        warn = encode(warn)

        if level > self.warn_lev:
            return

        for i in warn.split('\n'):
            print(" %s %s" % (self.color_func('yellow', '*'),i), file=self.std_out)


    def error (self, error, level = None):

        error = encode(error)

        for i in error.split('\n'):
            # NOTE: Forced flushing ensures that stdout and stderr
            # stay in nice order.  This is a workaround for calls like
            # "layman -L |& less".
            self.std_out.flush()
            self.error_out.flush()
            print(" %s %s" % (self.color_func('red', '*'), i), file=self.std_out)
            self.std_out.flush()
        self.do_error_callback(error)


    def die (self, error):

        error = encode(error)

        for i in error.split('\n'):
            self.error(self.color_func('red', 'Fatal error: ') + i)
        self.error(self.color_func('red', 'Fatal error(s) - aborting'))
        sys.exit(1)
