#!/usr/bin/env python3
# generates markdown tables from CSV files, after doing lovely sorting
# by different columns, blah blah...
from copy import deepcopy
import functools

def write_mdwn_row(f, row):
    row = "|".join(row)
    row = "|%s|\n" % row
    f.write(row)

def underlines(header):
    row = []
    for col in header:
        row.append("-" * len(col))
    return row

# sorting functions
def is_svp64_page(page):
    return page in ['sv/setvl', 'sv/svstep', 'sv/remap']

def sort_by_page(p1, p2):
    p1 = p1['page']
    p2 = p2['page']
    if not (is_svp64_page(p1) ^ is_svp64_page(p2)):
        if p1 < p2: return -1
        if p1 > p2: return 1
        return 0
    if is_svp64_page(p1):
        return -1
    if is_svp64_page(p2):
        return 1
    return 0

levels = ['SFFS', 'SV/E', 'SV/D', 'SV/S', 'opt']

def sort_by_level(p1, p2):
    p1 = levels.index(p1['level'])
    p2 = levels.index(p2['level'])
    if p1 < p2: return -1
    if p1 > p2: return 1
    return 0

priorities = ['high', 'med', 'low', 'TBD']

def sort_by_priority(p1, p2):
    p1 = priorities.index(p1['priority'])
    p2 = priorities.index(p2['priority'])
    if p1 < p2: return -1
    if p1 > p2: return 1
    return 0

def sort_by_cost(p1, p2):
    p1 = p1['cost']
    p2 = p2['cost']
    if not p1.isdigit(): p1 = 0
    if not p2.isdigit(): p2 = 0
    p1 = int(p1)
    p2 = int(p2)
    if p1 < p2: return -1
    if p1 > p2: return 1
    return 0

def sort_by_level_priority_cost(p1, p2):
    v = sort_by_level(p1, p2)
    if v == 0:
        v = sort_by_priority(p1, p2)
    if v == 0:
        v = sort_by_cost(p1, p2)
    return v


def sort_by_cost_priority_page(p1, p2):
    v = sort_by_cost(p1, p2)
    if v == 0:
        v = sort_by_priority(p1, p2)
    if v == 0:
        v = sort_by_page(p1, p2)
    return v


def by_levelpriority_then_cost(areas):
    # first blat all columns together (drop area-dict)
    res = []
    for row in areas.values():
        res += row
    # now sort them
    res = sorted(res, key=functools.cmp_to_key(sort_by_level_priority_cost))
    # now split out into a dict again this time by cost-priority
    levels = {}
    for row in res:
        key = row['level']+row['priority']
        if key not in levels:
            levels[key] = []
        levels[key].append(row)
    return levels


def by_cost_then_priority_then_page(areas):
    # first blat all columns together (drop area-dict)
    res = []
    for row in areas.values():
        res += row
    # now sort them
    res = sorted(res, key=functools.cmp_to_key(sort_by_cost_priority_page))
    # now split out into a dict again this time by cost-priority
    costs = {}
    for row in res:
        cost = row['cost']
        if cost not in costs:
            costs[cost] = []
        costs[cost].append(row)
    return costs


# For prettier printing, replace short column heading
# names with full, consistent names.
# Expected input is a list of column strings
def column_header_replacement(header):
    replacement_col = {'cost': 'XO Cost'}
    new_header = deepcopy(header)
    for index, shortname in enumerate(replacement_col.keys()):
        # update with replacement if any otherwise leave alone
        new_header[index] = replacement_col.get(shortname, shortname)
    return new_header


def print_table(title, header, areas, sortby):
    fname = title.lower().replace(" ", "_")
    with open("ls012/%s.mdwn" % fname, "w") as f:
        # write out the page header
        f.write("\\newpage{}\n")
        f.write("\n")
        f.write("# %s\n" % title)
        f.write("\n")
        # sort everything if required
        if sortby is not None:
            areas = sortby(areas)
        linecount = None
        # start writing out areas
        for title, rows in areas.items():
            # start new page (if not first newpage)
            if linecount is not None:
                # allow 58 rows per page
                linecount += len(rows)
                if linecount >= 58:
                    linecount = 0
            if linecount == 0:
                f.write("\\newpage{}\n")
                f.write("\n")
            if linecount is None: # skipped first newpage
                linecount = 0

            f.write("## %s\n" % title)
            f.write("\n")

            # work out maximum length of items, and adjust header
            hdr = deepcopy(header)
            cols = {}
            for hd in hdr:
                cols[hd] = len(hd)
            for row in rows:
                for hd, value in row.items():
                    cols[hd] = max(cols[hd], len(value))
            # adjust header (add spaces)
            for i, hd in enumerate(hdr):
                n_spaces = cols[hd] - len(hd)
                hdr[i] = hdr[i] + " " * n_spaces
            # write out header
            write_mdwn_row(f, hdr)
            write_mdwn_row(f, underlines(hdr))
            for row in rows:
                # adjust row (add same spaces as header width)
                r = []
                for key in row.keys():
                    col_len, value = cols[key], row[key]
                    if key == 'page':
                        prefix = 'https://libre-soc.org/openpower/'
                        v = value.replace("_", "\_") # latex, duh
                        url = '[%s](%s%s)' % (value, prefix, v)
                        r.append(url)
                    elif key == 'rfc' and value.startswith('ls'):
                        prefix = 'https://libre-soc.org/openpower/sv/rfc/'
                        url = '[%s](%s%s)' % (value, prefix, value)
                        r.append(url)
                    else:
                        value = value.replace("_", "\_") # latex, duh
                        n_spaces = col_len - len(value)
                        r.append(value + " " * n_spaces)
                # write row
                write_mdwn_row(f, r)
            f.write("\n\n")

            # approx 8 lines per header
            linecount += 9

if __name__ == '__main__':
    with open("ls012/optable.csv") as f:
        l = map(str.strip, f.readlines())
    areas = {}
    header = None
    for line in l:
        if line.startswith("#"):
            area = line[1:].strip()
            areas[area]= []
            continue
        # split line by commas, whitespace-strip it
        line = list(map(str.strip, line.split(',')))
        # identify header
        if header is None:
            header = line
            continue
        # create a dictionary by tuple of header+line
        linedict = dict(zip(header, line))
        #print (area)
        #print (linedict)
        #print ()
        # store line in area
        areas[area].append(linedict)

    # excellent - now have a dictionary of list of dictionaries:
    # area - list-of-instructions - dictionary-by-heading
    print_table("Areas", header, areas, None)

    # now sort by cost and then by page
    print_table("XO cost", header, areas, by_cost_then_priority_then_page)

    # now sort by level combined with priority, then cost
    print_table("Level", header, areas, by_levelpriority_then_cost)