#!/usr/bin/env python3
"""Writes docs/images/glyphs.svg from ui/js/Icons.js, one tile per entry in its PATHS table.

    tools/flea-glyph-sheet                 rewrites docs/images/glyphs.svg
    tools/flea-glyph-sheet /tmp/out.svg    writes somewhere else, for a diff

The sheet's own comment has claimed since it was committed that it is generated from Icons.js, and
nothing generated it: `send` stayed on the sheet after it left the table and `drive`, `filter` and
`lock` never reached it. This is that generator, so the claim is true and the sheet cannot go stale.
"""

import json
import os
import re
import sys

# The sheet's own palette. A static file cannot read the live Omarchy theme, so these are the
# sheet's colours only; in the app every mark takes a palette role and never a hex.
GROUND = "#16161e"
PANEL = "#1a1b26"
LINE = "#262b40"
INK = "#c0caf5"
DIM = "#565f89"
BRAND = "#9ece6a"
FONT = "ui-monospace,SFMono-Regular,Menlo,Consolas,monospace"

# The brand spiral is the header's mark and is deliberately not in PATHS: rows and menus never
# draw it, so ui/Spinner.qml and ui/FleaMark.qml own it instead of the glyph table.
SPIRAL = "M21 21H3V3h18v14H7V7h10v6h-6"

# Tile field geometry, unchanged from the sheet this replaces.
COLS = 8
TILE_W, TILE_H = 96, 86
PITCH_X, PITCH_Y = 97, 87
FIELD_X, FIELD_Y = 16, 108
MARK = 32
GRID = 24
MARGIN = 15
FOOTER = 37
HEADER_H = 80


def load_paths(src):
    """Return the PATHS table as a dict, by parsing the block as JSON rather than by counting lines.

    Sample input, the block this reads:
        var PATHS = {
            "file": "M4 22V2h10l6 6v14H4z M14 2v6h6",
            // one comment line
            "folder": "M2 20V3h6l2 3h12v14H2z"
        }
    """
    text = open(src, encoding="utf-8").read()
    start = text.index("var PATHS = {") + len("var PATHS = ")
    depth, end = 0, None
    for i in range(start, len(text)):
        if text[i] == "{":
            depth += 1
        elif text[i] == "}":
            depth -= 1
            if depth == 0:
                end = i + 1
                break
    if end is None:
        sys.exit("flea-glyph-sheet: PATHS in %s never closes" % src)
    block = text[start:end]
    # No SVG path data contains "//", so a whole-line comment strip cannot eat a "d" string; if one
    # ever did, json.loads below fails loudly rather than dropping a mark from the sheet in silence.
    lines = [l for l in block.split("\n") if not l.strip().startswith("//")]
    block = "\n".join(lines)
    block = re.sub(r",(\s*})", r"\1", block)
    return json.loads(block)


def tile(x, y, name, d):
    """One tile: its panel, the mark centred in the top of it, and the name under it."""
    mx = x + (TILE_W - MARK) / 2
    my = y + 18
    scale = MARK / GRID
    out = ['  <rect x="%d" y="%d" width="%d" height="%d" fill="%s"/>' % (x, y, TILE_W, TILE_H, PANEL)]
    if d is not None:
        out.append(
            '  <g transform="translate(%.1f,%.1f) scale(%.4f)" fill="none" stroke="%s" '
            'stroke-width="2" stroke-linecap="square" stroke-linejoin="miter"><path d="%s"/></g>'
            % (mx, my, scale, INK, d)
        )
        out.append(
            '  <text x="%.1f" y="%d" fill="%s" font-family="%s" font-size="9" '
            'text-anchor="middle">%s</text>' % (x + TILE_W / 2, y + 72, DIM, FONT, name)
        )
    return out


def render(paths):
    names = list(paths.keys())
    rows = (len(names) + COLS - 1) // COLS
    field_w = COLS * PITCH_X + 1
    field_h = rows * PITCH_Y + 1
    width = MARGIN + field_w + MARGIN
    height = FIELD_Y - 1 + field_h + FOOTER

    out = [
        '<svg xmlns="http://www.w3.org/2000/svg" width="%d" height="%d" viewBox="0 0 %d %d" '
        'role="img" aria-label="Flea glyph specimen, the Omarchy cut">' % (width, height, width, height),
        "  <!-- Generated by tools/flea-glyph-sheet from ui/js/Icons.js: one tile per entry in that",
        "       file's PATHS table, drawn at the app's own stroke of 1.5 on lucide's 24 unit grid",
        "       with square caps and mitered joins. Do not hand-edit; re-run the generator. A static",
        "       file cannot read the live palette, so these colours are the sheet's own; in the app",
        "       every mark takes a palette role and never a hex. -->",
        '  <rect width="%d" height="%d" fill="%s"/>' % (width, height, GROUND),
        '  <rect x="16" y="16" width="%d" height="%d" fill="%s" stroke="%s"/>'
        % (width - 32, HEADER_H, PANEL, LINE),
        '  <g transform="translate(34.0,34.0) scale(%.4f)" fill="none" stroke="%s" stroke-width="1.5" '
        'stroke-linecap="square" stroke-linejoin="miter"><path d="%s"/></g>' % (44 / GRID, BRAND, SPIRAL),
        '  <text x="98" y="46" fill="%s" font-family="%s" font-size="13" font-weight="700">'
        "The Omarchy cut</text>" % (INK, FONT),
        '  <text x="98" y="64" fill="%s" font-family="%s" font-size="11">'
        "Lucide's 24 unit grid and metrics, square caps, mitered joins, no rounded corner anywhere.</text>"
        % (DIM, FONT),
        '  <text x="98" y="80" fill="%s" font-family="%s" font-size="11">'
        "The spiral at left is the brand mark: empty state, loading crawl, transfers. Rows never draw it.</text>"
        % (DIM, FONT),
        '  <rect x="%d" y="%d" width="%d" height="%d" fill="%s"/>'
        % (MARGIN, FIELD_Y - 1, field_w, field_h, LINE),
    ]
    for i in range(rows * COLS):
        x = FIELD_X + (i % COLS) * PITCH_X
        y = FIELD_Y + (i // COLS) * PITCH_Y
        name = names[i] if i < len(names) else None
        out += tile(x, y, name, paths[name] if name else None)
    out.append(
        '  <text x="16" y="%d" fill="%s" font-family="%s" font-size="10">%d marks ship today, one per '
        "entry in ui/js/Icons.js. Base geometry from lucide 1.38.0 (ISC), recut.</text>"
        % (height - 14, DIM, FONT, len(names))
    )
    out.append("</svg>")
    return "\n".join(out) + "\n"


def main():
    root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    src = os.path.join(root, "ui/js/Icons.js")
    out = sys.argv[1] if len(sys.argv) > 1 else os.path.join(root, "docs/images/glyphs.svg")
    if not os.path.isfile(src):
        sys.exit("flea-glyph-sheet: %s is missing" % src)
    paths = load_paths(src)
    if not paths:
        sys.exit("flea-glyph-sheet: PATHS parsed to nothing, which is never right")
    open(out, "w", encoding="utf-8").write(render(paths))
    print("flea-glyph-sheet: %d marks written to %s" % (len(paths), out))


main()
