from typing import Tuple

from gdsfactory.tech import LAYER

nm = 1e-3


def layer_to_string(layer: Tuple[int, int]) -> str:
    return f"{layer[0]}/{layer[1]}"


def get_xsection_script(
    t_box=1.0,
    t_slab=90 * nm,
    t_si=0.22,
    t_ge=0.4,
    t_nitride=0.4,
    h_etch1=0.07,
    h_etch2=0.06,
    h_etch3=0.09,
    t_clad=0.6,
    t_m1=0.5,
    t_m2=0.5,
    t_m3=2.0,
    gap_m1_m2=0.6,
    gap_m2_m3=0.3,
    t_heater=0.1,
    gap_oxide_nitride=0.82,
    t_m1_oxide=0.6,
    t_m2_oxide=2.0,
    t_m3_oxide=0.5,
    layer_wg=LAYER.WG,
    layer_fc=LAYER.SLAB150,
    layer_rib=LAYER.SLAB90,
    layer_n=LAYER.N,
    layer_np=LAYER.NP,
    layer_npp=LAYER.NPP,
    layer_p=LAYER.P,
    layer_pp=LAYER.PP,
    layer_ppp=LAYER.PPP,
    layer_PDPP=LAYER.GEP,
    layer_nitride=LAYER.WGN,
    layer_Ge=LAYER.GE,
    layer_GePPp=LAYER.GEP,
    layer_GeNPP=LAYER.GEN,
    layer_viac=LAYER.VIAC,
    layer_viac_slot=LAYER.VIAC,
    layer_m1=LAYER.M1,
    layer_mh=LAYER.HEATER,
    layer_via1=LAYER.VIA1,
    layer_m2=LAYER.M2,
    layer_via2=LAYER.VIA2,
    layer_m3=LAYER.M3,
    layer_open=LAYER.PADOPEN,
) -> str:
    """Returns xsection script for klayout_pyxs plugin.

    https://gdsfactory.github.io/klayout_pyxs/DocGrow.html

    """
    return f"""

t_box={t_box}
t_slab={t_slab}
t_si={t_si}
t_ge={t_ge}
t_nitride={t_nitride}
h_etch1={h_etch1}
h_etch2={h_etch2}
h_etch3={h_etch3}
t_clad={t_clad}
t_m1={t_m1}
t_m2={t_m2}
t_m3={t_m3}
t_heater={t_heater}
gap_m1_m2={gap_m1_m2}
gap_m2_m3={gap_m2_m3}
gap_oxide_nitride={gap_oxide_nitride}
t_m1_oxide={t_m1_oxide}
t_m2_oxide={t_m2_oxide}
t_m3_oxide={t_m3_oxide}

l_wg = layer("{layer_to_string(layer_wg)}")
l_fc = layer("{layer_to_string(layer_fc)}")
l_rib = layer("{layer_to_string(layer_rib)}")

l_n = layer("{layer_to_string(layer_n)}")
l_np = layer("{layer_to_string(layer_np)}")
l_npp = layer("{layer_to_string(layer_npp)}")
l_p = layer("{layer_to_string(layer_p)}")
l_pp = layer("{layer_to_string(layer_pp)}")
l_ppp = layer("{layer_to_string(layer_ppp)}")
l_PDPP = layer("{layer_to_string(layer_PDPP)}")
l_bottom_implant = l_PDPP

l_nitride = layer("{layer_to_string(layer_nitride)}")
l_Ge = layer("{layer_to_string(layer_Ge)}")
l_GePPp = layer("{layer_to_string(layer_GePPp)}")
l_GeNPP = layer("{layer_to_string(layer_GeNPP)}")

l_viac = layer("{layer_to_string(layer_viac)}")
l_viac_slot = layer("{layer_to_string(layer_viac_slot)}")
l_m1 = layer("{layer_to_string(layer_m1)}")
l_mh = layer("{layer_to_string(layer_mh)}")
l_via1 = layer("{layer_to_string(layer_via1)}")
l_m2 = layer("{layer_to_string(layer_m2)}")
l_via2 = layer("{layer_to_string(layer_via2)}")
l_m3 = layer("{layer_to_string(layer_m3)}")
l_open = layer("{layer_to_string(layer_open)}")

l_top_implant = l_GePPp.or_(l_GeNPP)
l_viac = l_viac.or_(l_viac_slot)

# Declare the basic accuracy used to remove artifacts for example: delta(5 * dbu)
delta(dbu)
depth(12.0)
height(12.0)

################ front-end

l_wg_etch1 = l_wg.inverted()  # protects ridge
l_wg_etch2 = (
    l_fc.or_(l_wg)
).inverted()  # protects ridge and grating couplers from the etch down to the slab (forms rib straights)
l_wg_etch3 = (
    l_rib.or_(l_fc).or_(l_wg)
).inverted()  # protects ridge, grating couplers and rib straights from the final etch to form strip straights


################ back-end
substrate = bulk
box = deposit(t_box)
si = deposit(t_si)

################ silicon etch to for the passives
mask(l_wg_etch1).etch(
    h_etch1, 0.0, mode="round", into=[si]
)  # 70nm etch for GC, rib and strip
mask(l_wg_etch2).etch(
    h_etch2, 0.0, mode="round", into=[si]
)  # 60nm etch after 70nm = 130nm etch (90nm slab)
mask(l_wg_etch3).etch(
    h_etch3, 0.0, mode="round", into=[si]
)  # etches the remaining 90nm slab for strip straights

output("300/0", box)
output("301/0", si)

############### doping
mask(l_bottom_implant).etch(t_si, 0.0, mode="round", into=[si])
bottom_implant = mask(l_bottom_implant).grow(t_si, 0.0, mode="round")

mask(l_n).etch(t_slab, 0.0, mode="round", into=[si])
n = mask(l_n).grow(t_slab, 0.0, mode="round")

mask(l_p).etch(t_slab, 0.0, mode="round", into=[si])
p = mask(l_p).grow(t_slab, 0.0, mode="round")

mask(l_np).etch(t_slab, 0.0, mode="round", into=[n, p, si, bottom_implant])
np = mask(l_np).grow(t_slab, 0.0, mode="round")

mask(l_pp).etch(t_slab, 0.0, mode="round", into=[n, p, si, bottom_implant])
pp = mask(l_pp).grow(t_slab, 0.0, mode="round")

mask(l_npp).etch(t_slab, 0.0, mode="round", into=[n, p, np, pp, si, bottom_implant])
npp = mask(l_npp).grow(t_slab, 0.0, mode="round")

mask(l_ppp).etch(t_slab, 0.0, mode="round", into=[n, p, np, pp, si, bottom_implant])
ppp = mask(l_ppp).grow(t_slab, 0.0, mode="round")

output("327/0", bottom_implant)
output("330/0", p)
output("320/0", n)
output("321/0", npp)
output("331/0", ppp)

################ Ge
Ge = mask(l_Ge).grow(t_ge, 0, bias=0.0, taper=10)
output("315/0", Ge)

################ Nitride
ox_nitride = deposit(2 * gap_oxide_nitride, 2 * gap_oxide_nitride)
planarize(less=gap_oxide_nitride, into=[ox_nitride])
output("302/0", ox_nitride)

nitride = mask(l_nitride).grow(t_nitride, 0, bias=0.0, taper=10)
output("305/0", nitride)

################# back-end
################# VIAC, M1 and MH
ox_si = deposit(t_clad + t_ge + t_nitride, t_clad + t_ge + t_nitride, mode="round")
planarize(less=t_ge + t_nitride, into=[ox_si])
mask(l_viac).etch(
    t_clad + t_ge + t_nitride + gap_oxide_nitride, taper=4, into=[ox_si, ox_nitride]
)

viac = deposit(2 * t_clad, 2 * t_clad)
planarize(less=2 * t_clad, into=[viac])

mh = deposit(t_heater, t_heater)
mask(l_mh.inverted()).etch(t_heater + t_heater, into=[mh])
m1 = deposit(t_m1, t_m1)
mask(l_m1.inverted()).etch(t_m1 + t_m1, into=[m1])
output("306/0", mh)
output("399/0", m1)

output("302/0", ox_si)
output("303/0", viac)

################# VIA1 and M2
ox_m1 = deposit(2 * t_m1_oxide, 2 * t_m1_oxide, mode="round")
planarize(less=t_m1_oxide, into=[ox_m1])

mask(l_via1).etch(t_m1_oxide + gap_m1_m2, taper=4, into=[ox_m1])
via1 = deposit(t_m2, t_m2)

mask(l_m2.inverted()).etch(t_m2, taper=4, into=[via1])
output("308/0", via1)

ox_m2 = deposit(2 * t_m2_oxide, 2 * t_m2_oxide, mode="round")
planarize(less=t_m2_oxide, into=[ox_m2])
output("309/0", ox_m2)
output("307/0", ox_m1)

################# VIA2 and M3
mask(l_via2).etch(t_m2_oxide + gap_m2_m3, taper=4, into=[ox_m2, ox_m2])
via2 = deposit(t_m3, t_m3)
mask(l_m3.inverted()).etch(t_m3, taper=4, into=[via2])
output("310/0", via2)

################# passivation and ML Open
ox_m3 = deposit(t_m3_oxide, t_m3_oxide, mode="round")
mask(l_open).etch(t_m3_oxide + t_m3_oxide, into=[ox_m3], taper=5)
output("311/0", ox_m3)
"""


if __name__ == "__main__":
    s = get_xsection_script()
    print(s)
