# Copyright 2026 Gaofeng Fan
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Tests for hierarchical circuit design (Circuit and instantiation)."""

from analogpy import Circuit, nmos, pmos, generate_spectre


def make_inverter(w_n=1e-6, w_p=2e-6, l=180e-9):
    """Create an inverter circuit with configurable sizing."""
    inv = Circuit("inverter", ports=["in", "out", "vdd", "vss"])

    inv.add_instance(nmos, "MN", d=inv.net("out"), g=inv.net("in"),
                    s=inv.net("vss"), b=inv.net("vss"), w=w_n, l=l)
    inv.add_instance(pmos, "MP", d=inv.net("out"), g=inv.net("in"),
                    s=inv.net("vdd"), b=inv.net("vdd"), w=w_p, l=l)

    return inv


def test_circuit_definition():
    """Test basic circuit creation."""
    inv = make_inverter()

    assert inv.name == "inverter"
    assert inv.ports == ["in", "out", "vdd", "vss"]
    assert len(inv.subcircuit_instances) == 2


def test_circuit_instantiation():
    """Test instantiating a circuit in a top-level circuit."""
    # Create circuit
    inv = make_inverter()

    # Create top-level circuit (no ports)
    top = Circuit("top", ports=[])
    vin = top.net("vin")
    vout = top.net("vout")
    vdd = top.net("vdd")
    gnd = top.gnd()  # Global ground "0" at top level

    # Instantiate - connect cell's vss port to global ground
    x1 = top.add_instance(inv, "X1",
                         **{"in": vin, "out": vout, "vdd": vdd, "vss": gnd})

    assert x1.name == "X1"
    assert x1.subcircuit.name == "inverter"
    assert len(top.subcircuit_instances) == 1


def test_multiple_instantiation():
    """Test multiple instances of the same circuit."""
    inv = make_inverter()

    top = Circuit("ring_osc", ports=[])
    vdd = top.net("vdd")
    gnd = top.gnd()  # Global ground "0" at top level

    # Create a 3-stage ring oscillator
    n1 = top.net("n1")
    n2 = top.net("n2")
    n3 = top.net("n3")

    top.add_instance(inv, "X1", **{"in": n3, "out": n1, "vdd": vdd, "vss": gnd})
    top.add_instance(inv, "X2", **{"in": n1, "out": n2, "vdd": vdd, "vss": gnd})
    top.add_instance(inv, "X3", **{"in": n2, "out": n3, "vdd": vdd, "vss": gnd})

    assert len(top.subcircuit_instances) == 3

    # Generate netlist - should have only one subcircuit definition
    netlist = generate_spectre(top)
    assert netlist.count("subckt inverter") == 1
    assert "X1" in netlist
    assert "X2" in netlist
    assert "X3" in netlist


def test_nested_hierarchy():
    """Test circuits containing other circuits."""
    # Create inverter
    inv = make_inverter()

    # Create buffer (two inverters)
    buf = Circuit("buffer", ports=["in", "out", "vdd", "vss"])
    mid = buf.net("mid")

    buf.add_instance(inv, "X_INV1",
                    **{"in": buf.net("in"), "out": mid,
                       "vdd": buf.net("vdd"), "vss": buf.net("vss")})
    buf.add_instance(inv, "X_INV2",
                    **{"in": mid, "out": buf.net("out"),
                       "vdd": buf.net("vdd"), "vss": buf.net("vss")})

    # Top level
    top = Circuit("top", ports=[])
    vdd = top.net("vdd")
    gnd = top.gnd()  # Global ground "0" at top level

    top.add_instance(buf, "X_BUF",
                    **{"in": top.net("in"), "out": top.net("out"),
                       "vdd": vdd, "vss": gnd})

    netlist = generate_spectre(top)
    print("\n=== Nested Hierarchy Netlist ===")
    print(netlist)

    # Should have both circuit definitions
    assert "subckt inverter" in netlist
    assert "subckt buffer" in netlist
    assert "X_BUF" in netlist


def test_netlist_generation():
    """Test complete netlist generation with hierarchy."""
    inv = make_inverter()

    top = Circuit("tb_inverter", ports=[])
    vin = top.net("vin")
    vout = top.net("vout")
    vdd = top.net("vdd")
    gnd = top.gnd()  # Global ground "0" at top level

    top.add_instance(inv, "X1",
                    **{"in": vin, "out": vout, "vdd": vdd, "vss": gnd})

    netlist = generate_spectre(top)
    print("\n=== Generated Netlist ===")
    print(netlist)

    # Verify structure
    assert "subckt inverter (in out vdd vss)" in netlist
    assert "ends inverter" in netlist
    assert "X1 (vin vout vdd 0) inverter" in netlist


if __name__ == "__main__":
    test_circuit_definition()
    print("test_circuit_definition passed")

    test_circuit_instantiation()
    print("test_circuit_instantiation passed")

    test_multiple_instantiation()
    print("test_multiple_instantiation passed")

    test_nested_hierarchy()
    print("test_nested_hierarchy passed")

    test_netlist_generation()
    print("test_netlist_generation passed")

    print("\nAll hierarchy tests passed!")
