# -*- coding: utf-8 -*-
from typing import Collection, Optional, Union

C_UL = "┌"
C_UR = "┐"
C_LL = "└"
C_LR = "┘"

C_V = "│"
C_VL = "├"
C_VR = "┤"

C_H = "─"
C_HD = "┬"
C_HU = "┴"
C_X = "┼"


def row(
    fields: Collection[str],
    sep: str = C_V,
    start: str = C_V,
    end: str = C_V,
) -> str:
    res = []

    for i, field in enumerate(fields):
        if not i:
            res.append(start)
        else:
            res.append(sep)

        res.append(field)

    res.append(end)

    return "".join(res)


def table(
    data: Collection[Collection[str]],
    headers: Optional[Collection] = None,
    header_interval: Optional[int] = None,
    justify: Optional[Union[str, Collection[str]]] = None,
    collapse_empty: bool = False,
):
    res = []

    if not data:
        return ""

    columns = max([len(x) for x in data])
    if headers:
        columns = max(columns, len(headers))

    if isinstance(justify, str):
        justify = [justify] * columns
    elif not justify:
        justify = []

    justify = list(justify) + ["^"] * (columns - len(justify))

    colwidths = [0] * columns

    if headers:
        for i, col in enumerate(headers):
            colwidths[i] = max(colwidths[i], len(col))

    for data_row in data:
        for i, col in enumerate(data_row):
            colwidths[i] = max(colwidths[i], len(col))

    def row_iterable():
        if headers and not header_interval:
            yield headers

        for i, data_row in enumerate(data):
            if header_interval and i % header_interval == 0:
                yield headers
            yield data_row

    for i, data_row in enumerate(row_iterable()):
        if not i:
            res.append(
                row([C_H * colw for colw in colwidths], start=C_UL, sep=C_HD, end=C_UR)
            )
        else:
            res.append(
                row([C_H * colw for colw in colwidths], start=C_VL, sep=C_X, end=C_VR)
            )
        is_empty = not any([True for x in data_row if x.strip()])
        if not (is_empty and collapse_empty):
            padding = [""] * (columns - len(data_row))
            padded_row = [
                f"{cell:{justify[j]}{colwidths[j]}s}"
                for j, cell in enumerate(list(data_row) + padding)
            ]
            res.append(row(padded_row))

    res.append(row([C_H * colw for colw in colwidths], start=C_LL, sep=C_HU, end=C_LR))

    return "\n".join(res)


if __name__ == "__main__":
    headers = [""] + ["···{:01X}".format(x) for x in range(16)]
    data = [[f"{x:03X}·"] for x in range(32)]

    print(
        table(
            data,
            headers=headers,
            justify=">",
            header_interval=8,
        )
    )