
from typing import Union, List, Tuple

from qgis.core import *
from qgis.core import Qgis
from qgis.core import QgsMessageLog
#from qgis._core import QgsCoordinateReferenceSystem, QgsPointXY

from .points import *
from geogst.core.geometries.general.lines import *


def get_layer_crs_as_wtk(
    layer,
) -> Union[None, str]:

    try:
        return layer.crs().toWkt()
    except Exception as e:
        QgsMessageLog.logMessage(
            f"Error getting layer CRS as WKT: {e!r}",
            "qgSurf",
            Qgis.MessageLevel.Warning
        )
        return None


def get_crs_as_proj4_string(layer) -> Union[None, str]:

    try:

        return layer.crs().toProj4()

    except Exception as e:

        QgsMessageLog.logMessage(
            f"Error getting CRS as proj4 string: {e!r}",
            "qgSurf",
            Qgis.MessageLevel.Warning
        )
        return None


def qgis_project_3d_coords_list(
    coords_3d: List[Tuple[float, float, float]],
    src_crs: QgsCoordinateReferenceSystem = None,
    dest_crs:Optional[QgsCoordinateReferenceSystem] = None,
) -> Union[Error, List[Tuple[float, float, float]]]:

    try:

        projected_coords_list = []

        for x, y, z in coords_3d:

            result = qgis_project_3d_coords(
                x,
                y,
                z,
                src_crs,
                dest_crs,
            )

            if isinstance(result, Error):
                return result

            projected_coords_list.append(result)

        return projected_coords_list

    except Exception as e:

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


def qgis_project_3d_coords(
        x: float,
        y: float,
        z: float,
        src_crs: QgsCoordinateReferenceSystem = None,
        dest_crs:Optional[QgsCoordinateReferenceSystem] = None
) -> Union[Error, Tuple[float, float, float]]:
    """
    Project a triplet of x-y-z coordinates to a new projection.
    If the source/destination CRS is not provided, it will be set to EPSG 4236 (WGS-84).

    :param x: the x coordinate.
    :param y: the y coordinate.
    :param z: the z coordinate.
    :param src_crs: the source coordinate.
    :param dest_crs: the destination coordinate.
    :return: the projected x-y-z coordinates or error..
    """

    try:

        if not src_crs:
            src_crs = QgsCoordinateReferenceSystem("EPSG:4326")

        if not dest_crs:
            dest_crs = QgsCoordinateReferenceSystem("EPSG:4326")

        context = QgsCoordinateTransformContext()
        context.addCoordinateOperation(
            src_crs,
            dest_crs,
            "",
        )

        coordinate_transform = QgsCoordinateTransform(
            src_crs,
            dest_crs,
            context)

        qgs_pt = coordinate_transform.transform(
            x,
            y)

        x, y = qgs_pt.x(), qgs_pt.y()

        return x, y, z

    except Exception as e:

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


def qgis_project_coords(
    x: float,
    y: float,
    src_crs: QgsCoordinateReferenceSystem = None,
    dest_crs:Optional[QgsCoordinateReferenceSystem] = None
) -> Tuple[Union[None, Tuple[float, float]], Error]:
    """
    Project a pair of x-y coordinates to a new projection.
    If the source/destination CRS is not provided, it will be set to EPSG 4236 (WGS-84).

    :param x: the x coordinate.
    :param y: the y coordinate.
    :param src_crs: the source coordinate.
    :param dest_crs: the destination coordinate.
    :return: the projected x-y coordinates.
    """

    try:

        if not src_crs:
            src_crs = QgsCoordinateReferenceSystem("EPSG:4326")

        if not dest_crs:
            dest_crs = QgsCoordinateReferenceSystem("EPSG:4326")

        context = QgsCoordinateTransformContext()
        context.addCoordinateOperation(
            src_crs,
            dest_crs,
            "",
        )

        coordinate_transform = QgsCoordinateTransform(
            src_crs,
            dest_crs,
            context)

        qgs_pt = coordinate_transform.transform(
            x,
            y)

        x, y = qgs_pt.x(), qgs_pt.y()

        return (x, y), Error()

    except Exception as e:

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


def project_qgs_point(
    qgsPt,
    srcCrs,
    destCrs
):

    return QgsCoordinateTransform(
        srcCrs,
        destCrs,
        QgsProject.instance()
    ).transform(qgsPt)


def project_line_2d(
    src_line_2d: Ln,
    src_crs: QgsCoordinateReferenceSystem,
    dest_crs: QgsCoordinateReferenceSystem
) -> Union[None, Ln]:

    try:

        coords = []

        for pt in src_line_2d.pts():
            projected_pt = project_point(
                pt=pt,
                srcCrs=src_crs,
                destCrs=dest_crs
            )

            coords.append([projected_pt.x, projected_pt.y])

        return Ln(coords)

    except Exception as e:

        QgsMessageLog.logMessage(
            f"Error in qgis_project_coords: {e!r}",
            "qgSurf",
            Qgis.MessageLevel.Warning
        )
        return None


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
