# 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.

from analogpy import Circuit, Testbench, nmos, generate_spectre, pmos, auto_comment_params

def test_basic():
    """Test basic inverter circuit definition."""
    ckt = Circuit("inv", ports=["vin", "vout", "vdd", "vss"])
    vin = ckt.net("vin")
    vout = ckt.net("vout")
    vdd = ckt.net("vdd")
    vss = ckt.net("vss")  # Ground as port, not global "0"

    ckt.add_instance(nmos, "MN1", d=vout, g=vin, s=vss, b=vss, w=1e-6, l=180e-9)
    ckt.add_instance(pmos, "MP1", d=vout, g=vin, s=vdd, b=vdd, w=2e-6, l=180e-9)

    netlist = generate_spectre(ckt)
    assert "MN1" in netlist
    assert "MP1" in netlist
    assert "nmos" in netlist
    assert "pmos" in netlist


def test_auto_comment_params_decorator():
    """Test that @auto_comment_params decorator adds function arguments as comments."""
    @auto_comment_params
    def make_circuit(model_name: str, size: float, multiplier: float = 1.0) -> Circuit:
        ckt = Circuit("test_circuit", ports=["A", "B"])
        return ckt

    # Call with positional and keyword args
    circuit = make_circuit("model.txt", 100.5, multiplier=2.0)

    # Verify comments were added
    assert len(circuit.comments) == 3
    assert "model_name = model.txt" in circuit.comments
    assert "size = 100.5" in circuit.comments
    assert "multiplier = 2.0" in circuit.comments


def test_auto_comment_params_with_defaults():
    """Test that @auto_comment_params includes default parameter values."""
    @auto_comment_params
    def make_circuit_with_defaults(name: str, value: float = 42.0) -> Circuit:
        ckt = Circuit("default_test", ports=["X"])
        return ckt

    # Call without specifying the default parameter
    circuit = make_circuit_with_defaults("test")

    # Default value should still be in comments
    assert "name = test" in circuit.comments
    assert "value = 42.0" in circuit.comments


def test_circuit_auto_naming_variable_capture():
    """Test that Circuit auto-names using Python variable name on conflict."""
    @auto_comment_params
    def make_oled(model: str) -> Circuit:
        return Circuit("oled_model", ports=["A", "B"])

    # First use: gets base name
    oled_first = make_oled("model1.txt")
    assert oled_first.name == "oled_model"

    # Subsequent uses: capture variable name
    oled_blue = make_oled("blue.txt")
    assert oled_blue.name == "oled_blue"

    oled_red = make_oled("red.txt")
    assert oled_red.name == "oled_red"


def test_circuit_auto_naming_counter_fallback():
    """Test that Circuit falls back to counter when variable name can't be captured."""
    def make_oled() -> Circuit:
        return Circuit("oled_model", ports=["A", "B"])

    # List comprehension - can't capture variable names
    circuits = [make_oled() for _ in range(3)]

    assert circuits[0].name == "oled_model"
    assert circuits[1].name == "oled_model_1"
    assert circuits[2].name == "oled_model_2"

def test_global_name_counter_circuit_testbench():
    """Test that Circuit and Testbench share a global name counter to prevent collisions."""
    # First Circuit with name "shared" gets the base name
    circuit1 = Circuit("shared", ports=["A"])
    assert circuit1.name == "shared"

    # Testbench with same name should get incremented name (shared_1)
    tb1 = Testbench("shared")
    assert tb1.name == "shared_1"

    # Another Circuit with same name should get shared_2
    circuit2 = Circuit("shared", ports=["B"])
    assert circuit2.name == "shared_2"

    # Another Testbench with same name should get shared_3
    tb2 = Testbench("shared")
    assert tb2.name == "shared_3"


def test_global_name_counter_testbench_first():
    """Test global counter when Testbench is created first."""
    # Testbench first with name "tb_first" gets the base name
    tb = Testbench("tb_first")
    assert tb.name == "tb_first"

    # Circuit with same name should get incremented
    ckt = Circuit("tb_first", ports=["X"])
    assert ckt.name == "tb_first_1"


if __name__ == "__main__":
    test_basic()

