from abc import ABC, abstractmethod


class TableFormatter(ABC):
    @abstractmethod
    def headings(self, headers):
        pass

    @abstractmethod
    def row(self, rowdata):
        pass


class TextTableFormatter(TableFormatter):
    def _printer(self, data):
        print(*("{: >10}".format(value) for value in data))

    def headings(self, headers):
        self._printer(headers)
        print(*("{:->10}".format("") for _ in headers))

    def row(self, rowdata):
        self._printer(rowdata)


class CSVTableFormatter(TableFormatter):
    def _printer(self, data):
        print(",".join(str(value) for value in data))

    def headings(self, headers):
        return self._printer(headers)

    def row(self, rowdata):
        return self._printer(rowdata)


class HTMLTableFormatter(TableFormatter):
    def _cell(self, value, tag):
        return f"<{tag}>{value}</{tag}>"

    def _printer(self, data, tag):
        line = f"<tr>{' '.join(self._cell(str(value), tag) for value in data)}</tr>"
        print(line)

    def headings(self, headers):
        return self._printer(headers, "th")

    def row(self, rowdata):
        return self._printer(rowdata, "td")


class ColumnFormatMixin:
    formats = []

    def row(self, rowdata):
        rowdata = [(fmt % d) for fmt, d in zip(self.formats, rowdata)]
        super().row(rowdata)


class UpperHeadersMixin:
    def headings(self, headers):
        super().headings([h.upper() for h in headers])


def create_formatter(name, column_formats=None, upper_headers=False):
    formatters = {
        "text": TextTableFormatter,
        "csv": CSVTableFormatter,
        "html": HTMLTableFormatter,
    }
    formatter = formatters.get(name, None)
    if not name:
        raise ValueError(f'formatter named "{name}" not implemented')
    if upper_headers:

        class _UpperFormatter(UpperHeadersMixin, formatter):
            pass

        formatter = _UpperFormatter
    if column_formats:

        class _ColumnFormatter(ColumnFormatMixin, formatter):
            formats = column_formats

        formatter = _ColumnFormatter
    return formatter()


def print_table(records, fields, formatter):
    if not isinstance(formatter, TableFormatter):
        raise TypeError("expected a TableFormatter")

    formatter.headings(fields)
    for record in records:
        formatter.row([getattr(record, fieldname) for fieldname in fields])
