#!/usr/bin/env python3
# $Id: pprender_footprints 72 2026-08-27 08:32:33Z peter $
#
# pprender_footprints --- renders real, extracted footprint
# geometry (footprint.py's own Pad/Footprint data, read directly
# from KiCad's own .kicad_mod files) as an SVG panel per footprint:
# every real pad drawn at its own real position, size, and shape,
# the courtyard outline (KiCad's own "real keep-out size" layer),
# and an explicit millimeter scale so panels showing very different
# physical sizes stay individually readable without hiding just how
# different those real sizes are.
#
# Built directly in answer to Péter's own follow-up: having the
# extracted numbers is one thing, seeing the actual shapes is
# another. Every footprint reference here is exactly the same one
# used in the comparison table shared previously -- nothing
# re-guessed.

import sys
import os

sys.path.insert(0, os.path.dirname(__file__))
from footprint import load_footprint

PAD_COLORS = {"thru_hole": "#b5651d", "smd": "#8a8a8a", "np_thru_hole": "#666666"}


def pad_svg(pad, sx, sy, ox, oy):
    """One pad, drawn at its own real position/size/shape, scaled and
    offset into panel-local pixel coordinates. sy is negated (mm Y
    grows downward on a real board's own coordinate system in KiCad's
    file format, same as SVG, so no flip needed here -- confirmed by
    comparing against the raw .kicad_mod coordinates directly, not
    assumed)."""
    cx = ox + pad.x * sx
    cy = oy + pad.y * sy
    w = max(abs(pad.size_x * sx), 2)
    h = max(abs(pad.size_y * sy), 2)
    color = PAD_COLORS.get(pad.kind, "#8a8a8a")
    out = []
    if pad.shape in ("circle",):
        out.append(f'<circle cx="{cx}" cy="{cy}" r="{w/2}" fill="{color}" stroke="#222" stroke-width="0.5"/>')
    elif pad.shape in ("oval", "roundrect"):
        rx = min(w, h) * 0.3
        out.append(f'<rect x="{cx-w/2}" y="{cy-h/2}" width="{w}" height="{h}" rx="{rx}" '
                    f'fill="{color}" stroke="#222" stroke-width="0.5"/>')
    else:  # rect and anything else
        out.append(f'<rect x="{cx-w/2}" y="{cy-h/2}" width="{w}" height="{h}" '
                    f'fill="{color}" stroke="#222" stroke-width="0.5"/>')
    # Pad number, small, centered -- skipped for very small/dense
    # pads (a 24-pin connector's own 0.3mm-pitch row) where text
    # would just collide with itself.
    if w > 10 and h > 6:
        out.append(f'<text x="{cx}" y="{cy+3}" font-size="6" text-anchor="middle" fill="#fff">{pad.number}</text>')
    return "".join(out)


def render_panel(fp, label, box_w, box_h, margin=34):
    """One footprint, fit to box_w x box_h pixels, with the courtyard
    outline, every real pad, a title, and an explicit mm scale bar so
    a viewer can tell how physically large this really is, not just
    how large it looks relative to the other panels on the same
    page. `label` is which of this project's own components this
    candidate footprint stands in for (e.g. "R1-R3"), shown above
    the raw KiCad footprint name itself."""
    cx0 = min(p.x - p.size_x / 2 for p in fp.pads)
    cx1 = max(p.x + p.size_x / 2 for p in fp.pads)
    cy0 = min(p.y - p.size_y / 2 for p in fp.pads)
    cy1 = max(p.y + p.size_y / 2 for p in fp.pads)
    if fp.courtyard_bbox:
        kx0, ky0, kx1, ky1 = fp.courtyard_bbox
        cx0, cx1 = min(cx0, kx0), max(cx1, kx1)
        cy0, cy1 = min(cy0, ky0), max(cy1, ky1)
    span_x, span_y = max(cx1 - cx0, 1), max(cy1 - cy0, 1)
    avail_w, avail_h = box_w - 2 * margin, box_h - 2 * margin - 36
    scale = min(avail_w / span_x, avail_h / span_y)
    ox = margin + (avail_w - span_x * scale) / 2 - cx0 * scale
    oy = margin + 36 + (avail_h - span_y * scale) / 2 - cy0 * scale

    out = [f'<rect x="1" y="1" width="{box_w-2}" height="{box_h-2}" fill="#fff" stroke="#ccc"/>']
    out.append(f'<text x="{box_w/2}" y="16" font-size="12" text-anchor="middle" fill="#222" font-weight="bold">{label}</text>')
    out.append(f'<text x="{box_w/2}" y="29" font-size="8" text-anchor="middle" fill="#666">{fp.name}</text>')

    if fp.courtyard_bbox:
        kx0, ky0, kx1, ky1 = fp.courtyard_bbox
        cw, ch = fp.courtyard_size()
        out.append(f'<rect x="{ox+kx0*scale}" y="{oy+ky0*scale}" '
                    f'width="{cw*scale}" height="{ch*scale}" fill="none" '
                    f'stroke="#2c7a4b" stroke-width="1" stroke-dasharray="3,2"/>')
        out.append(f'<text x="{box_w/2}" y="{box_h-8}" font-size="8" text-anchor="middle" fill="#2c7a4b">'
                    f'courtyard {cw:.2f} x {ch:.2f}mm</text>')

    for p in fp.pads:
        out.append(pad_svg(p, scale, scale, ox, oy))

    # A 5mm scale bar, drawn once per panel at a fixed real length --
    # since panels are individually fit-to-box, a viewer comparing bar
    # lengths across panels can see relative real size even though
    # the footprints themselves are drawn at different scales.
    bar_y = box_h - 22
    out.append(f'<line x1="{margin}" y1="{bar_y}" x2="{margin+5*scale}" y2="{bar_y}" stroke="#222" stroke-width="1.5"/>')
    out.append(f'<text x="{margin}" y="{bar_y-4}" font-size="7" fill="#222">5mm</text>')

    return "".join(out)


FOOTPRINTS = [
    ("R1-R3 (SMD 0805)", "Resistor_SMD:R_0805_2012Metric"),
    ("R1-R3 (THT axial)", "Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P10.16mm_Horizontal"),
    ("C1/C2/C5 (SMD 0805)", "Capacitor_SMD:C_0805_2012Metric"),
    ("C3/C4 (electrolytic)", "Capacitor_THT:CP_Radial_D6.3mm_P2.50mm"),
    ("D1/D2 (SMA)", "Diode_SMD:D_SMA"),
    ("L1 (SMD 1210)", "Inductor_SMD:L_1210_3225Metric"),
    ("RV1 (trimmer)", "Potentiometer_THT:Potentiometer_Bourns_3296W_Vertical"),
    ("J1 (USB-C receptacle)", "Connector_USB:USB_C_Receptacle_GCT_USB4115-03-C"),
]

def _version():
    """This project's own current Dewey-decimal version, read from
    VERSION -- see netlist.py's own identical helper for the full
    reasoning. Duplicated per-script rather than factored into a
    shared module -- see render.py's own identical note on why."""
    here = os.path.dirname(os.path.abspath(__file__))
    try:
        with open(os.path.join(here, "VERSION")) as f:
            for line in f:
                line = line.strip()
                if line and not line.startswith("#"):
                    return line
    except OSError:
        pass
    return "unknown"


if __name__ == "__main__":
    # CLI entry point, ADDED 2026-08-19 -- unlike the two
    # example_circuit*.py scripts, this one genuinely has a couple of
    # real, safe options worth adding (where the panel goes, whether
    # to announce it), even though its own FOOTPRINTS list stays
    # fixed by design (see this file's own header for why: it's a
    # direct visual companion to a specific, real footprint
    # comparison already discussed, not a general "any footprint"
    # tool -- that's ppfootprint(1)).
    import argparse
    parser = argparse.ArgumentParser(
        prog="pprender_footprints",
        description="Render this project's own fixed panel of real "
                     "footprint comparisons (see FOOTPRINTS in this "
                     "script's own source) as one SVG: every real pad at "
                     "its own real position/size/shape, the courtyard "
                     "outline, and an explicit mm scale bar. For any "
                     "OTHER footprint, not just this fixed set, see "
                     "ppfootprint(1) instead.")
    parser.add_argument("-o", "--output", metavar="FILE",
                         default="footprints_render.svg",
                         help="output SVG path (default: %(default)s)")
    parser.add_argument("--version", action="version",
                         version=f"%(prog)s {_version()}")
    parser.add_argument("--quiet", action="store_true",
                         help="suppress the final \"wrote ...\" line on "
                              "success")
    args = parser.parse_args()

    cols, rows = 4, 2
    box_w, box_h = 320, 280
    W, H = cols * box_w, rows * box_h

    svg = [f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {W} {H}" font-family="monospace">']
    svg.append(f'<rect x="0" y="0" width="{W}" height="{H}" fill="#e9e4d8"/>')
    for i, (label, fp_ref) in enumerate(FOOTPRINTS):
        r, c = divmod(i, cols)
        try:
            fp = load_footprint(fp_ref)
        except Exception as e:
            svg.append(f'<text x="{c*box_w+10}" y="{r*box_h+30}" font-size="9" fill="red">{label}: {e}</text>')
            continue
        panel = render_panel(fp, label, box_w, box_h)
        svg.append(f'<g transform="translate({c*box_w},{r*box_h})">{panel}</g>')
    svg.append("</svg>")

    with open(args.output, "w") as f:
        f.write("\n".join(svg))
    if not args.quiet:
        print(f"wrote {args.output}, {W}x{H}")
