"""Via chain."""
from __future__ import annotations
import gdsfactory as gf
from gdsfactory.component import Component
from gdsfactory.components.via import via1
from gdsfactory.components.via_stack import via_stack_m2_m3
from gdsfactory.typings import ComponentSpec, LayerSpecs
[docs]
@gf.cell
def via_chain(
    num_vias: int = 100,
    cols: int = 10,
    via: ComponentSpec = via1,
    contact: ComponentSpec = via_stack_m2_m3,
    layers_bot: LayerSpecs = ("M1",),
    layers_top: LayerSpecs = ("M2",),
    offsets_top: tuple[float, ...] = (0,),
    offsets_bot: tuple[float, ...] = (0,),
    via_min_enclosure: float = 1.0,
    min_metal_spacing: float = 1.0,
    via_xoffset: float = 0.0,
    via_yoffset: float = 0.0,
) -> Component:
    """Via chain to extract via resistance.
    Args:
        num_vias: number of vias.
        cols: number of column pairs.
        via: via component.
        contact: contact component.
        layers_bot: list of bottom layers.
        layers_top: list of top layers.
        offsets_top: list of top layer offsets.
        offsets_bot: list of bottom layer offsets.
        via_min_enclosure: via_min_enclosure.
        min_metal_spacing: min_metal_spacing.
        via_xoffset: horizontal offset of the vias.
        via_yoffset: vertical offset of the vias.
    .. code::
        side view:
                                              min_metal_spacing
           ┌────────────────────────────────────┐              ┌────────────────────────────────────┐
           │  layers_top                        │              │                                    │
           │                                    │◄───────────► │                                    │
           └─────────────┬─────┬────────────────┘              └───────────────┬─────┬──────────────┘
                         │     │         via_enclosure                         │     │
                         │     │◄───────────────►                              │     │
                         │     │                                               │     │
                         │     │                                               │     │
                         │width│                                               │     │
                         ◄─────►                                               │     │
                         │     │                                               │     │
           ┌─────────────┴─────┴───────────────────────────────────────────────┴─────┴───────────────┐
           │ layers_bot                                                                              │
           │                                                                                         │
           └─────────────────────────────────────────────────────────────────────────────────────────┘
           ◄─────────────────────────────────────────────────────────────────────────────────────────►
                                         2*e + w + min_metal_spacing + 2*e + w
    """
    if cols % 2 != 0:
        raise ValueError(f"{cols=} must be even")
    c = gf.Component()
    rows = num_vias / cols
    if int(rows) != rows:
        raise ValueError(f"{num_vias=} must be a multiple of {cols=}")
    if rows <= 1:
        raise ValueError(
            f"rows must be at least 2. Got {rows=}. You can increase the number vias {num_vias=}."
        )
    if rows % 2 != 0:
        raise ValueError(
            f"{rows=} must be even. Number of vias needs to be a multiple of {2*cols=}."
        )
    via = gf.get_component(via)
    contact = gf.get_component(contact)
    wire_length = 2 * (2 * via_min_enclosure + via.size_info.width) + min_metal_spacing
    via_width = via.size_info.width
    wire_width = via_width + 2 * via_min_enclosure
    wire_size = (wire_length, wire_width)
    via_spacing = (
        2 * via_min_enclosure + min_metal_spacing + via.size_info.width,
        wire_width + min_metal_spacing,
    )
    vias = c.add_array(
        component=via,
        columns=cols,
        rows=rows,
        spacing=via_spacing,
    )
    top_wire = gf.c.rectangles(size=wire_size, layers=layers_top, offsets=offsets_top)
    top_wires = c.add_array(
        component=top_wire,
        columns=cols // 2,
        rows=rows,
        spacing=(wire_length + min_metal_spacing, wire_width + min_metal_spacing),
    )
    bot_wire = gf.c.rectangles(size=wire_size, layers=layers_bot, offsets=offsets_bot)
    bot_wires = c.add_array(
        component=bot_wire,
        columns=cols // 2,
        rows=rows,
        spacing=(wire_length + min_metal_spacing, wire_width + min_metal_spacing),
    )
    top_wires.x = 0
    bot_wires.xmin = (
        top_wires.xmin + wire_length / 2 + min_metal_spacing / 2 - via_xoffset
    )
    bot_wires.y = 0
    top_wires.y = 0
    vias.xmin = top_wires.xmin + via_min_enclosure + via_spacing[0]
    vias.ymin = top_wires.ymin + via_min_enclosure + via_yoffset
    vertical_wire_left = gf.c.rectangle(
        size=(2 * via_min_enclosure + via_width, 2 * wire_width + min_metal_spacing),
        layer=layers_top[0],
    )
    right_wires = c.add_array(
        component=vertical_wire_left,
        columns=1,
        rows=rows // 2,
        spacing=(wire_width + min_metal_spacing, 2 * (wire_width + min_metal_spacing)),
    )
    right_wires.xmax = vias.xmax + via_min_enclosure
    right_wires.y = 0
    left_wires = c.add_array(
        component=vertical_wire_left,
        columns=1,
        rows=rows // 2 - 1,
        spacing=(wire_width + min_metal_spacing, 2 * (wire_width + min_metal_spacing)),
    )
    left_wires.xmin = top_wires.xmin
    left_wires.y = 0
    contact1 = c << contact
    contact2 = c << contact
    contact1.xmax = top_wires.xmin
    contact2.xmax = top_wires.xmin
    contact1.ymax = top_wires.ymin + wire_width
    contact2.ymin = top_wires.ymax - wire_width
    c.add_port(name="e1", port=contact1.ports["e1"])
    c.add_port(name="e2", port=contact2.ports["e1"])
    return c 
if __name__ == "__main__":
    c = via_chain(num_vias=40)
    c.show(show_ports=True)