# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
"""
Generate HTML documentation from Block
"""

from typing import TextIO

from reggen.ip_block import IpBlock
from reggen.html_helpers import render_td
from reggen.signal import Signal


def genout(outfile: TextIO, msg: str) -> None:
    outfile.write(msg)


def name_width(x: Signal) -> str:
    if x.bits.width() == 1:
        return x.name

    return '{}[{}:0]'.format(x.name, x.bits.msb)


def gen_kv(outfile: TextIO, key: str, value: str) -> None:
    genout(outfile,
           '<p><i>{}:</i> {}</p>\n'.format(key, value))


def gen_cfg_html(cfgs: IpBlock, outfile: TextIO) -> None:
    rnames = cfgs.get_rnames()

    ot_server = 'https://docs.opentitan.org'
    comport_url = ot_server + '/doc/rm/comportability_specification'
    genout(outfile,
           '<p>Referring to the <a href="{url}">Comportable guideline for '
           'peripheral device functionality</a>, the module '
           '<b><code>{mod_name}</code></b> has the following hardware '
           'interfaces defined.</p>\n'
           .format(url=comport_url, mod_name=cfgs.name))

    # clocks
    gen_kv(outfile,
           'Primary Clock',
           '<b><code>{}</code></b>'.format(cfgs.clocking.primary.clock))
    other_clocks = cfgs.clocking.other_clocks()
    if other_clocks:
        other_clocks_str = ['<b><code>{}</code></b>'.format(clk)
                            for clk in other_clocks]
        gen_kv(outfile, 'Other Clocks', ', '.join(other_clocks_str))
    else:
        gen_kv(outfile, 'Other Clocks', '<i>none</i>')

    # bus interfaces
    dev_ports = ['<b><code>{}</code></b>'.format(port)
                 for port in cfgs.bus_interfaces.get_port_names(False, True)]
    assert dev_ports
    gen_kv(outfile, 'Bus Device Interfaces (TL-UL)', ', '.join(dev_ports))

    host_ports = ['<b><code>{}</code></b>'.format(port)
                  for port in cfgs.bus_interfaces.get_port_names(True, False)]
    if host_ports:
        gen_kv(outfile, 'Bus Host Interfaces (TL-UL)', ', '.join(host_ports))
    else:
        gen_kv(outfile, 'Bus Host Interfaces (TL-UL)', '<i>none</i>')

    # IO
    ios = ([('input', x) for x in cfgs.xputs[1]] +
           [('output', x) for x in cfgs.xputs[2]] +
           [('inout', x) for x in cfgs.xputs[0]])
    if ios:
        genout(outfile, "<p><i>Peripheral Pins for Chip IO:</i></p>\n")
        genout(
            outfile, "<table class=\"cfgtable\"><tr>" +
            "<th>Pin name</th><th>direction</th>" +
            "<th>Description</th></tr>\n")
        for direction, x in ios:
            genout(outfile,
                   '<tr><td>{}</td><td>{}</td>{}</tr>'
                   .format(name_width(x),
                           direction,
                           render_td(x.desc, rnames, None)))
        genout(outfile, "</table>\n")
    else:
        genout(outfile, "<p><i>Peripheral Pins for Chip IO: none</i></p>\n")

    # Inter-Module Signals
    if not cfgs.inter_signals:
        genout(outfile, "<p><em>Inter-Module Signals: none</em></p>\n")
    else:
        genout(outfile, "<p><em>Inter-Module Signals:</em> <a href=\"/doc/rm/comportability_specification/#inter-signal-handling\">Reference</a></p>\n")

        genout(outfile,
               "<table class=\"cfgtable\">\n" +
               "  <caption>Inter-Module Signals</caption>\n" +
               "  <thead>\n" +
               "    <tr>\n" +
               "      <th>Port Name</th>\n" +
               "      <th>Package::Struct</th>\n" +
               "      <th>Type</th>\n" +
               "      <th>Act</th>\n" +
               "      <th>Width</th>\n" +
               "      <th>Description</th>\n" +
               "    </tr>\n" +
               "  </thead>\n" +
               "  <tbody>\n")

        for ims in cfgs.inter_signals:
            name = ims.name
            pkg_struct = ims.package + "::" + ims.struct if ims.package is not None else ims.struct
            sig_type = ims.signal_type
            act = ims.act
            width = str(ims.width) if ims.width is not None else "1"
            desc = ims.desc if ims.desc is not None else ""
            genout(outfile,
                   "    <tr>\n" +
                   "      <td>" + name + "</td>\n" +
                   "      <td>" + pkg_struct + "</td>\n" +
                   "      <td>" + sig_type+ "</td>\n" +
                   "      <td>" + act + "</td>\n" +
                   "      <td>" + width + "</td>\n" +
                   "      <td>" + desc + "</td>\n" +
                   "    </tr>\n")
            continue

        genout(outfile,
               "  </tbody>\n" +
               "</table>\n")

    if not cfgs.interrupts:
        genout(outfile, "<p><i>Interrupts: none</i></p>\n")
    else:
        genout(outfile, "<p><i>Interrupts:</i></p>\n")
        genout(
            outfile, "<table class=\"cfgtable\"><tr><th>Interrupt Name</th>" +
            "<th>Description</th></tr>\n")
        for x in cfgs.interrupts:
            genout(outfile,
                   '<tr><td>{}</td>{}</tr>'
                   .format(name_width(x),
                           render_td(x.desc, rnames, None)))
        genout(outfile, "</table>\n")

    if not cfgs.alerts:
        genout(outfile, "<p><i>Security Alerts: none</i></p>\n")
    else:
        genout(outfile, "<p><i>Security Alerts:</i></p>\n")
        genout(
            outfile, "<table class=\"cfgtable\"><tr><th>Alert Name</th>" +
            "<th>Description</th></tr>\n")
        for x in cfgs.alerts:
            genout(outfile,
                   '<tr><td>{}</td>{}</tr>'
                   .format(x.name,
                           render_td(x.desc, rnames, None)))
        genout(outfile, "</table>\n")

    if not cfgs.countermeasures:
        genout(outfile, "<p><i>Security Countermeasures: none</i></p>\n")
    else:
        genout(outfile, "<p><i>Security Countermeasures:</i></p>\n")
        genout(
            outfile, "<table class=\"cfgtable\"><tr><th>Countermeasure ID</th>" +
            "<th>Description</th></tr>\n")
        for cm in cfgs.countermeasures:
            genout(outfile,
                   '<tr><td>{}</td>{}</tr>'
                   .format(cfgs.name.upper() + '.' + str(cm),
                           render_td(cm.desc, rnames, None)))
        genout(outfile, "</table>\n")
