import math
import shutil
import sys


class Formatter:
    def start(self):
        pass

    def result(self, result):
        """ Render a single result
        :type result: hwtest.component.ComponentStatus
        """
        pass

    def finish(self):
        pass

    def _format_value(self, result):
        value = ""
        if result.value is not None:
            value = result.value
        return value

    def _format_status(self, result):
        status = "broken"
        if result.status:
            status = "working"
        return status

    def diff(self, added, removed, fixed, broken):
        if len(added) + len(removed) + len(broken) + len(fixed) == 0:
            print('Nothing changed')
            sys.exit(0)

        if len(added) > 0:
            print('Added hardware: ')
            for result in added:
                status = 'working' if result.status else 'broken'
                print(' * {} [{}] {} {}'.format(result.model, result.path, status, result.value))

        if len(fixed) > 0:
            print('Fixed hardware: ')
            for result in fixed:
                print(' * {} [{}] {}'.format(result.model, result.path, result.value))

        if len(broken) > 0:
            print('Broken hardware: ')
            for result in broken:
                print(' * {} [{}]'.format(result.model, result.path))

        if len(removed) > 0:
            print('Removed hardware: ')
            for result in removed:
                category, model = result.split('.', maxsplit=1)
                print(' * {} ({})'.format(model, category))


class ShellTableFormatter(Formatter):
    def __init__(self):
        self.row = None

    def start(self):
        width, height = shutil.get_terminal_size((120, 25))
        self.row = "{: <16} {: <20} "
        width -= 16 + 1 + 20 + 1
        flexwidth = math.floor((width - 10) / 2)
        self.row += "{: <" + str(flexwidth) + "} "
        self.row += "{: <10} "
        self.row += "{}"

        print(self.row.format("Category", "Model", "Path", "Status", "Value"))

    def result(self, result):
        status = self._format_status(result)
        value = self._format_value(result)
        print(self.row.format(result.category, result.model, result.path, status, value))


class MarkdownTableFormatter(Formatter):
    def start(self):
        print('| Category | Model | Path | Status | Value |')
        print('| -------- | ----- | ---- | ------ | ----- |')

    def result(self, result):
        status = self._format_status(result)
        value = self._format_value(result)
        value = value.replace("\n", "<br>")
        print('| {} | {} | {} | {} | {} |'.format(result.category, result.model, result.path, status, value))


class MediaWikiTableFormatter(Formatter):
    def start(self):
        print('{| class="wikitable feature-colors"')
        print('! style="text-align:left;"| Category')
        print('! style="text-align:left;"| Model')
        print('! style="text-align:left;"| Path')
        print('! style="text-align:left;"| Status')
        print('! style="text-align:left;"| Value')

    def result(self, result):
        value = self._format_value(result)

        model = result.model
        if model == '-':
            model = ''

        status = 'Broken'
        if result.status:
            status = ' class="feature-yes"| Working'

        print('|-')
        print('|{}'.format(result.category))
        print('|{}'.format(model))
        print('|{}'.format(result.path))
        print('|{}'.format(status))
        print('|{}'.format(value))

    def finish(self):
        print("|}")


class ColoredTTYFormatter(Formatter):
    def __init__(self):
        self.success = None
        self.fail = None

    def start(self):
        width, height = shutil.get_terminal_size((120, 25))
        self.success = '\x1b[1;32;40m' \
                       '{: <' + str(width) + '}\x1b[0;32;40m{: <' + str(width) + '}{: <' + str(width) + '}\x1b[0m'
        self.fail = '\x1b[1;31;40m' \
                    '{: <' + str(width) + '}\x1b[0;31;40m{: <' + str(width) + '}{: <' + str(width) + '}\x1b[0m'

    def result(self, result):
        value = self._format_value(result)
        line1 = "{} {}".format(result.category, result.model)
        line2 = "      {}".format(result.path)
        line3 = "      {}".format(value)

        if result.status:
            print(self.success.format(line1, line2, line3))
        else:
            print(self.fail.format(line1, line2, line3))

    def diff(self, added, removed, fixed, broken):
        width, height = shutil.get_terminal_size((120, 25))

        if len(added) + len(removed) + len(broken) + len(fixed) == 0:
            print('\x1b[1;32;40m')
            print('*' + ((width - 2) * '-') + '*')
            centered = '|{:^' + str(width - 2) + '}|'
            print(centered.format('Nothing changed'))
            print('*' + ((width - 2) * '-') + '*')
            print('\x1b[0m')
            sys.exit(0)

        if len(added) > 0:
            print('\x1b[1;32;40m')
            print('** Added hardware **')

            print('Added hardware: ')
            for result in added:
                status = 'working' if result.status else 'broken'
                print(' * {} [{}] {} {}'.format(result.model, result.path, status, result.value))
            print('\x1b[0m')

        if len(fixed) > 0:
            print('\x1b[1;32;40m')
            print('** Fixed hardware **')
            for result in fixed:
                print(' * {} [{}] {}'.format(result.model, result.path, result.value))
            print('\x1b[0m')

        if len(broken) > 0:
            print('\x1b[1;31;40m')
            print('** Broken hardware **')
            for result in broken:
                print(' * {} [{}]'.format(result.model, result.path))
            print('\x1b[0m')

        if len(removed) > 0:
            print('\x1b[1;31;40m')
            print('** Removed hardware **')
            for result in removed:
                category, model = result.split('.', maxsplit=1)
                print(' * {} ({})'.format(model, category))
            print('\x1b[0m')
