#!/usr/bin/env python3
# -*- mode: python; coding: utf-8; eval: (blacken-mode); -*-
r"""input-filter-md.py FILENAME
input-filter-md.py --index FILENAMES...

Doxygen .md input filter that adds extended syntax.
With --index, generates an index file.
Assumes BUILD_DIR environment variable is set.

@PAR@ <section> <name> (...)

    Adds an index item and expands to

    \anchor <key>
    \par <name> (...)

@SECREF@ <section>

    Expands to

    \secreflist
    \refitem ...
    ...
    \endsecreflist

    containing all index items from the specified section.

# Section title    @IDX@ <section> [<alternative index name>]

   Adds the section title to the index, and expands to an anchor

   # Section title {#key}

The index keys can be used in \ref and have format

    {section}__{name}

where the parts are converted to lowercase and _ replaces
non-alphanumerics.

"""
import sys
import re
import os


def index_key(section, name):
    key = f"{section}__{name}".lower()
    return re.sub(r"[^A-Za-z0-9_-]", "_", key)


BUILD_DIR = os.environ["BUILD_DIR"]
PAR_RE = r"^@PAR@\s+([^\s]*)[ \t]+(\S+)(.*)$"
IDX_RE = r"^(#+)(.*)@IDX@[ \t]+(\S+)([ \t]+\S+)?[ \t]*$"
SECREF_RE = r"^@SECREF@[ \t]+([^\n]*)[ \t]*$"


def main(args):
    fn = args[0]
    with open(fn, "r") as f:
        text = f.read()

    def par(m):
        section = m.group(1)
        name = m.group(2)
        rest = m.group(3).strip()
        key = index_key(section, name)
        return f"\\anchor {key}\n\\par {name} {rest}"

    def idx(m):
        level = m.group(1)
        title = name = m.group(2).strip()
        section = m.group(3)
        alt = m.group(4)
        if title == title.upper():
            name = name.capitalize()
        key = index_key(section, name)
        text = f"{level} {title} {{#{key}}}"
        if alt and alt.strip():
            alt_key = index_key(section, alt.strip())
            if alt_key != key:
                text += f"\n\\anchor {alt_key}"
        return text

    def secref(m):
        import os
        import json

        secs = m.group(1).split()

        with open(os.path.join(BUILD_DIR, "index.json"), "r") as f:
            index = json.load(f)

        items = {}

        for sec in secs:
            if sec not in index:
                print(f"{fn}: no index '{sec}'", file=sys.stderr)
            else:
                for name, key in index[sec].items():
                    if name in items:
                        pkey, psec = items.pop(name)
                        nname = f"{name} ({sec})"
                        items[nname] = (key, sec)
                        if pkey is not None:
                            pname = f"{name} ({psec})"
                            items[pname] = (pkey, psec)
                            items[name] = (None, None)
                    else:
                        items[name] = (key, sec)

        text = [r"\secreflist"]
        for name, (key, sec) in sorted(items.items()):
            if key is not None:
                text.append(rf'\refitem {key} "{name}"')
        text.append(r"\endsecreflist")
        text = "\n".join(text)
        return f"{text}\n"

    text = re.sub(PAR_RE, par, text, flags=re.M)
    text = re.sub(IDX_RE, idx, text, flags=re.M)
    text = re.sub(SECREF_RE, secref, text, flags=re.M)

    print(text)


def main_index(args):
    import json

    sections = {}

    for fn in set(args):
        with open(fn, "r") as f:
            load_index(sections, f.read())

    result = {}

    for section, items in sections.items():
        for name in items:
            key = index_key(section, name)
            result.setdefault(section, {})[name] = key

    with open(os.path.join(BUILD_DIR, "index.json"), "w") as f:
        json.dump(result, f)


def load_index(sections, text):
    def par(m):
        section = m.group(1)
        name = m.group(2)
        sections.setdefault(section, []).append(name)
        return ""

    def idx(m):
        name = m.group(2).strip()
        section = m.group(3)
        alt = m.group(4)
        if name == name.upper():
            name = name.capitalize()
        sections.setdefault(section, []).append(name)
        if alt and alt.strip():
            sections.setdefault(section, []).append(alt.strip())
        return ""

    text = re.sub(PAR_RE, par, text, flags=re.M)
    text = re.sub(IDX_RE, idx, text, flags=re.M)


if __name__ == "__main__":
    if len(sys.argv) >= 2 and sys.argv[1] == "--index":
        main_index(sys.argv[2:])
    elif len(sys.argv) == 2:
        main(sys.argv[1:])
    else:
        print(__doc__.strip())
        sys.exit(1)
