#!/usr/bin/env python3
# encoding: utf-8
"""
makeTOC.py — generate the alphabetical A–Z index page (toc.html) for the
Glyphs Help book from the `pgs/*.html` topic pages.

Originally created by Georg Seifert on 2010-06-23.
Ported to Python 3 in 2026.

Layout assumed:
    <here>/makeTOC.py
    <here>/pgs/*.html       ← topic pages, each with <title> and <a name="…">
    <here>/toc.html         ← written by this script

For each topic page we read:
    <title>…</title>                                                  → display title
    first <a name="…">                                                → link anchor
If the title contains "–" (en-dash), the script ALSO indexes the part
after the dash separately, so e.g. "Font Info – Features" is reachable
under both "F" (Font Info) and "F" (Features).
"""

import os
import sys
from pathlib import Path
from xml.dom.minidom import parse

SCRIPT_DIR = Path(__file__).resolve().parent
PGS_DIR = SCRIPT_DIR / "pgs"
OUT_FILE = SCRIPT_DIR / "toc.html"
BOOK_ID = "Glyphs Help"  # used in help:anchor=… bookID=<this>

ALPHABET = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")


def collect_pages(pgs_dir: Path) -> dict[str, str]:
    """Walk pgs/*.html and return a dict of {title: anchor-name}.

    For titles containing an en-dash ("–"), the portion after the dash is
    also added as a separate key, so the same page is indexed twice.
    """
    pages: dict[str, str] = {}
    for html in sorted(pgs_dir.iterdir()):
        if html.suffix.lower() != ".html":
            continue
        try:
            dom = parse(str(html))
        except Exception as e:
            print(f"  skip {html.name}: {e}", file=sys.stderr)
            continue

        titles = dom.getElementsByTagName("title")
        anchors = dom.getElementsByTagName("a")
        if not titles or not titles[0].childNodes or not anchors:
            print(f"  skip {html.name}: missing <title> or <a name>", file=sys.stderr)
            continue

        title = titles[0].childNodes[0].data
        name = anchors[0].getAttribute("name")
        if not name:
            # Some pages have leading anchors without a name attribute; fall
            # back to scanning for the first one that actually has it.
            for a in anchors:
                n = a.getAttribute("name")
                if n:
                    name = n
                    break
        if not name:
            print(f"  skip {html.name}: no anchor with a name", file=sys.stderr)
            continue

        pages[title] = name

        # Also index the portion after an en-dash, if present.
        if "–" in title:
            idx = title.find("–") + 1
            alt = title[idx:].strip()
            if alt:
                pages[alt] = name
                print(f"  alias '{alt}' → {name}")

    return pages


def bucket_by_letter(pages: dict[str, str]) -> dict[str, list[str]]:
    toc: dict[str, list[str]] = {}
    for title in pages:
        letter = title[0].upper()
        toc.setdefault(letter, []).append(title)
    return toc


HEADER = '''\
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
\t<title>Glyphs Index</title>
\t<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
\t<meta name="template" content="index"/>
\t<meta name="pagetype" content="index"/>
\t<meta name="robots" content="anchors"/>
\t<link href="sty/index.css" rel="stylesheet" type="text/css"/>
\t<link href="sty/home_os.css" rel="stylesheet" type="text/css"/>
\t<script type="text/javascript" charset="utf-8">
\t\tfunction go(target)
\t\t{{
\t\t\twindow.location = target;
\t\t\twindow.scrollBy(0,-60);
\t\t}}
\t</script>
</head>

<body>
\t<!--top navigation area-->
\t<div id="navbox" class="gradient">
\t\t<a name="toc"></a>
\t\t<div id="navleftbox">
\t\t\t<a class="navlink_left" href="help:anchor='access' bookID={book}">Start</a>
\t\t</div>
\t</div>
\t<!--closes navigation area-->
\t<!--alphabet area-->
\t<div id="alpharow">
\t\t<div id="alphabox">
'''

FOOTER = '''\
\t\t<br style="clear:both;" />
\t</div>
\t<!--closes list area-->
</body>
</html>
'''


def render(pages: dict[str, str], toc: dict[str, list[str]]) -> str:
    parts: list[str] = []
    parts.append(HEADER.format(book=BOOK_ID))

    # Alphabet row: linked letters get an <a>, missing letters render flat.
    for letter in ALPHABET:
        if letter in toc:
            parts.append(
                f'\t\t\t<span class="alphaletters">'
                f'<a href="javascript:go(\'#{letter}\');">{letter}</a></span>\n'
            )
        else:
            parts.append(f'\t\t\t<span class="alphaletters">{letter}</span>\n')

    parts.append('\t\t</div>\n\t</div>\n')
    parts.append('\t<!-- closes alphabet area --><!--list area-->\n')
    parts.append('\t<div id="indexlist">\n')

    for letter in ALPHABET:
        if letter not in toc:
            continue
        parts.append(
            f'\t\t<div class="letterhead"><a name="{letter}"></a>{letter}</div>\n'
        )
        for title in sorted(toc[letter]):
            anchor = pages[title]
            parts.append(
                '\t\t<div class="indexitem">\n'
                '\t\t\t<div class="indexentrytext">\n'
                f'\t\t\t\t<a href="help:anchor={anchor} bookID={BOOK_ID}">{title}</a>\n'
                '\t\t\t</div>\n'
                '\t\t</div>\n'
            )

    parts.append(FOOTER)
    return "".join(parts)


def main() -> int:
    if not PGS_DIR.is_dir():
        print(f"✗ no pgs/ folder next to this script (expected {PGS_DIR})", file=sys.stderr)
        return 1

    print(f"→ scanning {PGS_DIR}")
    pages = collect_pages(PGS_DIR)
    print(f"  collected {len(pages)} titles (including aliases)")

    toc = bucket_by_letter(pages)
    print(f"  buckets: {', '.join(f'{k}({len(v)})' for k, v in sorted(toc.items()))}")

    html = render(pages, toc)
    OUT_FILE.write_text(html, encoding="utf-8")
    print(f"✓ wrote {OUT_FILE}")
    return 0


if __name__ == "__main__":
    sys.exit(main())
