from geogst.core.geometries.lines import *

from .messages import error_qgis
from .vectors import *


def extract_line_3d_from_qgslinestring_3d(
    linestring: QgsLineString
) -> Ln:

    coords = []

    for pt in linestring:
        coords.append([pt.x, pt.y, pt.z])

    return Ln(coords)


def extract_multiline_3d_from_qgsmultilinestring_3d(
    multilinestring: QgsMultiLineString
) -> MultiLine:

    lines = []

    for line in multilinestring:

        lines.append(
            extract_line_3d_from_qgslinestring_3d(line)
        )

    return MultiLine(lines)


def extract_coords_2d_from_qgslinestring(
    qgslinestring,
) -> List[Tuple[numbers.Real, numbers.Real]]:

    return [(qgspoint.x(), qgspoint.y()) for qgspoint in qgslinestring]


def extract_coords_3d_from_linez_geometry(
    linez_geometry: 'qgs.Geometry',
) -> List[Tuple[numbers.Real, numbers.Real, numbers.Real]]:

    if linez_geometry.isEmpty() or QgsWkbTypes.hasZ(linez_geometry.wkbType()) == False:
        print(f"Geometry does not contain Z coordinates or is not valid")
        return None

    coords_3d = []
    points = linez_geometry.constGet().points()
    for point in points:
        coords_3d.append((point.x(), point.y(), point.z()))

    return coords_3d


def extract_coords_3d_from_multilinez_geometry(
    multilinez_geometry: 'qgs.Geometry',
) -> List[List[Tuple[numbers.Real, numbers.Real, numbers.Real]]]:

    multiline_coords_3d = []
    for linez_geometry in multilinez_geometry:
        line_coords_3d = extract_coords_3d_from_linez_geometry(linez_geometry)
        if line_coords_3d is not None:
            multiline_coords_3d.append(line_coords_3d)

    return multiline_coords_3d


def extract_coords_2d_from_qgsmultilinestring(
    qgsmultilinestring,
) -> List[List[Tuple[numbers.Real, numbers.Real]]]:

    return [extract_coords_2d_from_qgslinestring(qgslinestring) for qgslinestring in qgsmultilinestring]


def extract_coords_2d_with_order_from_selected_features_in_layer_multipart(
    line_layer,
    order_field_ndx: Optional[numbers.Integral] = None
) -> Tuple[bool, Union[str, Tuple[List, List]]]:

    try:

        lines = []
        order_values = []

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

        dummy_progressive = 0

        for feature in features:

            dummy_progressive += 1

            order_val = feature[order_field_ndx] if order_field_ndx is not None else dummy_progressive

            order_values.append(order_val)

            geom = feature.geometry()

            if geom.isMultipart():

                lines.append(
                    (
                        'multiline',
                        extract_coords_2d_from_qgsmultilinestring(geom.asMultiPolyline())  # geom is QVector<QgsPolyline>
                     )
                )
                # now it's a list of list of (x,y) tuples

            else:

                lines.append(
                    (
                        'line',
                        extract_coords_2d_from_qgslinestring(geom.asPolyline())  # geom is QVector<QgsPointXY>
                    )
                )

        return True, (lines, order_values)

    except Exception as e:

        return False, f"{e!r}"


def extract_coords_3d_with_fid_from_selected_features_in_layer_multipart(
    line_layer,
) -> Union[str, List]:

    try:

        lines = []

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

        for feature in features:

            fid = feature.id()
            geom = feature.geometry()

            if geom.isMultipart():

                lines.append(
                    (
                        fid,
                        'multiline',
                        extract_coords_3d_from_multilinez_geometry(geom)
                     )
                )
                # now it's a list of list of (x,y,z) tuples

            else:

                lines.append(
                    (
                        fid,
                        'line',
                        extract_coords_3d_from_linez_geometry(geom)
                    )
                )

        return lines

    except Exception as e:

        return f"{e!r}"


def extract_selected_lines_2d_with_attributes_from_layer_multipart(
    line_layer,
    field_list=None
) -> List[List]:

    if field_list is None:
        field_list = []

    lines = []

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

    provider = line_layer.dataProvider()
    field_indices = [provider.fieldNameIndex(field_name) for field_name in field_list]

    for feature in features:
        geom = feature.geometry()
        if geom.isMultipart():
            rec_geom = extract_coords_2d_from_qgsmultilinestring(geom.asMultiPolyline())
        else:
            rec_geom = [extract_coords_2d_from_qgslinestring(geom.asPolyline())]

        attrs = feature.fields().toList()
        rec_data = [str(feature.attribute(attrs[field_ndx].name())) for field_ndx in field_indices]

        lines.append([rec_geom, rec_data])

    return lines


def extract_line_23d_from_qgslinestring_23d(
    line: QgsLineString
) -> Tuple[bool, Union[str, Ln]]:

    try:

        coords = []

        dim = set()

        for point in line.points():

            if np.isfinite(point.z()):
                coords.append([point.x(), point.y(), point.z()])
                dim.add(3)
            else:
                coords.append([point.x(), point.y()])
                dim.add(2)

        if len(dim) != 1:
            return False, f"Dimension variety is {len(dim)} (should be 1)"

        return True, Ln(coords)

    except Exception as e:

        return False, f"{e!r}"


def extract_lines_23d_from_qgslinestring_multipart(
    qgis_geometry,
) -> Union[None, List[Ln]]:
    """
    Extract geometry as lines list and attributes from line layer.
    """

    try:

        lns = []

        if not qgis_geometry.isMultipart():

            success, result = extract_line_23d_from_qgslinestring_23d(qgis_geometry)

            if not success:
                msg = result
                return None, Error(
                    True,
                    caller_name(),
                    Exception(msg),
                    traceback.format_exc())

            ln = result

            lns.append(ln)

        else:

            for subline in qgis_geometry.parts():

                success, result = extract_line_23d_from_qgslinestring_23d(subline)

                if not success:
                    msg = result
                    return None, Error(
                        True,
                        caller_name(),
                        Exception(msg),
                        traceback.format_exc()
                    )

                ln = result

                lns.append(ln)

        return lns

    except Exception as e:

        err = Error(
            True,
            caller_name(),
            e,
            traceback.format_exc()
        )

        error_qgis(
            "Line extraction failed",
            f"{err!r}",
        )

        return None

