# This code is part of KQCircuits
# Copyright (C) 2022 IQM Finland Oy
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with this program. If not, see
# https://www.gnu.org/licenses/gpl-3.0.html.
#
# The software distribution should follow IQM trademark policy for open-source software
# (meetiqm.com/iqm-open-source-trademark-policy). IQM welcomes contributions to the code.
# Please see our contribution agreements for individuals (meetiqm.com/iqm-individual-contributor-license-agreement)
# and organizations (meetiqm.com/iqm-organization-contributor-license-agreement).


from kqcircuits.util.geometry_helper import bspline_points
from kqcircuits.util.parameters import Param, pdt
from kqcircuits.qubits.double_pads import DoublePads
from kqcircuits.pya_resolver import pya


class DoublePadsSplines(DoublePads):
    """A two-island qubit, consisting of two spline-constructed islands shunted by a junction,
    with one capacitive coupler.

    Contains a coupler on the north edge and two separate qubit islands in the center
    joined by a junction or SQUID loaded from another library.
    Refpoint for a readout line at the opening to the coupler and a modifiable refpoint for
    a driveline outside of the rectangle.
    """

    island_spline = Param(
        pdt.TypeList,
        "Control points of a B-Spline that defines left half of the island",
        [-250, 20, -250, 80, -100, 100, 0, 100],
    )
    island_spline_samples = Param(
        pdt.TypeInt,
        "Number of samples taken from each island spline",
        100,
        docstring=(
            "Number of samples taken from each island spline. "
            "There is a spline for each consequent series of four control points"
        ),
    )

    def _build_island1(self, squid_height, taper_height):
        island1_bottom = self.squid_offset + squid_height / 2
        curve_start = pya.DPoint(0, island1_bottom + taper_height)
        island1_control_points = [curve_start + pya.DPoint(-self.island1_taper_width / 2, 0)] + [
            curve_start + pya.DPoint(float(self.island_spline[idx]), float(self.island_spline[idx + 1]))
            for idx in range(0, len(self.island_spline), 2)
        ]
        island1_control_points += [pya.DPoint(-p.x, p.y) for p in reversed(island1_control_points[:-1])]
        for i, p in enumerate(island1_control_points):
            self.refpoints[f"island1_control_point_{i}"] = p
        island1_polygon = pya.DPolygon(
            bspline_points(island1_control_points, self.island_spline_samples, startpoint=True, endpoint=True)
        )
        island1_region = pya.Region(island1_polygon.to_itype(self.layout.dbu))
        island1_taper = pya.Region(
            pya.DPolygon(
                [
                    # pertrude taper into island in case of precision errors
                    pya.DPoint(self.island1_taper_width / 2, island1_bottom + taper_height + 1),
                    pya.DPoint(self.island1_taper_width / 2, island1_bottom + taper_height),
                    pya.DPoint(self.island1_taper_junction_width / 2, island1_bottom),
                    pya.DPoint(-self.island1_taper_junction_width / 2, island1_bottom),
                    pya.DPoint(-self.island1_taper_width / 2, island1_bottom + taper_height),
                    # pertrude taper into island in case of precision errors
                    pya.DPoint(-self.island1_taper_width / 2, island1_bottom + taper_height + 1),
                ]
            ).to_itype(self.layout.dbu)
        )
        return island1_region + island1_taper

    def _build_island2(self, squid_height, taper_height):
        island2_top = self.squid_offset - squid_height / 2
        curve_start = pya.DPoint(0, island2_top - taper_height)
        island2_control_points = [curve_start + pya.DPoint(-self.island2_taper_width / 2, 0)] + [
            curve_start + pya.DPoint(float(self.island_spline[idx]), -float(self.island_spline[idx + 1]))
            for idx in range(0, len(self.island_spline), 2)
        ]
        island2_control_points += [pya.DPoint(-p.x, p.y) for p in reversed(island2_control_points[:-1])]
        for i, p in enumerate(island2_control_points):
            self.refpoints[f"island2_control_point_{i}"] = p
        island2_polygon = pya.DPolygon(
            bspline_points(island2_control_points, self.island_spline_samples, startpoint=True, endpoint=True)
        )
        island2_region = pya.Region(island2_polygon.to_itype(self.layout.dbu))
        island2_taper = pya.Region(
            pya.DPolygon(
                [
                    # pertrude taper into island in case of precision errors
                    pya.DPoint(self.island2_taper_width / 2, island2_top - taper_height - 1),
                    pya.DPoint(self.island2_taper_width / 2, island2_top - taper_height),
                    pya.DPoint(self.island2_taper_junction_width / 2, island2_top),
                    pya.DPoint(-self.island2_taper_junction_width / 2, island2_top),
                    pya.DPoint(-self.island2_taper_width / 2, island2_top - taper_height),
                    # pertrude taper into island in case of precision errors
                    pya.DPoint(-self.island2_taper_width / 2, island2_top - taper_height - 1),
                ]
            ).to_itype(self.layout.dbu)
        )
        return island2_region + island2_taper
