#!/usr/bin/env python3
# $Id: ppexample_circuit2 144 2026-10-01 08:42:19Z peter $
#
# ppexample_circuit2 --- this project's own SECOND
# self-test example circuit, deliberately shaped as close to the
# OPPOSITE of the first one (ppexample_circuit1, which
# stresses the tight, deep, few-gaps-many-flags extreme): THIS one is
# flat (every passive attaches directly to an anchor, zero passive-
# to-passive chains at all) and comfortable (2 PWR_FLAG nets against
# 3 real gaps -- more room than needed, not a tight squeeze). The
# real, concrete question this answers: does the algorithm's own
# "fast path" -- succeeding at the shared module's own tuned A4
# defaults, zero retries -- generalize to a genuinely different
# circuit's own shallow topology, or was that fast path itself
# somehow specific to whichever circuit it was originally tuned
# against?
#
# EXTRACTED 2026-08-14 as part of packpay's own creation (a surgical
# extraction from the dudhya project, where this circuit originally
# found a real bug -- see packpay's own ResearchJournal.org for the
# full account of what it found and how it was fixed).
#
# Reuses real, already-verified PINS geometry (example_pins.py, kept
# self-contained in this project) for U1 (STUSB4500QTR), U2 (XL4015,
# used TWICE under different refs -- a genuinely plausible real
# design, a dual-rail supply built from two of the same buck
# converter module), and J1 (the USB-C receptacle) -- four real
# anchors, a wider row than the first example circuit's own two.
# Every passive attaches directly to one of these four, nothing
# chains through another passive.

import sys
import os

sys.path.insert(0, os.path.dirname(__file__))
from netlist import Netlist, Component, Net  # noqa: E402
from kicad_gen import write_project_file  # noqa: E402
from auto_layout import classify, high_degree_nets, order_anchors_by_connectivity  # noqa: E402
from kicad_layout import generate_schematic_auto  # noqa: E402
from example_pins import PINS  # noqa: E402 -- real, verified symbol pin geometry,
                                # kept self-contained in this project (see
                                # example_pins.py's own header) rather than
                                # depending on any downstream project's own data.

_SYM_DIR = os.environ.get("KICAD_SYM_DIR", "/usr/share/kicad/symbols")


def build_netlist():
    components = {
        "U1": Component(ref="U1", value="STUSB4500QTR", lib="Interface_USB", part="STUSB4500QTR"),
        "J1": Component(ref="J1", value="USB-C (PD source)", lib="Connector",
                         part="USB_C_Receptacle_PowerOnly_6P"),
        "U2": Component(ref="U2", value="XL4015", lib="Regulator_Switching", part="XL4015"),
        "U3": Component(ref="U3", value="XL4015 (2nd rail)", lib="Regulator_Switching", part="XL4015"),
        "R10": Component(ref="R10", value="4.7k", lib="Device", part="R"),
        "R20": Component(ref="R20", value="10k", lib="Device", part="R"),
        "C10": Component(ref="C10", value="1uF", lib="Device", part="C"),
        "C20": Component(ref="C20", value="10uF", lib="Device", part="C"),
        "C30": Component(ref="C30", value="1uF", lib="Device", part="C"),
        "L10": Component(ref="L10", value="10uH", lib="Device", part="L"),
        "D10": Component(ref="D10", value="1N5819", lib="Device", part="D_Schottky"),
    }

    pin_names, pin_types = {}, {}

    def node(ref, pin, name, typ):
        pin_names[(ref, pin)] = name
        pin_types[(ref, pin)] = typ
        return (ref, pin)

    nets = [
        # Two PWR_FLAG-needing nets against three real gaps (4 anchors)
        # -- comfortable, not a tight squeeze; the opposite shape from
        # the first synthetic test circuit's own 3-flags-1-gap case.
        Net(name="PWRA", code="1", nodes=[
            node("U1", "24", "VDD", "power_in"),
        ]),
        Net(name="GNDX", code="2", nodes=[
            node("U1", "10", "GND", "power_in"),
            node("U3", "1", "GND", "power_in"),
            node("U2", "1", "GND", "power_in"),
            node("J1", "A12", "GND_A12", "passive"),
        ]),
        # Every passive below attaches DIRECTLY to one anchor -- no
        # passive-to-passive chain anywhere in this circuit, the
        # genuinely different shape from the first synthetic test
        # circuit's own three-level chain.
        Net(name="REGOUT1", code="3", nodes=[
            node("U1", "21", "VREG_1V2", "power_out"),
            node("R10", "1", "1", "passive"),
        ]),
        Net(name="REGOUT2", code="4", nodes=[
            node("U1", "23", "VREG_2V7", "power_out"),
            node("C10", "1", "1", "passive"),
        ]),
        Net(name="VSYSNET", code="5", nodes=[
            node("U1", "22", "VSYS", "power_in"),
            node("C30", "1", "1", "passive"),
        ]),
        Net(name="VBUSIN", code="6", nodes=[
            node("J1", "A9", "VBUS_A9", "passive"),
            node("R20", "1", "1", "passive"),
        ]),
        Net(name="CC1NET", code="7", nodes=[
            node("J1", "A5", "CC1", "passive"),
            node("D10", "A", "A", "passive"),
        ]),
        Net(name="U2OUT", code="8", nodes=[
            node("U2", "3", "SW", "power_out"),
            node("L10", "1", "1", "passive"),
        ]),
        Net(name="U2IN", code="9", nodes=[
            node("U2", "5", "VIN", "power_in"),
            node("C20", "1", "1", "passive"),
        ]),
        Net(name="U3FB", code="10", nodes=[
            node("U3", "2", "FB", "input"),
            node("R10", "2", "2", "passive"),
        ]),
        Net(name="U3OUT", code="11", nodes=[
            node("U3", "3", "SW", "power_out"),
            node("D10", "K", "K", "passive"),
        ]),
        # Tie up the remaining passive pins so every 2-pin part has
        # both ends on a real net (R20.2, C10.2, C20.2, C30.2, L10.2)
        # -- CHANGED 2026-09-29, from one, single, shared "MISC" net
        # tying up all five at once to five separate, single-member
        # nets instead, closing a real, confirmed gap
        # FutureResearch.org's own rail-only-passives item found: a
        # single, shared net for five otherwise-unrelated pins is
        # itself a real, high-degree net (5 members), which
        # kicad_layout.py's own new rail_only_refs -- and, confirmed
        # directly, auto_layout.py's own already-established rail-
        # attachment fallback, which shares the SAME real criterion
        # -- both read as a genuine power/ground rail purely by
        # member count, with no real power-distribution significance
        # at all. Confirmed directly: R20 was wrongly swept into
        # kicad_layout.py's own new dedicated row as a result. Five
        # separate nets keep this file's own real intent (every pin
        # on a real net, none left dangling) without a real net
        # shaped like a rail purely by coincidence.
        Net(name="R20-UNUSED", code="12", nodes=[node("R20", "2", "2", "passive")]),
        Net(name="C10-UNUSED", code="13", nodes=[node("C10", "2", "2", "passive")]),
        Net(name="C20-UNUSED", code="14", nodes=[node("C20", "2", "2", "passive")]),
        Net(name="C30-UNUSED", code="15", nodes=[node("C30", "2", "2", "passive")]),
        Net(name="L10-UNUSED", code="16", nodes=[node("L10", "2", "2", "passive")]),
    ]

    return Netlist(components=components, nets=nets, pin_names=pin_names, pin_types=pin_types)


def generate():
    nl = build_netlist()
    anchors, passives = classify(nl)
    rails = high_degree_nets(nl)
    ordered_anchors = order_anchors_by_connectivity(nl, anchors, rails)
    print(f"anchors: {ordered_anchors}, passives: {sorted(passives)}, rails: {sorted(rails)}")
    print(f"PWR_FLAG nets: {nl.nets_needing_pwr_flag()}")

    sch = generate_schematic_auto(
        nl, PINS, _SYM_DIR, ordered_anchors, passives, rails,
        title="dudhya: synthetic test circuit 2 -- flat/comfortable shape check",
        date="2026-08-14",
        rev="test2",
        company="dudhya project",
        comments=[
            "NOT a real dudhya circuit -- a second synthetic netlist,",
            "deliberately shaped opposite to the first one (flat, no",
            "passive-to-passive chains, comfortable PWR_FLAG headroom)",
            "to add confidence beyond the two circuits already tested.",
            "See ResearchJournal.org's 2026-08-14 entry.",
        ],
        verbose=True,
    )

    out_sch = os.path.join(os.path.dirname(__file__), "packpay-example-circuit2.kicad_sch")
    out_pro = os.path.join(os.path.dirname(__file__), "packpay-example-circuit2.kicad_pro")
    root_uuid = sch.write(out_sch)
    write_project_file(out_pro, root_uuid, os.path.basename(out_pro))
    print(f"wrote {out_sch}, root uuid {root_uuid}")
    return out_sch


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__":
    # Same treatment, same reasoning, as example_circuit1.py's own
    # __main__ block -- see its own comment.
    import argparse
    parser = argparse.ArgumentParser(
        prog="ppexample_circuit2",
        description="Build and write this project's own second synthetic "
                     "test circuit (packpay-example-circuit2.kicad_sch), "
                     "deliberately shaped opposite to the first (flat, no "
                     "passive-to-passive chains). Used by 'make check'. "
                     "Takes no arguments: the circuit and its output path "
                     "are both fixed, by design.")
    parser.add_argument("--version", action="version",
                         version=f"%(prog)s {_version()}")
    parser.parse_args()
    generate()
