# 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 .ip_block import IpBlock
from .html_helpers import render_td
from .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")

    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")
