#==============================================================
#
#  Abs:   Module for creating pretty tables
#
#  Name:  tables.py
#
#  Rem:   This module converts 2D arrays into pretty tables.
#         The columns are decimal aligned if numbers are
#         present, otherwise left aligned.
#
#  Facility: SLAC Controls
#
#  Auth:  05-Aug-2019, A. Le (anle)
#--------------------------------------------------------------
#  Mods: (Latest to oldest)
#
#==============================================================
#
def make_table(rows):
    '''Generates a pretty table from a 2D array (list of lists)'''
    if len(rows) == 0:
        return ""
    aligned_cols = []
    padded_rows = pad_rows(rows)
    for i in range(len(padded_rows[0])):
        col = [row[i] for row in padded_rows]
        aligned = left_align(col)
        aligned_cols.append(aligned)

    table = ""
    for i, row in enumerate(padded_rows):
        new_row = ""
        for j in range(len(row)):
            new_row += aligned_cols[j][i]
            if j == len(row) - 1:
                table += new_row.rstrip() + "\n"
            else:
                new_row += "   "

    return table

def pad_rows(rows):
    '''Adds empty elements to short rows so that all rows have
    the same number of columns
    '''
    max_col = max(len(row) for row in rows)
    padded_rows = list(rows)
    for row in padded_rows:
        cols_to_add = max_col - len(row)
        row.extend([""] * cols_to_add)

    return padded_rows

def left_align(column):
    '''Left or decimal aligns a column. Ensures that all
    elements have the same string length
    '''
    strings = [str(element) for element in column]
    dot_idxs = [dot_index(string) for string in strings]
    max_idx = max(dot_idxs)
    dot_idxs = [max_idx if idx == -1 else idx for idx in dot_idxs]
    aligned = [" " * (max_idx - dot_idx) + string for string, dot_idx in zip(strings, dot_idxs)]
    max_len = max(len(string) for string in aligned)
    return ["{:<{}}".format(string, max_len) for string in aligned]

def dot_index(string):
    '''Determines the index of the decimal point.
    Returns -1 if decimal point is not found.
    '''
    try:
        float(string)
        index = string.find(".")
        if index == -1:
            return len(string)
        else:
            return index
    except ValueError:
        return -1
