"""A complete, single-file minimal PDK.

In a real distribution, layers / profiles / components / transitions
each live in their own module. For learning purposes everything is
collapsed into one file here. The end-user import surface is the same:

    import lumicron.pdks.tinyphot.all as pdk
    pdk.LAYER.SILC
    pdk.RP.silc_strip
    pdk.Pad(size=80)
"""
import lumicron as lm
import lumicron_pdk as lpdk

# ── Layers ───────────────────────────────────────────────────
LAYER = lpdk.LayerTable(
    SILC = lpdk.Layer(9, 0, color="#008000", min_width=0.2, min_radius=5.0),
    SILN = lpdk.Layer(5, 0, color="#ff69b4", min_width=0.4, min_radius=10.0),
    OXID = lpdk.Layer(1, 0, color="#87ceeb"),
    MTL1 = lpdk.Layer(4, 0, color="#ffd700", min_width=1.0, min_spacing=1.0),
)

# ── Route profiles ───────────────────────────────────────────
@lpdk.route_profile(port_width=0.5, radius=10.0, radius_min=5.0)
def silc_strip(p):
    p.layer(LAYER.SILC, width=p.port_width)
    p.layer(LAYER.OXID, width=p.port_width + 4.0)


@lpdk.route_profile(port_width=0.5, radius=10.0, radius_min=5.0)
def siln_strip(p):
    p.layer(LAYER.SILN, width=p.port_width)
    p.layer(LAYER.OXID, width=p.port_width + 4.0)


RP = lpdk.RouteProfiles(silc_strip=silc_strip, siln_strip=siln_strip)


# ── Components ───────────────────────────────────────────────
@lm.pcell
def Pad(size: float = 80.0):
    c = lm.CELL("Pad")
    body = lm.Rectangle(x_dim=size, y_dim=size, layer=LAYER.MTL1)
    h = c.add(body)
    c.Place(h).at((0, 0))
    c.add(lm.PORT("p", position=(0, -size / 2), direction=270,
                  width=size, layer=LAYER.MTL1, port_type="electrical"))
    return c


@lm.pcell
def Waveguide(length: float = 100.0):
    """A straight waveguide stub — useful for grating couplers, taps."""
    c = lm.CELL("Waveguide")
    rect = lm.Rectangle(x_dim=length, y_dim=0.5, layer=LAYER.SILC)
    h = c.add(rect)
    c.Place(h).using("SW").at((0, -0.25))
    c.add(lm.PORT("o1", position=(0, 0), direction=180,
                  width=0.5, route_profile=silc_strip))
    c.add(lm.PORT("o2", position=(length, 0), direction=0,
                  width=0.5, route_profile=silc_strip))
    return c


# ── Transitions ──────────────────────────────────────────────
def silc_siln_elevator():
    return lpdk.PhotonicElevator(
        layer_a=LAYER.SILC, layer_b=LAYER.SILN,
        width_a=0.5, width_b=0.5,
        tip_a=0.08, tip_b=0.08,
        taper_length_a=25.0, taper_length_b=25.0,
        overlap=10.0,
        cladding_layer=LAYER.OXID, cladding_margin=2.0,
        route_profile_a=silc_strip,
        route_profile_b=siln_strip,
    )


TRANSITIONS = lpdk.TransitionRegistry()
TRANSITIONS.register(LAYER.SILC, LAYER.SILN, silc_siln_elevator)
lpdk.default_transitions().register(LAYER.SILC, LAYER.SILN, silc_siln_elevator)


# ── Demo cell using the PDK we just defined ──────────────────
@lm.pcell
def TinyPhotDemo():
    c = lm.CELL("TinyPhotDemo")
    wg1 = c.add(Waveguide(length=80))
    wg2 = c.add(Waveguide(length=80))
    c.Place(wg1).at((0, 0))
    c.Place(wg2).at((300, 200)).rotate_by(180)
    c.Route(wg1.ports["o2"], wg2.ports["o2"])
    return c


if __name__ == "__main__":
    TinyPhotDemo().to_gds("tinyphot_demo.gds")
