#!/usr/bin/env python3
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
r"""Top Module Documentation Generator
"""
import os
from tabulate import tabulate

TABLE_HEADER = '''<!--
DO NOT EDIT THIS FILE DIRECTLY.
It has been generated with the following command:
'''


def set_md_table_font_size(row, size):
    """Wrap each row element with HTML tags and specify font size"""
    for k, elem in enumerate(row):
        row[k] = '<p style="font-size:' + str(size) + '">' + elem + '</p>'
    return row


def create_pinout_table(top, c_helper, target):
    """Create the pinout table for a specific target and extract some stats"""
    header = [
        "Pad Name", "Type", "Bank", "Connection", "Special Function",
        "Pinmux Insel Constant / Muxed Output Index", "Description"
    ]
    table_rows = [set_md_table_font_size(header, "smaller")]
    colalign = ("center", ) * len(header)

    # Pad stats
    stats = {}
    stats['muxed'] = 0
    stats['direct'] = 0
    stats['manual'] = 0

    # get all pads for this target
    pads = top['pinout']['pads'] + target['pinout']['add_pads']
    remove_ports = target['pinout']['remove_ports']
    remove_pads = target['pinout']['remove_pads']
    special_signals = target['pinmux']['special_signals']

    # map pad names to special function signals
    special_pads = {}
    for sig in special_signals:
        special_pads.update({sig['pad']: {'name': sig['name'],
                                          'desc': sig['desc']}})

    i = 2  # insel enum starts at 2
    j = 0  # mio_out enum starts at 0
    for pad in pads:
        # get the corresponding insel constant and MIO output index
        if pad['connection'] == 'muxed':
            name, _, _ = c_helper.pinmux_insel.constants[i]
            insel = name.as_c_enum()
            name, _, _ = c_helper.pinmux_mio_out.constants[j]
            mio_out = name.as_c_enum()
            i += 1
            j += 1
        else:
            insel = "-"
            mio_out = "-"
        # check whether this pad/port needs to be dropped
        if pad['name'] in remove_ports:
            continue
        if pad['name'] in remove_pads:
            continue
        # gather some stats
        stats[pad['connection']] += 1
        # annotate special functions
        if pad['name'] in special_pads:
            special_func = special_pads[pad['name']]['name']
            desc = pad['desc'] + " / " + special_pads[pad['name']]['desc']
        else:
            special_func = "-"
            desc = pad['desc']
        row = [
            pad['name'],
            pad['type'],
            pad['bank'],
            pad['connection'],
            special_func,
            insel + ' / ' + mio_out,
            desc
        ]
        table_rows.append(set_md_table_font_size(row, "smaller"))

    ret_str = tabulate(table_rows,
                       headers="firstrow",
                       tablefmt="pipe",
                       colalign=colalign)

    return ret_str, stats


def create_pinmux_table(top, c_helper):
    """Create the pinmux connectivity table"""
    header = [
        "Module / Signal", "Connection", "Pad",
        "Pinmux Outsel Constant / Peripheral Input Index", "Description"
    ]
    table_rows = [set_md_table_font_size(header, "smaller")]
    colalign = ("center", ) * len(header)

    i = 3  # outsel enum starts at 3
    j = 0  # periph_input enum starts at 0
    for sig in top['pinmux']['ios']:
        port = sig['name']
        if sig['width'] > 1:
            port += '[' + str(sig['idx']) + ']'
        pad = sig['pad'] if sig['pad'] else '-'
        # get the corresponding insel constant
        if sig['connection'] == 'muxed' and sig['type'] in ['inout', 'output']:
            name, _, _ = c_helper.pinmux_outsel.constants[i]
            outsel = name.as_c_enum()
            i += 1
        else:
            outsel = "-"
        # get the corresponding peripheral input index
        if sig['connection'] == 'muxed' and sig['type'] in ['inout', 'input']:
            name, _, _ = c_helper.pinmux_peripheral_in.constants[j]
            periph_in = name.as_c_enum()
            j += 1
        else:
            periph_in = "-"
        row = [
            port,
            sig['connection'],
            pad,
            outsel + " / " + periph_in,
            sig['desc']
        ]
        table_rows.append(set_md_table_font_size(row, "smaller"))

    return tabulate(table_rows,
                    headers="firstrow",
                    tablefmt="pipe",
                    colalign=colalign)


def gen_pinmux_docs(top, c_helper, out_path):
    """Generate target summary table and linked subtables"""

    pinmux_path = out_path / '../ip/pinmux/doc'
    doc_path = out_path / 'ip/pinmux/doc/autogen'
    doc_path.mkdir(parents=True, exist_ok=True)

    # this is used to create relative hyperlinks from the summary table to
    # the individual target tables.
    relpath_prefix = os.path.relpath(doc_path.resolve(), pinmux_path.resolve())

    topname = top['name']
    gencmd = ("util/topgen.py -t hw/top_{topname}/data/top_{topname}.hjson "
              "-o hw/top_{topname}/\n\n".format(topname=topname))

    header = [
        "Target Name", "#IO Banks", "#Muxed Pads", "#Direct Pads",
        "#Manual Pads", "#Total Pads", "Pinout / Pinmux Tables"
    ]
    table_rows = [header]
    colalign = ("center", ) * len(header)

    for target in top['targets']:

        # create pinout/pinmux tables for this target
        pinout_table = '---\n'
        pinout_table += 'title: ' + target['name'].upper()
        pinout_table += ' Target Pinout and Pinmux Connectivity\n'
        pinout_table += '---\n'
        pinout_table += TABLE_HEADER + gencmd + "-->\n\n"
        pinout_table += '## Pinout Table\n\n'
        table_str, stats = create_pinout_table(top, c_helper, target)
        pinout_table += table_str + '\n'
        pinout_table += '## Pinmux Connectivity\n\n'
        pinout_table += create_pinmux_table(top, c_helper)
        pinout_table += "\n"

        pinout_table_path = doc_path / ("pinout_" + target['name'] + ".md")
        with open(pinout_table_path, 'w') as outfile:
            outfile.write(pinout_table)

        # gather some statistics
        num_banks = len(top['pinout']['banks'])
        # create summary table entry
        pinout_table_relpath = relpath_prefix + "/pinout_" + target['name'] + "/index.html"
        row = [
            target['name'].upper(),
            num_banks,
            stats['muxed'],
            stats['direct'],
            stats['manual'],
            stats['muxed'] + stats['direct'] + stats['manual'],
            "[Pinout Table](" + str(pinout_table_relpath) + ")"
        ]
        table_rows.append(row)

    summary_table = tabulate(table_rows,
                             headers="firstrow",
                             tablefmt="pipe",
                             colalign=colalign)
    summary_table = TABLE_HEADER + gencmd + "-->\n\n" + summary_table + "\n"

    target_table_path = doc_path / "targets.md"
    with open(target_table_path, 'w') as target_outfile:
        target_outfile.write(summary_table)


def gen_top_docs(top, c_helper, out_path):
    # create pinout / pinmux specific tables for all targets
    gen_pinmux_docs(top, c_helper, out_path)
