
from gst.qgis.points import *
from gst.qgis.vectors import *


def polyline_to_xytuple_list(
        qgsline
):

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


def multipolyline_to_xytuple_list2(
        qgspolyline
):

    return [polyline_to_xytuple_list(qgsline) for qgsline in qgspolyline]


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

    try:

        success, result = try_line_geoms_with_field_infos(
            line_shape,
            order_field_ndx
        )

        if not success:
            msg = result
            return False, msg

        profile_orig_lines, order_values = result

        return True, (profile_orig_lines, order_values)

    except Exception as e:

        return False, f"{e!r}"


def project_line2d(
    src_line2d: Ln,
    src_crs: QgsCoordinateReferenceSystem,
    dest_crs: QgsCoordinateReferenceSystem
) -> Ln:

    coords = []

    for pt in src_line2d.pts():
        projected_pt = project_point(
            pt=pt,
            srcCrs=src_crs,
            destCrs=dest_crs
        )
        coords.append([projected_pt.x, projected_pt.y])

    return Ln(coords)


def try_line_geoms_with_field_infos(
    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',
                        multipolyline_to_xytuple_list2(geom.asMultiPolyline())  # geom is QVector<QgsPolyline>
                     )
                )
                # now it's a list of list of (x,y) tuples

            else:

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

        return True, (lines, order_values)

    except Exception as e:

        return False, f"{e!r}"


def line_geoms_attrs(
    line_layer,
    field_list=None
):
    """
    Deprecated: use 'try_line_geoms_attrs'
    """

    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 = multipolyline_to_xytuple_list2(geom.asMultiPolyline())
        else:
            rec_geom = [polyline_to_xytuple_list(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 project_xy_list(
    src_crs_xy_list,
    srcCrs,
    destCrs
):

    pt_list_dest_crs = []
    for x, y in src_crs_xy_list.pts:
        srcPt = QgsPointXY(x, y)
        destPt = project_qgs_point(srcPt, srcCrs, destCrs)
        pt_list_dest_crs = pt_list_dest_crs.append([destPt.x(), destPt.y()])

    return pt_list_dest_crs


def try_convert_line(
        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 try_extract_lines_from_qgis_layer(
        layer: QgsVectorLayer
) -> Tuple[bool, Union[str, List[Tuple]]]:
    """
    Extract geometry as line and attributes from line layer.
    """

    try:

        success, result = try_geoms_attrs(
            layer=layer,
        )

        if not success:
            msg = result
            return False, msg

        records = result

        lines_infos = []

        for line, *res in records:

            if not line.isMultipart():

                success, result = try_convert_line(line)

                if not success:
                    msg = result
                    return False, msg

                ln = result

                new_rec = (ln, *res)

                lines_infos.append((ln, *res))

            else:

                for subline in line.parts():
                    success, result = try_convert_line(subline)

                    if not success:
                        msg = result
                        return False, msg

                    ln = result

                    lines_infos.append((ln, *res))

        return True, lines_infos

    except Exception as e:

        return False, f"{e!r}"


def convert_line_from_qgis(
        qgis_line
) -> Tuple[Union[type(None), List[Ln]], Error]:
    """
    Extract geometry as line and attributes from line layer.
    """

    try:

        lns = []

        if not qgis_line.isMultipart():

            success, result = try_convert_line(qgis_line)

            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_line.parts():

                success, result = try_convert_line(subline)

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

                ln = result

                lns.append(ln)

        return lns, Error()

    except Exception as e:

        return None, Error(
            True,
            caller_name(),
            e,
            traceback.format_exc()
        )

