"""Cookbook: a Mach-Zehnder interferometer.

Two paths between a Y-splitter and a Y-combiner. The top arm carries a
chained taper pair (a thermal phase shifter region); the bottom arm
takes a delay via .jog(). This example does not request length matching.
"""
import lumicron as lm
import lumicron.pdks.elyon_demo.all as pdk


@lm.pcell
def YSplitter(width: float = 0.5):
    """Stand-in Y-junction with three ports: i1, o1, o2."""
    c = lm.CELL("YSplitter")
    body = lm.Rectangle(x_dim=20, y_dim=4, layer=pdk.LAYER.SILC)
    c.add(body)
    c.Place(body).at((0, 0))
    c.add(lm.PORT("i1", position=(0, 0), direction=180,
                  width=width, route_profile=pdk.RP.silc_strip))
    c.add(lm.PORT("o1", position=(20, 1.5), direction=0,
                  width=width, route_profile=pdk.RP.silc_strip))
    c.add(lm.PORT("o2", position=(20, -1.5), direction=0,
                  width=width, route_profile=pdk.RP.silc_strip))
    return c


@lm.pcell
def MZI(arm_length: float = 400, delay: float = 60):
    c = lm.CELL("MZI")

    s = c.add(YSplitter())
    k = c.add(YSplitter())

    c.Place(s).at((0, 0))
    c.Place(k).at((arm_length + 60, 0)).rotate_by(180)

    # Top arm — straight, with a phase-shifter taper bay.
    (c.Route(s.ports["o1"], radius=15, bend="euler").to(k.ports["o2"])
        .taper(offset=120, width=2.0, length=20)
        .taper(offset=120, width=0.5, length=20))

    # Bottom arm — jog south by `delay` to add a delay region.
    c.Route(s.ports["o2"], radius=15, bend="euler").to(k.ports["o1"]) \
        .jog("S", by=delay)
    return c


if __name__ == "__main__":
    MZI().to_gds("mzi.gds")
