"""Helper functions for making things readable by humans."""
import inspect
import math
import types
import typing


__all__ = ['humanize_bytes', 'print_table']


def print_table(headers: typing.List[str], columns: typing.List[typing.List[str]],
                order: typing.List[int] = None):
    """Pretty-prints a table.

    Parameters:
        headers: The column names.
        columns: The column values.
        order: Order in which to print the column the values. Defaults to the order in which the
            values are given.

    """

    # Check inputs
    if len(headers) != len(columns):
        raise ValueError('there must be as many headers as columns')

    if len(set(map(len, columns))) > 1:
        raise ValueError('all the columns must be of the same length')

    # Determine the width of each column based on the maximum length of it's elements
    col_widths = [
        max(*map(len, col), len(header))
        for header, col in zip(headers, columns)
    ]

    # Make a template to print out rows one by one
    row_format = ' '.join(['{:' + str(width + 2) + 's}' for width in col_widths])

    # Determine the order in which to print the column values
    if order is None:
        order = range(len(columns[0]))

    # Build the table
    table = (
        row_format.format(*headers) + '\n' +
        '\n'.join((
            row_format.format(*[
                col[i].rjust(width)
                for col, width in zip(columns, col_widths)
            ])
            for i in order
        ))
    )

    return table


def humanize_bytes(n_bytes):
    """Returns a human-friendly byte size."""
    suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
    human = n_bytes
    rank = 0
    if n_bytes != 0:
        rank = int((math.log10(n_bytes)) / 3)
        rank = min(rank, len(suffixes) - 1)
        human = n_bytes / (1024.0 ** rank)
    f = ('%.2f' % human).rstrip('0').rstrip('.')
    return '%s %s' % (f, suffixes[rank])
