# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: percent
#       format_version: '1.3'
#       jupytext_version: 1.17.3
# ---

# %% [markdown]
# # Export a resonator for simulation
#

# %%

import gdsfactory as gf
import numpy as np
from gdsfactory.export import to_stl

from qpdk.cells.airbridge import cpw_with_airbridges
from qpdk.cells.launcher import launcher
from qpdk.cells.resonator import resonator_coupled
from qpdk.config import PATH
from qpdk.tech import LAYER, route_bundle_sbend_cpw

# %% [markdown]
# ## System geometry
#
# Create a simulation layout with a resonator coupled to two probeline launchers.
#
# The layout consists of:
#   - A coupled resonator (with open start and specified coupling length)
#   - Two launcher components (mirrored and positioned symmetrically)
#   - CPW routes with airbridges connecting launchers to resonator coupling ports
#   - A simulation area layer enlarged around the layout
#   - Ports added for both launchers with prefixes


# %%
@gf.cell
def resonator_simulation(coupling_gap: float = 12.0) -> gf.Component:
    """Create a resonator simulation layout with launchers and CPW routes."""
    c = gf.Component()

    res_ref = c << resonator_coupled(
        open_start=True, coupling_straight_length=300, coupling_gap=coupling_gap
    )
    res_ref.movex(-res_ref.size_info.width / 4)

    launcher_left = c << launcher()
    launcher_right = c << launcher()
    launcher_right.mirror()

    width_offset = res_ref.size_info.width + 700
    launcher_left.move((-width_offset, 0))
    launcher_right.move((width_offset, 0))

    route_bundle_sbend_cpw(
        c,
        [launcher_left["o1"], launcher_right["o1"]],
        [res_ref["coupling_o1"], res_ref["coupling_o2"]],
        cross_section=cpw_with_airbridges(
            airbridge_spacing=250.0, airbridge_padding=20.0
        ),
    )

    c.kdb_cell.shapes(LAYER.SIM_AREA).insert(c.bbox().enlarged(0, 100))

    c.add_ports(launcher_left.ports, prefix="left_")
    c.add_ports(launcher_right.ports, prefix="right_")

    return c


if __name__ == "__main__":
    from qpdk import PDK

    PDK.activate()

    # Create and display the filled version
    c = gf.Component("resonator_simulation")
    res_ref = c << resonator_simulation()
    c.add_ports(res_ref.ports)
    c.plot(
        pixel_buffer_options=dict(width=1300, height=1000, oversampling=2, linewidth=3)
    )
    c.show()

    # Export 3D model
    to_stl(
        c,
        PATH.simulation / "resonator_simulations.stl",
        layer_stack=PDK.layer_stack,
        hull_invalid_polygons=True,
    )

    material_spec = {
        "Si": {"relative_permittivity": 11.45},
        "Nb": {"relative_permittivity": np.inf},
        "vacuum": {"relative_permittivity": 1},
    }

    # TODO implement running simulations here

    # from gplugins.palace import run_scattering_simulation_palace
    #
    # results = run_scattering_simulation_palace(
    #     c,
    #     layer_stack=PDK.layer_stack,
    #     material_spec=material_spec,
    #     only_one_port=True,
    #     driven_settings={
    #         "MinFreq": 0.1,
    #         "MaxFreq": 5,
    #         "FreqStep": 5,
    #     },
    #     n_processes=1,
    #     simulation_folder=Path().cwd() / "temporary",
    #     mesh_parameters=dict(
    #         background_tag="vacuum",
    #         background_padding=(0,) * 5 + (700,),
    #         port_names=[port.name for port in c.ports],
    #         default_characteristic_length=200,
    #         resolutions={
    #             "M1": {
    #                 "resolution": 15,
    #             },
    #             "Silicon": {
    #                 "resolution": 40,
    #             },
    #             "vacuum": {
    #                 "resolution": 40,
    #             },
    #             **{
    #                 f"M1__{port}": {  # `__` is used as the layer to port delimiter for Elmer
    #                     "resolution": 20,
    #                     "DistMax": 30,
    #                     "DistMin": 10,
    #                     "SizeMax": 14,
    #                     "SizeMin": 3,
    #                 }
    #                 for port in c.ports
    #             },
    #         },
    #     ),
    # )
    #
    # display(results)

    #
    # df = results.scattering_matrix
    # df.columns = df.columns.str.strip()
    # s_complex = 10 ** (df["|S[2][1]| (dB)"].values / 20) * np.exp(
    #     1j * np.deg2rad(df["arg(S[2][1]) (deg.)"].values)
    # )
    # # Assuming a 1-port reflection measurement for capacitance extraction
    # y11 = (1 - s_complex) / (1 + s_complex) / 50.0
    # freq_hz = df["f (GHz)"].values * 1e9
    # cap = np.imag(y11) / (freq_hz * 2 * np.pi)
    # display(cap)
    #
    # plt.plot(freq_hz / 1e9, cap * 1e15)
    # plt.xlabel("Freq (GHz)")
    # plt.ylabel("C (fF)")
    #
    # if results.field_file_locations:
    #     pv.start_xvfb()
    #     pv.set_jupyter_backend("trame")
    #     field = pv.read(results.field_file_locations[0])
    #     slice = field.slice_orthogonal(z=layer_stack.layers["bw"].zmin * 1e-6)
    #
    #     p = pv.Plotter()
    #     p.add_mesh(slice, scalars="Ue", cmap="turbo")
    #     p.show_grid()
    #     p.camera_position = "xy"
    #     p.enable_parallel_projection()
    #     p.show()
