import numbers

import qgis.core
from qgis.core import QgsMessageLog
from qgis.core import Qgis

from geogst.core.geometries.shapes.polygons import *


def has_usable_geometry(
    geometry
) -> bool:
    """
    Whether a geometry holds something to read, i.e. it is neither missing,
    nor null, nor empty. Reading the parts of the others either raises or,
    worse, yields undefined coordinates.
    """

    return geometry is not None and not geometry.isNull() and not geometry.isEmpty()


def qgismpolygon_to_polygons(
    geometry: qgis.core.QgsPolygon
) -> List[Polygon]:

    polygons = []

    if not has_usable_geometry(geometry):
        return polygons

    for ndx, part in enumerate(geometry.constParts()):

        outer_ring_coordinates = []

        outer_ring = part.exteriorRing() if hasattr(part, "exteriorRing") else None

        # an empty polygon has a part without any ring: reading its vertices
        # would raise and so discard every polygon of the layer

        if outer_ring is None:
            continue

        for vert in outer_ring.vertices():
            outer_ring_coordinates.append((vert.x(), vert.y()))

        if len(outer_ring_coordinates) < 2:
            continue

        outer_ring_line = Ln(outer_ring_coordinates)

        num_rings = part.numInteriorRings()

        if num_rings == 0:

            inner_ring_lines = None

        else:

            inner_ring_lines =[]

            for ring_ndx in range(num_rings):

                inner_ring_coords = []

                inner_ring = part.interiorRing(ring_ndx)
                for vert in inner_ring.vertices():
                    inner_ring_coords.append((vert.x(), vert.y()))

                inner_ring_lines.append(Ln(inner_ring_coords))

        polygons.append(Polygon(outer_ring_line, inner_ring_lines))

    return polygons


def qgismpolygon_to_xy_list(
    geometry: qgis.core.QgsPolygon
) -> List[Tuple[numbers.Real, numbers.Real]]:

    xy_coords = []

    if not has_usable_geometry(geometry):
        return xy_coords

    for ndx, part in enumerate(geometry.constParts()):

        outer_ring = part.exteriorRing() if hasattr(part, "exteriorRing") else None

        if outer_ring is None:
            continue

        for vert in outer_ring.vertices():
            xy_coords.append((vert.x(), vert.y()))

        num_rings = part.numInteriorRings()

        if num_rings > 0:

            for ring_ndx in range(num_rings):

                inner_ring = part.interiorRing(ring_ndx)

                for vert in inner_ring.vertices():
                    xy_coords.append((vert.x(), vert.y()))

    return xy_coords


def polygon_geometries(
    layer,
) -> List[List[Tuple[numbers.Real, numbers.Real]]]:

    xy_coords = []

    if layer.selectedFeatureCount() > 0:
        features = layer.selectedFeatures()
    else:
        features = layer.getFeatures()

    for feature in features:

        geom = feature.geometry()

        if not has_usable_geometry(geom):

            QgsMessageLog.logMessage(
                f"Feature {feature.id()} has null or empty geometry, skipping",
                "qgSurf",
                Qgis.MessageLevel.Warning
            )
            continue

        xy_pairs = qgismpolygon_to_xy_list(
            geometry=geom)

        if not xy_pairs:
            continue

        xy_coords.append(xy_pairs)

    return xy_coords
