# SPDX-License-Identifier: MIT
#
# Copyright (c) 2020 Philippe Proulx <pproulx@efficios.com>
#
# This file is a Sphinx extension which adds the following roles:
#
# `bt2man`:
#     A typical manual page reference, like `grep(1)`.
#
#     Example:
#
#         :bt2man:`grep(1)`
#
#     This role creates a simple inline literal node with the role's
#     text if it's not a Babeltrace 2 manual page reference, or an
#     external link to the corresponding online manual page (on
#     `babeltrace.org`) with the appropriate project's version
#     (`version` configuration entry) otherwise.
#
# `bt2link`:
#     An external link with an URL in which a specific placeholder is
#     replaced with the project's version.
#
#     The role's text follows the typical external link format, for
#     example:
#
#         Link text <https://example.com/>
#
#     Any `@ver@` in the URL is replaced with the project's version
#     (`version` configuration entry).
#
#     Example:
#
#         :bt2link:`libbabeltrace2 <https://babeltrace.org/docs/v@ver@/libbabeltrace2/>`

import re
import functools

import docutils
import docutils.nodes
import docutils.utils


def _bt2man_role(
    bt2_version, name, rawtext, text, lineno, inliner, options=None, content=None
):
    # match a manual page reference
    m = re.match(r"^([a-zA-Z0-9_.:-]+)\(([a-zA-Z0-9]+)\)$", text)

    if not m:
        msg = "Cannot parse manual page reference `{}`".format(text)
        inliner.reporter.severe(msg, line=lineno)
        return [inliner.problematic(rawtext, rawtext, msg)], [msg]

    # matched manual page and volume
    page = m.group(1)
    vol = m.group(2)

    # create nodes: `ret_node` is the node to return
    page_node = docutils.nodes.strong(rawtext, page)
    vol_node = docutils.nodes.inline(rawtext, "({})".format(vol))
    man_node = docutils.nodes.inline(rawtext, "", page_node, vol_node)
    ret_node = docutils.nodes.literal(rawtext, "", man_node)

    if page.startswith("babeltrace2"):
        # Babeltrace 2 manual page: wrap `ret_node` with an external
        # link node
        url_tmpl = "https://babeltrace.org/docs/v{ver}/man{vol}/{page}.{vol}/"
        url = url_tmpl.format(ver=bt2_version, vol=vol, page=page)
        ret_node = docutils.nodes.reference(
            rawtext, "", ret_node, internal=False, refuri=url
        )

    return [ret_node], []


def _bt2link_role(
    bt2_version, name, rawtext, text, lineno, inliner, options=None, content=None
):
    # match link text and URL
    m = re.match(r"^([^<]+) <([^>]+)>$", text)

    if not m:
        msg = "Cannot parse link template `{}`".format(text)
        inliner.reporter.severe(msg, line=lineno)
        return [inliner.problematic(rawtext, rawtext, msg)], [msg]

    link_text = m.group(1)

    # replace `@ver@` with the project's version
    url = m.group(2).replace("@ver@", bt2_version)

    # create and return an external link node
    node = docutils.nodes.reference(rawtext, link_text, internal=False, refuri=url)
    return [node], []


def _add_roles(app):
    # add the extension's roles; the role functions above expect the
    # project's version as their first parameter
    app.add_role("bt2man", functools.partial(_bt2man_role, app.config.version))
    app.add_role("bt2link", functools.partial(_bt2link_role, app.config.version))


def setup(app):
    app.connect("builder-inited", _add_roles)
    return {
        "version": app.config.version,
        "parallel_read_safe": True,
    }
