from .wire import DCTaper
from ..waveguides.waveguide_traces import WG_TMPL, TemplatedWindowWaveguide
from ..waveguides.cells import BaseWaveguide

import ipkiss3.all as i3
from ipkiss3.pcell.layout.port_list import PortList


class TemplatedHeaterWaveguide(TemplatedWindowWaveguide):
    class Layout(TemplatedWindowWaveguide.Layout):
        def _generate_elements(self, elems):
            elems = super(TemplatedHeaterWaveguide.Layout, self)._generate_elements(elems)
            heater = i3.get_layer_elements(elems, [i3.TECH.PPLAYER.HEATER])[0]

            tt_lv = self.cell._get_trace_template().get_default_view(i3.LayoutView)
            amount = 100
            round_radius = tt_lv.heater_width / 2.0 + amount

            elems += i3.Boundary(
                shape=i3.ShapeRound(
                    original_shape=i3.ShapeGrow(
                        original_shape=heater.shape,
                        amount=amount,
                    ),
                    radius=round_radius,
                ),
                layer=i3.TECH.PPLAYER.HEATER_CROSSTALK,
            )
            return elems

        def _generate_instances(self, insts):
            insts = super(TemplatedHeaterWaveguide.Layout, self)._generate_instances(insts)

            opt_ports = super(TemplatedHeaterWaveguide.Layout, self)._generate_ports(PortList())
            tt_lv = self.cell._get_trace_template().get_default_view(i3.LayoutView)
            offset = tt_lv.heater_offset
            for idx, port in enumerate(opt_ports):
                elec_pos = port.position.move_polar_copy(offset, port.angle + 90.0 * (-1 + 2 * idx))
                elec_name = "wire" if "in" in port.name else "ground_wire"
                taper = DCTaper(
                    name=self.name + "_taper_" + elec_name,
                )
                taper.Layout(
                    in_width=tt_lv.metal_width,
                    out_width=getattr(tt_lv, elec_name + "_width"),
                )
                insts += i3.SRef(
                    reference=taper,
                    name=taper.name,
                    position=elec_pos,
                    transformation=i3.Rotation(rotation=port.angle),
                )
            return insts

        def _generate_ports(self, ports):
            opt_ports = super(TemplatedHeaterWaveguide.Layout, self)._generate_ports(ports)
            ports = PortList()
            ports += opt_ports
            elec_names = ["wire", "ground_wire"]
            ports += i3.expose_ports(
                instances=self.instances,
                port_name_map={self.name + "_taper_" + elec_name + ":out": elec_name for elec_name in elec_names},
            )

            return ports


class HeaterTemplate(WG_TMPL):
    """
    Heater waveguide template which consists of a waveguide template with the heater layer on top.
    To be used with HeaterWaveguide in order to have ElectricalPorts at the start and at the end.
    """

    _templated_class = TemplatedHeaterWaveguide

    class Layout(WG_TMPL.Layout):
        _doc_properties = [
            "core_width",
            "heater_width",
            "heater_offset",
            "metal_width",
            "wire_width",
            "ground_wire_width",
        ]
        heater_width = i3.PositiveNumberProperty(default=30, doc="Width of the Heater path [um]")
        heater_offset = i3.NumberProperty(
            default=0.0,
            doc="Offset of the Heater line relative to the waveguide centerline [um]",
        )
        core_width = i3.NonNegativeNumberProperty(default=1.0, doc="Width of the waveguide [um]")
        metal_width = i3.NonNegativeNumberProperty(default=20.0, doc="Width of the Metal path [um]")
        wire_width = i3.NonNegativeNumberProperty(default=50.0, doc="Width of the Metal path [um]")
        ground_wire_width = i3.NonNegativeNumberProperty(default=100.0, doc="Width of the Metal path [um]")

        def _default_windows(self):
            core_hw = 0.5 * self.core_width
            htr_hw = 0.5 * self.heater_width
            metal_hw = 0.5 * self.metal_width
            htr_offset = self.heater_offset

            windows = []
            if core_hw > 0:
                windows = [
                    i3.PathTraceWindow(
                        layer=i3.TECH.PPLAYER.WG,
                        start_offset=-core_hw,
                        end_offset=core_hw,
                    ),
                ]

            if metal_hw > 0:
                windows.append(
                    i3.PathTraceWindow(
                        layer=i3.TECH.PPLAYER.M1,
                        start_offset=htr_offset - metal_hw,
                        end_offset=htr_offset + metal_hw,
                    )
                )

            windows.append(
                i3.PathTraceWindow(
                    layer=i3.TECH.PPLAYER.HEATER,
                    start_offset=htr_offset - htr_hw,
                    end_offset=htr_offset + htr_hw,
                )
            )
            return windows

        def _default_trace_template_for_ports(self):
            return WG_TMPL(name=self.name + "_fp").Layout(core_width=self.core_width)


class HeaterWaveguide(BaseWaveguide):
    """
    Heater waveguide that uses the HeaterTemplate as trace_template
    and adds electrical ports at the start and at the end.
    """

    _name_prefix = "Heater"

    def _default_trace_template(self):
        return HeaterTemplate()

    class Layout(BaseWaveguide.Layout):
        _doc_properties = [
            "core_width",
            "heater_width",
            "metal_width",
            "heater_offset",
            "bend_radius",
            "shape",
            "wire_width",
            "ground_wire_width",
        ]

        core_width = i3.NonNegativeNumberProperty(default=0.6, doc="Width of the waveguide [um]")
        metal_width = i3.NonNegativeNumberProperty(default=20.0, doc="Width of the Metal path [um]")
        heater_width = i3.PositiveNumberProperty(default=30.0, doc="Width of the Heater path [um]")
        heater_offset = i3.NumberProperty(
            default=0.0,
            doc="Offset of the heater line relative to the waveguide centerline [um]",
        )
        wire_width = i3.NonNegativeNumberProperty(default=50.0, doc="Width of the Metal path [um]")
        ground_wire_width = i3.NonNegativeNumberProperty(default=100.0, doc="Width of the Metal path [um]")

        def _default_trace_template(self):
            lv = self.cell.trace_template.get_default_view(self)
            lv.set(
                core_width=self.core_width,
                heater_width=self.heater_width,
                heater_offset=self.heater_offset,
                metal_width=self.metal_width,
                wire_width=self.wire_width,
                ground_wire_width=self.ground_wire_width,
            )
            return lv

        def _default_shape(self):
            return i3.Shape([i3.Coord2(0.0, 0.0), i3.Coord2(50.0, 0.0)])

        def _generate_ports(self, ports):
            return self.instances["contents"].ports

    class Netlist(i3.NetlistFromLayout):
        pass
