import sys
import os
from contextlib import contextmanager


class ColorPrint:
    def __init__(self, output=sys.stdout):
        self.system_output = output

    def get_console_width(self):
        system_output_fd = self.system_output.fileno()
        try:
            width, hight = os.get_terminal_size(system_output_fd)
        except OSError:
            return 80

        if width is None:
            return 80
        else:
            return width

    def print_right(self, left_offset, right_offset):
        console_width = self.get_console_width()
        number_of_spaces = console_width - left_offset - right_offset
        self.system_output.write(' ' * number_of_spaces)

    def print_colored(self, string, attribute='0', foreground='37',
                      background='40'):
        print_parameters = []
        if attribute:
            print_parameters.append(attribute)
        if foreground:
            print_parameters.append(foreground)
        if background:
            print_parameters.append(background)
        self.system_output.write('\033[{0}m{1}\033[0m'.
                                 format(';'.join(print_parameters), string))

    def print_bright_red(self, string):
        self.print_colored(string,
                           attribute='1',
                           foreground='31')

    def print_bright_green(self, string):
        self.print_colored(string,
                           attribute='1',
                           foreground='32')

    def print_bright_yellow(self, string):
        self.print_colored(string,
                           attribute='1',
                           foreground='33')

    def print_bright_blue(self, string):
        self.print_colored(string,
                           attribute='1',
                           foreground='34')

    def print_default(self, string):
        try:
            self.system_output.write(string)
        except UnicodeError:
            self.system_output.write(string.encode('utf-8'))
        try:
            self.system_output.flush()
        except IOError:
            exit(1)

    def print_line(self, left_arg, right_arg, left_offset=0,
                   printBR=True):
        COLORS = {'default': self.print_default,
                  'green': self.print_bright_green,
                  'red': self.print_bright_red,
                  'yellow': self.print_bright_yellow,
                  'blue': self.print_bright_blue}

        for color, left_string in left_arg:
            left_offset += len(left_string)
            COLORS.get(color, self.print_default)(left_string)

        right_offset = 0
        for color, right_string in right_arg:
            right_offset += len(right_string)
        if right_offset:
            self.print_right(left_offset, right_offset)
            for color, right_string in right_arg:
                COLORS.get(color, self.print_default)(right_string)
        if printBR:
            self.system_output.write('\n')
            try:
                self.system_output.flush()
            except IOError:
                exit(1)

    def print_ok(self, string, left_offset=0, printBR=True):
        self.system_output = sys.stdout
        self.print_line((('green', ' * '), ('', string)),
                        (('blue', '['), ('green', ' ok '), ('blue', ']')),
                        left_offset=left_offset, printBR=printBR)

    def print_only_ok(self, string, left_offset=0, printBR=True):
        self.system_output = sys.stdout
        self.print_line((('', string),),
                        (('blue', '['), ('green', ' ok '), ('blue', ']')),
                        left_offset=left_offset, printBR=printBR)

    def print_not_ok(self, string, left_offset=0, printBR=True):
        self.system_output = sys.stderr
        self.print_line((('green', ' * '), ('', string)),
                        (('blue', '['), ('red', ' !! '), ('blue', ']')),
                        left_offset=left_offset, printBR=printBR)

    def print_only_not_ok(self, string, left_offset=0, printBR=True):
        self.system_output = sys.stderr
        self.print_line((('', string),),
                        (('blue', '['), ('red', ' !! '), ('blue', ']')),
                        left_offset=left_offset, printBR=printBR)

    def print_warning(self, string, left_offset=0, printBR=True):
        self.system_output = sys.stdout
        self.print_line((('yellow', ' * '), ('', string)),
                        (('', ''),),
                        left_offset=left_offset, printBR=printBR)

    def print_error(self, string, left_offset=0, printBR=True):
        self.system_output = sys.stderr
        self.print_line((('red', ' * '), ('', string)),
                        (('', ''),),
                        left_offset=left_offset, printBR=printBR)

    def print_success(self, string, left_offset=0, printBR=True):
        self.system_output = sys.stdout
        self.print_line((('green', ' * '), ('', string)),
                        (('', ''),),
                        left_offset=left_offset, printBR=printBR)

    def print_info(self, string, left_offset=0, printBR=True):
        self.system_output = sys.stdout
        self.print_line((('blue', ' * '), ('', string)),
                        (('', ''),),
                        left_offset=left_offset, printBR=printBR)


# Заглушка вместо модуля вывода.
class IOModule:
    def __init__(self, save_messages=False):
        self.console_output = ColorPrint()

        self.save_messages = save_messages
        self.messages = []

    def set_error(self, message):
        if self.save_messages:
            self.messages.append(('error', message))
        self.console_output.print_error(message)

    def set_warning(self, message):
        if self.save_messages:
            self.messages.append(('warning', message))
        self.console_output.print_warning(message)

    def set_info(self, message):
        if self.save_messages:
            self.messages.append(('info', message))
        self.console_output.print_info(message)

    def set_success(self, message):
        if self.save_messages:
            self.messages.append(('success', message))
        self.console_output.print_ok(message)

    def set_failure(self, message):
        if self.save_messages:
            self.messages.append(('fail', message))
        self.console_output.print_not_ok(message)

    @contextmanager
    def set_task(self, message):
        pass
