#!/usr/bin/env python3
# $Id: ppextract_sources 194 2026-10-12 11:57:48Z peter $
#
# ppextract_sources --- write a ppresolve_components sources file from a KiCad schematic's
# own EMBEDDED symbols.
#
# ADDED 2026-10-07, the follow-up Péter asked for after the camera-trap loop showed that
# the instructions route has no default of reading a design's embedded symbols, as
# ppkicad_layout does. A schematic carries every symbol it uses, exactly as its author drew
# it, and needs no library installed anywhere: for a design that predates a library rename
# (camera-trap's Conn_01x03_Male, no longer in KiCad 10's Connector library) the embedded
# symbol is the ONLY one that exists. This command writes the embedded libraries to a
# directory and a sources file whose sym_path entries point at them -- what the loop test
# had to build by hand. The pin_map is left empty, for the user: only they know which pin
# is which (the instructions give holes, not pins).

import os
import subprocess
import sys


def _version():
    """This project's own current Dewey-decimal version (see ppnetlist's own identical
    helper for the fuller reasoning)."""
    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"


def _fail(message):
    print(f"ppextract_sources: {message}", file=sys.stderr)
    raise SystemExit(1)


def main(argv=None):
    import argparse
    import json

    parser = argparse.ArgumentParser(
        prog="ppextract_sources",
        description="Write a ppresolve_components sources file from a KiCad schematic's own "
                    "embedded symbols: the libraries go to a directory, every part gets an "
                    "entry whose sym_path points at them (else at an installed library, "
                    "$KICAD_SYM_DIR). The pin_map is left empty, for you to fill.")
    parser.add_argument("schematic", metavar="SCHEMATIC",
                        help="a .kicad_sch file (a netlist carries no symbols)")
    parser.add_argument("-o", "--output", metavar="FILE", default=None,
                        help="the sources file to write (default: STEM-sources.json)")
    parser.add_argument("--symbol-dir", metavar="DIR", default=None,
                        help="where to write the embedded libraries (default: STEM-symbols, in the directory "
                             "of the sources file)")
    parser.add_argument("--kicad-cli", metavar="CMD",
                        default=os.environ.get("KICAD_CLI", "kicad-cli"),
                        help="kicad-cli command (default: $KICAD_CLI if set, else 'kicad-cli')")
    parser.add_argument("--quiet", action="store_true", help="suppress informational messages")
    parser.add_argument("--version", action="version", version=f"%(prog)s {_version()}")
    args = parser.parse_args(argv)

    path = args.schematic
    if not path.endswith(".kicad_sch"):
        _fail(f"{path}: not a .kicad_sch file -- only a schematic carries its symbols "
              f"(a netlist does not)")
    if not os.path.isfile(path):
        _fail(f"{path}: no such file")
    stem = os.path.splitext(os.path.basename(path))[0]
    out = args.output or f"{stem}-sources.json"
    outdir = os.path.dirname(os.path.abspath(out))
    if not os.path.isdir(outdir):
        _fail(f"{out}: the directory {outdir} does not exist (nothing was written)")
    # The default symbol directory sits NEXT TO THE SOURCES FILE, not in the current directory (0.72.59). It used to be resolved against the
    # working directory, so `-o /elsewhere/sources.json` still left STEM-symbols/ wherever the tool was run: in the source tree, and from there
    # into an archive (audio-jack-symbols/, 0.72.54).
    symdir = os.path.abspath(args.symbol_dir) if args.symbol_dir else os.path.join(outdir, f"{stem}-symbols")

    from netlist import load_from_schematic
    from resolve_components import embedded_symbol_libs, sources_from_schematic
    try:
        nl = load_from_schematic(path, kicad_cli=args.kicad_cli)
    except OSError as e:
        _fail(f"cannot run {args.kicad_cli!r} ({e.strerror or e}) -- set --kicad-cli or $KICAD_CLI")
    except subprocess.CalledProcessError as e:
        _fail(f"kicad-cli could not read {path}: {(e.stderr or '').strip()[-200:] or e}")
    embedded = embedded_symbol_libs(path, symdir)
    sources, problems = sources_from_schematic(nl, embedded, os.environ.get("KICAD_SYM_DIR"))
    with open(out, "w") as f:
        json.dump({"sources": sources, "pin_map": {}}, f, indent=2)
        f.write("\n")
    if not args.quiet:
        print(f"wrote {out} ({len(sources)} of {len(nl.components)} parts)", file=sys.stderr)
        print(f"wrote {len(embedded)} embedded librar{'y' if len(embedded) == 1 else 'ies'} "
              f"to {symdir}", file=sys.stderr)
    for problem in problems:
        print(f"ppextract_sources: warning: {problem}", file=sys.stderr)


if __name__ == "__main__":
    main()
