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

"""
Voltage and current source cell definitions with symbol support.

Provides various source types as reusable cells:
- vpulse: Pulse voltage source
- vpwl: Piecewise linear voltage source
- vsin: Sinusoidal voltage source
- vcvs: Voltage-controlled voltage source
- vccs: Voltage-controlled current source
- ccvs: Current-controlled voltage source
- cccs: Current-controlled current source

Usage:
    from analogpy.cells import vpulse_cell, vcvs_cell

    pulse = vpulse_cell("CLK", v1=0, v2=1.8, period=10e-9, rise=100e-12)
    tb.instantiate(pulse, "I_CLK", plus=clk_net, minus=gnd)
"""

from typing import List, Tuple, Optional
from ..circuit import Circuit
from ..devices import vsource, vcvs, vccs, ccvs, cccs


def vpulse_cell(
    name: str,
    v1: float = 0.0,
    v2: float = 1.0,
    period: float = 1e-6,
    rise: float = 1e-9,
    fall: float = 1e-9,
    width: float = None,
    delay: float = 0.0,
) -> Circuit:
    """
    Create a pulse voltage source cell.

    Ports:
        plus: positive terminal
        minus: negative terminal

    Args:
        name: Cell name
        v1: Initial/low voltage
        v2: Pulsed/high voltage
        period: Pulse period
        rise: Rise time
        fall: Fall time
        width: Pulse width (default: period/2)
        delay: Initial delay
    """
    cell = Circuit(name, ports=["plus", "minus"])
    plus_net = cell.net("plus")
    minus_net = cell.net("minus")

    if width is None:
        width = period / 2

    # Use vsource with pulse parameters
    cell.instantiate(vsource, "V0", p=plus_net, n=minus_net,
                     type="pulse", val0=v1, val1=v2, period=period,
                     rise=rise, fall=fall, width=width, delay=delay)
    return cell


def vpwl_cell(
    name: str,
    points: List[Tuple[float, float]],
) -> Circuit:
    """
    Create a piecewise linear voltage source cell.

    Ports:
        plus: positive terminal
        minus: negative terminal

    Args:
        name: Cell name
        points: List of (time, voltage) tuples
    """
    cell = Circuit(name, ports=["plus", "minus"])
    plus_net = cell.net("plus")
    minus_net = cell.net("minus")

    # Use vsource with pwl parameter
    cell.instantiate(vsource, "V0", p=plus_net, n=minus_net, pwl=points)
    return cell


def vsin_cell(
    name: str,
    vo: float = 0.0,
    va: float = 1.0,
    freq: float = 1e6,
    td: float = 0.0,
    theta: float = 0.0,
) -> Circuit:
    """
    Create a sinusoidal voltage source cell.

    Ports:
        plus: positive terminal
        minus: negative terminal

    Args:
        name: Cell name
        vo: DC offset voltage
        va: Amplitude
        freq: Frequency in Hz
        td: Time delay
        theta: Damping factor
    """
    cell = Circuit(name, ports=["plus", "minus"])
    plus_net = cell.net("plus")
    minus_net = cell.net("minus")

    # Use vsource with sine parameters
    cell.instantiate(vsource, "V0", p=plus_net, n=minus_net,
                     type="sine", vo=vo, va=va, freq=freq, td=td, theta=theta)
    return cell


def vcvs_cell(
    name: str,
    gain: float = 1.0,
) -> Circuit:
    """
    Create a voltage-controlled voltage source cell.

    Ports:
        p_out: positive output terminal
        n_out: negative output terminal
        p_in: positive control terminal
        n_in: negative control terminal

    Args:
        name: Cell name
        gain: Voltage gain (Vout = gain * Vctrl)
    """
    cell = Circuit(name, ports=["p_out", "n_out", "p_in", "n_in"])
    p_out = cell.net("p_out")
    n_out = cell.net("n_out")
    p_in = cell.net("p_in")
    n_in = cell.net("n_in")

    cell.instantiate(vcvs, "E0", p_out=p_out, n_out=n_out,
                     p_in=p_in, n_in=n_in, gain=gain)
    return cell


def vccs_cell(
    name: str,
    gain: float = 1e-3,
) -> Circuit:
    """
    Create a voltage-controlled current source cell.

    Ports:
        p_out: positive output terminal (current flows out)
        n_out: negative output terminal (current flows in)
        p_in: positive control terminal
        n_in: negative control terminal

    Args:
        name: Cell name
        gain: Transconductance in A/V (Iout = gain * Vctrl)
    """
    cell = Circuit(name, ports=["p_out", "n_out", "p_in", "n_in"])
    p_out = cell.net("p_out")
    n_out = cell.net("n_out")
    p_in = cell.net("p_in")
    n_in = cell.net("n_in")

    cell.instantiate(vccs, "G0", p_out=p_out, n_out=n_out,
                     p_in=p_in, n_in=n_in, gm=gain)
    return cell


def ccvs_cell(
    name: str,
    rm: float = 1000.0,
) -> Circuit:
    """
    Create a current-controlled voltage source cell.

    In Spectre, ccvs references an iprobe by name to sense current.
    This cell is a wrapper - user must set the probe parameter when instantiating.

    Ports:
        plus: positive output terminal
        minus: negative output terminal

    Note: The probe parameter must be set at instantiation to reference
    the iprobe instance that senses the control current.

    Args:
        name: Cell name
        rm: Transresistance in V/A (Vout = rm * Iprobe)
    """
    cell = Circuit(name, ports=["plus", "minus"])
    plus_net = cell.net("plus")
    minus_net = cell.net("minus")

    # Note: probe must be set when instantiating
    cell.instantiate(ccvs, "H0", p=plus_net, n=minus_net, probe="", rm=rm)
    return cell


def cccs_cell(
    name: str,
    gain: float = 1.0,
) -> Circuit:
    """
    Create a current-controlled current source cell.

    In Spectre, cccs references an iprobe by name to sense current.
    This cell is a wrapper - user must set the probe parameter when instantiating.

    Ports:
        plus: positive output terminal (current flows out)
        minus: negative output terminal (current flows in)

    Note: The probe parameter must be set at instantiation to reference
    the iprobe instance that senses the control current.

    Args:
        name: Cell name
        gain: Current gain (Iout = gain * Iprobe)
    """
    cell = Circuit(name, ports=["plus", "minus"])
    plus_net = cell.net("plus")
    minus_net = cell.net("minus")

    # Note: probe must be set when instantiating
    cell.instantiate(cccs, "F0", p=plus_net, n=minus_net, probe="", gain=gain)
    return cell


# Symbol configurations for schematic visualization
SYMBOL_CONFIGS = {
    "vpulse": {
        "type": "vpulse",
        "shape": "circle_pulse",
        "color": "#006600",
        "label": "VPULSE",
    },
    "vpwl": {
        "type": "vpwl",
        "shape": "circle_pwl",
        "color": "#006600",
        "label": "VPWL",
    },
    "vsin": {
        "type": "vsin",
        "shape": "circle_sin",
        "color": "#006600",
        "label": "VSIN",
    },
    "vcvs": {
        "type": "vcvs",
        "shape": "diamond",
        "color": "#006600",
        "label": "E",
    },
    "vccs": {
        "type": "vccs",
        "shape": "diamond_arrow",
        "color": "#006600",
        "label": "G",
    },
    "ccvs": {
        "type": "ccvs",
        "shape": "diamond",
        "color": "#006600",
        "label": "H",
    },
    "cccs": {
        "type": "cccs",
        "shape": "diamond_arrow",
        "color": "#006600",
        "label": "F",
    },
}
