# -*- coding: utf-8 -*-argument 1 has unexpected type 'sip.wrappertype'
from qgis.core import (
    QgsCoordinateReferenceSystem,
    QgsLayerTree,
    QgsLayerTreeModel,
    QgsPointXY,
    QgsProject,
    QgsRasterLayer,
    QgsRectangle,
    QgsVectorLayer,
    QgsWkbTypes,
)
from qgis.gui import (
    QgsLayerTreeMapCanvasBridge,
    QgsMapToolEmitPoint,
    QgsMapToolIdentify,
    QgsMapToolPan,
    QgsRubberBand,
)
from qgis.PyQt import QtGui
from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtWidgets import QAction, QFileDialog, QMenu, QTreeView

from kfrm_tool.include.canvas.layer_identification import LayerIdentification
from kfrm_tool.include.file_export import FileExport
from kfrm_tool.include.Util import MsInfo


class CanvasTools(QgsMapToolEmitPoint):
    def __init__(self, canvas, layerTree=QTreeView(), layerName=""):
        QgsMapToolEmitPoint.__init__(self, canvas)
        self.canvas = canvas
        self.canvas.enableMapTileRendering(True)  # 타일 형태로 rendering
        self.canvas.setParallelRenderingEnabled(True)  # 병렬로 rendering
        self.canvas.setPreviewJobsEnabled(
            True
        )  # 지도 밖 일부 랜더링 여부 / 속도에 영향을 주는것 같아 보이지는 않음.

        self.layerTree = layerTree
        self.selecteLayer = layerName

        global BASE_LAYER
        BASE_LAYER = "OSM"

        self.layerList = []
        self.r = None
        self.rubberBand = QgsRubberBand(self.canvas, True)
        self.rubberBand.setWidth(1)
        self.reset()

        self.layerLegend()

        self.canvas.setContextMenuPolicy(Qt.CustomContextMenu)
        self.canvas.customContextMenuRequested.connect(self.canvasOpenContextMenu)
        self.layerTree.setContextMenuPolicy(Qt.CustomContextMenu)
        self.layerTree.customContextMenuRequested.connect(self.layerTreeOpenContextMenu)

    def canvasOpenContextMenu(self, position):
        try:
            menu = QMenu()
            menuList = ["View selected information"]
            actionList = ["viewLayerInfo"]

            for i in range(1):
                exec(f"{actionList[i]} = menu.addAction(self.tr(menuList[i]))")
                exec(f"{actionList[i]}.triggered.connect(self.{actionList[i]})")

            selectAction = menu.exec_(
                self.canvas.viewport().mapToGlobal(position)
            )  # 메뉴 실행 및 선택 값
        except Exception as e:
            print(e)

    def layerTreeOpenContextMenu(self, position):
        try:
            menu = QMenu()
            menuList = ["Zoom to select layer", "Layer download"]
            actionList = ["zoomToSelected", "layerDownload"]

            layerName = self.layerTree.indexAt(position).data()
            if layerName:
                layer = QgsProject.instance().mapLayersByName(layerName)[0]
                menuCount = len(menuList)
                for i in range(menuCount):
                    exec(f"{actionList[i]} = menu.addAction(self.tr(menuList[i]))")
                    exec(
                        f"{actionList[i]}.triggered.connect(partial(self.{actionList[i]}, layer))"
                    )  # lambda 사용시 에러

                exec(
                    f"{actionList[-1]}.setEnabled(bool(layerName!='KRM_BaseMap_'+BASE_LAYER))"
                )
                selectAction = menu.exec_(
                    self.layerTree.viewport().mapToGlobal(position)
                )  # 메뉴 실행 및 선택 값

        except Exception as e:
            print(e)

    def viewLayerInfo(self):
        self.layerInfo = LayerIdentification(self.layerList)

    def layerDownload(self, layer):
        savePath = QFileDialog.getSaveFileName(None, "Save layer", "", "SHP (*.shp)")[0]
        if savePath:
            rst = FileExport().dataExporToShape(savePath, layer.name(), overlap=False)
            MsInfo("The download is complete.")

    def layerLegend(self):
        self.layerRoot = QgsLayerTree()
        self.bridge = QgsLayerTreeMapCanvasBridge(self.layerRoot, self.canvas)
        self.bridge.setAutoSetupOnFirstLayer(False)
        model = QgsLayerTreeModel(self.layerRoot)
        model.setFlag(QgsLayerTreeModel.AllowNodeReorder)
        model.setFlag(QgsLayerTreeModel.AllowNodeRename)
        model.setFlag(QgsLayerTreeModel.AllowNodeChangeVisibility)
        model.setFlag(QgsLayerTreeModel.ShowLegend)
        self.layerTree.setModel(model)

    def reset(self):
        self.startPoint = self.endPoint = None
        self.isEmittingPoint = False
        self.rubberBand.reset(True)

    def canvasPressEvent(self, e):
        self.startPoint = self.toMapCoordinates(e.pos())
        self.endPoint = self.startPoint
        self.isEmittingPoint = True
        self.showRect(self.startPoint, self.endPoint)

    def canvasMoveEvent(self, e):
        if not self.isEmittingPoint:
            return

        self.endPoint = self.toMapCoordinates(e.pos())
        self.showRect(self.startPoint, self.endPoint)

    def showRect(self, startPoint, endPoint):
        self.rubberBand.reset(QgsWkbTypes.PolygonGeometry)
        if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y():
            return

        point1 = QgsPointXY(startPoint.x(), startPoint.y())
        point2 = QgsPointXY(startPoint.x(), endPoint.y())
        point3 = QgsPointXY(endPoint.x(), endPoint.y())
        point4 = QgsPointXY(endPoint.x(), startPoint.y())

        self.rubberBand.addPoint(point1, False)
        self.rubberBand.addPoint(point2, False)
        self.rubberBand.addPoint(point3, False)
        self.rubberBand.addPoint(point4, True)  # true to update canvas
        self.rubberBand.show()

    def rectangle(self):
        if self.startPoint is None or self.endPoint is None:
            return None
        elif (
            self.startPoint.x() == self.endPoint.x()
            or self.startPoint.y() == self.endPoint.y()
        ):
            return None

        return QgsRectangle(self.startPoint, self.endPoint)

    def canvasReleaseEvent(self, e):
        self.isEmittingPoint = False
        r = self.rectangle()
        self.layerList = [
            l for l in self.canvas.layers() if l.type() == 0
        ]  # 맨 첫번째(맨 뒤에)에 해당하는 레이어

        if self.layerList:  # 레이어가 비어있지 않다면
            feature = QgsMapToolIdentify(self.canvas).identify(
                e.x(), e.y(), self.layerList, QgsMapToolIdentify.DefaultQgsSetting
            )
            if e.button() == Qt.RightButton:  # 우클릭일 경우(다중 선택)
                return

            selection = QgsVectorLayer.SetSelection  # 옵션을 일반 선택 값으로 변경

            if r != None:  # 드래그 옵션 값이 있다면(드래그로 선택)
                self.r = r
                for l in self.layerList:
                    r = self.toLayerCoordinates(l, self.r)
                    l.selectByRect(r)

            elif feature:  # 드래그 하지 않은 상태에서 값을 선택했다면
                self.r = None
                for f in feature:
                    f.mLayer.selectByIds([f.mFeature.id()], selection)

            else:  # 클릭한 영역이 아무것도 없는 공간이라면
                self.r = None
                for layer in self.layerList:
                    layer.removeSelection()  # 선택된 객체 전부 삭제

        self.reset()

    # canvas event
    def zoomToExtent(self):
        self.canvas.zoomToFullExtent()
        self.canvas.refresh()

    def zoomToSelected(self, layer):
        bfExtent = layer.extent()
        point1 = self.toMapCoordinates(
            layer, QgsPointXY(bfExtent.xMinimum(), bfExtent.yMinimum())
        )
        point2 = self.toMapCoordinates(
            layer, QgsPointXY(bfExtent.xMaximum(), bfExtent.yMaximum())
        )
        afExtent = QgsRectangle(point1, point2)

        self.canvas.zoomToFeatureExtent(afExtent)
        self.canvas.refresh()

    def canvasPan(self):
        actionPan = QAction(self)
        self.toolPan = QgsMapToolPan(self.canvas)
        self.toolPan.setAction(actionPan)
        self.canvas.setMapTool(self.toolPan)

    def identify(self):
        self.canvas.setMapTool(self)  # setMapTool(self) 이게 있어야 canvasPressEvent가 활성화 됨.
        self.canvas.setCursor(QtGui.QCursor(Qt.PointingHandCursor))

    def getGeometry(self):
        if self.r != None:
            return self.r.asWktPolygon()

    def getFeature(self, layer):
        return layer.selectedFeatures()

    def addLayer(self, newLayer, index=1):
        existingLayers = self.canvas.layers()
        QgsProject.instance().addMapLayer(newLayer, False)

        if len(existingLayers) > 1:
            self.canvas.setLayers(existingLayers + [newLayer])
            self.layerRoot.insertLayer(index, newLayer)
        else:
            self.canvas.setLayers([newLayer])
            self.layerRoot.addLayer(newLayer)

    def setLayer(self, layer):
        self.exsitingRemoveAllLayers()
        #         srid = layer.sourceCrs().authid().split(":")[1]
        CRS = QgsCoordinateReferenceSystem(
            3857, QgsCoordinateReferenceSystem.PostgisCrsId
        )
        self.canvas.setDestinationCrs(CRS)

        basemapLayer = self.getBasemap(BASE_LAYER)
        layerList = [layer, basemapLayer]
        QgsProject.instance().addMapLayers(layerList, False)
        self.canvas.setLayers(layerList)

        self.canvas.setExtent(layer.extent())
        self.zoomToSelected(layer)

        for l in layerList:
            self.layerRoot.addLayer(l)

    def changeLayer(self, insertLayer, rmLayerName, rename):
        removeLayer = QgsProject.instance().mapLayersByName(rmLayerName)
        covertLayer = None
        if not removeLayer:
            removeLayer = QgsProject.instance().mapLayersByName(rename)
            rmLayerName = rename

        for lyr in removeLayer:
            covertLayer = self.layerRoot.findLayer(lyr)
            if covertLayer:
                break

        #         if not covertLayer:
        #             raise Warning(f"The {rmLayerName} layer does not exist. \nPlease redo the previous operation or run the program again after exiting.")

        layers = self.layerRoot.findLayers()
        index = layers.index(covertLayer) if covertLayer else 0

        self.existingRemoveLayer(rmLayerName, False)
        self.existingRemoveLayer(rename, False)

        layers = self.canvas.layers()
        QgsProject.instance().addMapLayer(insertLayer, False)
        self.canvas.setLayers(layers + [insertLayer])
        self.layerRoot.insertLayer(index, insertLayer)

    def existingRemoveLayer(self, layerName, flag=True):
        existsMap = QgsProject.instance().mapLayersByName(layerName)
        if existsMap:
            if flag:  # 하나만 삭제
                self.layerRoot.removeLayer(existsMap[0])
                QgsProject.instance().removeMapLayer(existsMap[0])
            else:  # 전체 삭제
                for layer in existsMap:
                    try:
                        self.layerRoot.removeLayer(layer)
                        QgsProject.instance().removeMapLayer(layer)
                    except Exception:
                        continue

    def exsitingRemoveAllLayers(self):
        layers = self.canvas.layers()
        if layers:
            idList = [l.id() for l in layers]
            self.layerRoot.clear()
            QgsProject.instance().removeMapLayers(idList)

    def getBasemap(self, kind):
        url = ""
        if kind == "OSM":
            url = "type=xyz&url=http://a.tile.openstreetmap.org/{z}/{x}/{y}.png"

        elif kind == "OSM Wmflabs":
            url = "type=xyz&url=http://tiles.wmflabs.org/osm-no-labels/{z}/{x}/{y}.png"

        elif kind == "Google basic":
            url = "type=xyz&url=https://mt1.google.com/vt/lyrs%3Dr%26x%3D{x}%26y%3D{y}%26z%3D{z}"

        elif kind == "Google hybrid":
            url = "type=xyz&url=https://mt1.google.com/vt/lyrs%3Dy%26x%3D{x}%26y%3D{y}%26z%3D{z}"

        elif kind == "Google satellite":
            url = "type=xyz&url=https://mt1.google.com/vt/lyrs%3Ds%26x%3D{x}%26y%3D{y}%26z%3D{z}"

        elif kind == "Google streets":
            url = "type=xyz&url=https://mt1.google.com/vt/lyrs%3Dm%26x%3D{x}%26y%3D{y}%26z%3D{z}"

        elif kind == "Carto Voyager":
            url = "type=xyz&url=https://a.basemaps.cartocdn.com/rastertiles/voyager_nolabels/ {z}/{x}/{y}@2x.png"
        basemapLayer = QgsRasterLayer(url, "KRM_BaseMap_" + kind, "wms")
        return basemapLayer
