# -*- coding: utf-8 -*-
"""
/***************************************************************************
 CIGeoETranslateToFitToAdjacentPolygon
                                 A QGIS plugin
 Do a polygon translation to the nearest polygon, by making it coincide their nearest vertices
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2019-02-06
        git sha              : $Format:%H$
        copyright            : (C) 2019 by Centro de Informação Geoespacial do Exército 
        email                : igeoe@igeoe.pt
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
"""
from PyQt5.QtCore import QSettings, QTranslator, qVersion, QCoreApplication, Qt, QObject, QPoint
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QAction, QToolBar, QMessageBox 

from qgis.gui import *
#from qgis.gui import QgsMessageBar, QgsMapToolEmitPoint, QgsMapToolAdvancedDigitizing
from qgis.core import *


# Initialize Qt resources from file resources.py
from .resources import *

# Import the code for the DockWidget
from .CIGeoE_Translate_To_Fit_To_Adjacent_Polygon_dockwidget import CIGeoETranslateToFitToAdjacentPolygonDockWidget
import os.path


class CIGeoETranslateToFitToAdjacentPolygon:
    """QGIS Plugin Implementation."""

    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        # Save reference to the QGIS interface
        self.iface = iface

        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)

        # initialize locale
        locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(
            self.plugin_dir,
            'i18n',
            'CIGeoETranslateToFitToAdjacentPolygon_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)

            if qVersion() > '4.3.3':
                QCoreApplication.installTranslator(self.translator)

        # Declare instance attributes
        self.actions = []
        self.menu = self.tr(u'&CIGeoE Translate To Fit To Adjacent Polygon')
        # TODO: We are going to let the user set this up in a future iteration

        # Check for CIGeoE toolbar. If exists, add button there; if not exists, create one
        cigeoeToolBarExists = False
        for x in iface.mainWindow().findChildren(QToolBar): 
            if x.windowTitle() == 'CIGeoE':
                self.toolbar = x
                cigeoeToolBarExists = True
        if cigeoeToolBarExists==False:
            self.toolbar = self.iface.addToolBar(u'CIGeoE')
                
        self.toolbar.setObjectName(u'CIGeoETranslateToFitToAdjacentPolygon')

        #print "** INITIALIZING CIGeoETranslateToFitToAdjacentPolygon"

        self.pluginIsActive = False
        self.dockwidget = None
        #self.selectedFeaturesMap={}
        #self.iface.mapCanvas().selectionChanged.connect(self.mapSelectionChanged)


    # noinspection PyMethodMayBeStatic
    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('CIGeoETranslateToFitToAdjacentPolygon', message)


    def add_action(
        self,
        icon_path,
        text,
        callback,
        enabled_flag=True,
        add_to_menu=True,
        add_to_toolbar=True,
        status_tip=None,
        whats_this=None,
        parent=None):
        """Add a toolbar icon to the toolbar.

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to show in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        """

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            self.toolbar.addAction(action)

        if add_to_menu:
            self.iface.addPluginToMenu(
                self.menu,
                action)

        self.actions.append(action)

        return action


    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""

        icon_path = ':/plugins/CIGeoE_Translate_To_Fit_To_Adjacent_Polygon/icon.png'
        self.add_action(
            icon_path,
            text=self.tr(u'CIGeoE: Translate To Fit To Adjacent Polygon'),
            callback=self.run,
            parent=self.iface.mainWindow())

    #--------------------------------------------------------------------------

    def onClosePlugin(self):
        """Cleanup necessary items here when plugin dockwidget is closed"""

        #print "** CLOSING CIGeoETranslateToFitToAdjacentPolygon"

        # disconnects
        self.dockwidget.closingPlugin.disconnect(self.onClosePlugin)

        # remove this statement if dockwidget is to remain
        # for reuse if plugin is reopened
        # Commented next statement since it causes QGIS crashe
        # when closing the docked window:
        # self.dockwidget = None

        self.pluginIsActive = False

        #remove click on canvas
        self.iface.mapCanvas().unsetMapTool(self.pointEmitter)
        #QObject.disconnect( self.pointEmitter, SIGNAL("canvasClicked(const QgsPoint, Qt::MouseButton)"), self.selectNow)     # pyqgis2
        self.pointEmitter.canvasClicked.disconnect(self.selectNow)
        #remove vertex marker
        self.iface.mapCanvas().scene().removeItem(self.vertex_marker)



    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""

        #print "** UNLOAD CIGeoETranslateToFitToAdjacentPolygon"

        for action in self.actions:
            self.iface.removePluginMenu(
                self.tr(u'&CIGeoE Translate To Fit To Adjacent Polygon'),
                action)
            self.iface.removeToolBarIcon(action)
        # remove the toolbar
        del self.toolbar

    #--------------------------------------------------------------------------

    def run(self):
        """Run method that loads and starts the plugin"""

        #*************************************************************

        layers = QgsProject.instance().layerTreeRoot().layerOrder()     
        
        if not layers:        
            QMessageBox.information(self.iface.mainWindow(), "Error", 'Layer is not loaded!')            
            return 

        #*************************************************************

        if not self.pluginIsActive:
            self.pluginIsActive = True

            #print "** STARTING CIGeoETranslateToFitToAdjacentPolygon"

            # dockwidget may not exist if:
            #    first run of plugin
            #    removed on close (see self.onClosePlugin method)
            if self.dockwidget == None:
                # Create the dockwidget (after translation) and keep reference
                self.dockwidget = CIGeoETranslateToFitToAdjacentPolygonDockWidget()

            # connect to provide cleanup on closing of dockwidget
            self.dockwidget.closingPlugin.connect(self.onClosePlugin)

            # show the dockwidget
            # TODO: fix to allow choice of dock location
            self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.dockwidget)

            #setup click on canvas
            self.pointEmitter = QgsMapToolEmitPoint(self.iface.mapCanvas())
            #QObject.connect( self.pointEmitter, SIGNAL("canvasClicked(const QgsPoint, Qt::MouseButton)"), self.selectNow)      # pyqgis2
            self.pointEmitter.canvasClicked.connect(self.selectNow)
            self.iface.mapCanvas().setMapTool( self.pointEmitter )
            #setup initial label value
            self.dockwidget.label.setText("Select first vertex.")
            #setup initial first vertex ref
            self.firstVertexLayer=None
            self.firstVertexFeatureId=None
            self.firstVertexIndex=None
            #vertex marker
            self.vertex_marker = QgsVertexMarker(self.iface.mapCanvas())
            self.vertex_marker.setIconType(QgsVertexMarker.ICON_BOX)
            self.vertex_marker.setColor(Qt.red)
            self.vertex_marker.setPenWidth(2)
            self.vertex_marker.setVisible(False)


            self.dockwidget.show()
        else:
            self.iface.removeDockWidget(self.dockwidget)
            self.onClosePlugin()

    def selectNow(self, point, button):
        #QMessageBox.information(None, "Clicked coords", " x: " + str(point.x()) + " Y: " + str(point.y()) )
        #QMessageBox.information(None, "Clicked coords", " x: " + str(point) )
        #QMessageBox.information(None, "Clicked coords", " x: " + str(button) )
        if button == Qt.LeftButton:
            #map_point = QgsMapToolAdvancedDigitizing.toMapCoordinates(QPoint(point.x(), point.y()))
            tol = QgsTolerance.vertexSearchRadius(self.iface.mapCanvas().mapSettings())
            snap_type = QgsPointLocator.Type(QgsPointLocator.Vertex)
            snap_layers = []
            for layer in self.iface.mapCanvas().layers():
                if not isinstance(layer, QgsVectorLayer): #or not layer.isEditable():
                    continue
                snap_layers.append(QgsSnappingUtils.LayerConfig(layer, snap_type, tol, QgsTolerance.ProjectUnits))      
            snap_util = self.iface.mapCanvas().snappingUtils()     

            """           
            old_layers = snap_util.layers()  
            old_mode = snap_util.snapToMapMode()       # AttributeError: 'QgsSnappingUtils' object has no attribute 'snapToMapMode'       
            old_intersections = snap_util.snapOnIntersections()
            snap_util.setLayers(snap_layers)
            snap_util.setSnapToMapMode(QgsSnappingUtils.SnapAdvanced)
            snap_util.setSnapOnIntersections(False)  # only snap to layers      
            """
            #m = snap_util.snapToMap(map_point)
            m = snap_util.snapToMap(point)
            """
            snap_util.setLayers(old_layers)
            snap_util.setSnapToMapMode(old_mode)           
            snap_util.setSnapOnIntersections(old_intersections)

            """                    
            # possibility to move a node
            if m.type() == QgsPointLocator.Vertex:
                request = QgsFeatureRequest().setFilterFid(m.featureId())
                #feat = m.layer().getFeatures(request).next()                      # pyqgis2
                for feat in m.layer().getFeatures(request):
                    featx = feat.geometry()
                    
                #if feat.geometry().type()!=QGis.Polygon:                          # pyqgis2  
                if feat.geometry().type()!=QgsWkbTypes.PolygonGeometry:  
                    QMessageBox.information(self.iface.mainWindow(), "Error", 'This plugin only works with polygon features.')
                else:
                    if self.firstVertexIndex==None:
                        #save first vertex values
                        self.firstVertexLayer=m.layer()
                        self.firstVertexFeatureId=m.featureId()
                        self.firstVertexIndex=m.vertexIndex()
                        #update vertex marker
                        self.vertex_marker.setCenter(m.point())
                        self.vertex_marker.setVisible(True)
                        self.vertex_marker.update()
                        #update label text
                        self.dockwidget.label.setText("Select second vertex.")
                    else:
                        #save second vertex values
                        self.secondVertexLayer=m.layer()
                        self.secondVertexFeatureId=m.featureId()
                        self.secondVertexIndex=m.vertexIndex()
                        #break if is a vertex of same polygon
                        if self.firstVertexLayer==self.secondVertexLayer and self.firstVertexFeatureId==self.secondVertexFeatureId:
                            QMessageBox.information(self.iface.mainWindow(), "Error", 'That vertex belongs to same polygon!')
                        else:
                            #translate polygon
                            self.translatePolygon(self.firstVertexLayer, self.firstVertexFeatureId, self.firstVertexIndex, self.secondVertexLayer, self.secondVertexFeatureId, self.secondVertexIndex)
                            
                        #reset first vertex values
                        self.firstVertexLayer=None
                        self.firstVertexFeatureId=None
                        self.firstVertexIndex=None
                        #remove vertex marker
                        self.vertex_marker.setVisible(False)
                        #reset label
                        self.dockwidget.label.setText("Select first vertex.")


                
                

    def translatePolygon(self, fvl, fvfid, fvidx, svl, svfid, svidx):
        fvl.startEditing()

        firstRequest = QgsFeatureRequest().setFilterFid(fvfid)
        #firstFeat = fvl.getFeatures(firstRequest).next()                # pyqgis2  
        #firstPoint = firstFeat.geometry().vertexAt(fvidx)           
        for firstFeat in fvl.getFeatures(firstRequest):
            firstFeatx = firstFeat.geometry()
            firstPoint = firstFeat.geometry().vertexAt(fvidx) 
            break               

        secondRequest = QgsFeatureRequest().setFilterFid(svfid)
        #secondFeat = svl.getFeatures(secondRequest).next()              # pyqgis2   
        #secondPoint = secondFeat.geometry().vertexAt(svidx)       
        for secondFeat in svl.getFeatures(secondRequest):
            secondFeatx = secondFeat.geometry()
            secondPoint = secondFeat.geometry().vertexAt(svidx)
            break                   

        dx=secondPoint.x()-firstPoint.x()
        dy=secondPoint.y()-firstPoint.y()        
              
        #res = firstFeat.geometry().translate(dx, dy) #QgsGeometry::OperationResult value; 0 means success       # pyqgis2
        geom = firstFeat.geometry()
        geom.translate(dx, dy)
        firstFeat.setGeometry(geom)                       

        newFeature=QgsFeature()
        newFeature.setGeometry( firstFeat.geometry() )
        
        newFeature.setFields( firstFeat.fields())
        for field in firstFeat.fields():
            newFeature.setAttribute(field.name(), firstFeat[field.name()])

        #fvl.addFeature(newFeature, True)                               # pyqgis2
        fvl.addFeature(newFeature)
        
        fvl.deleteFeature(firstFeat.id())               
        #fvl.setSelectedFeatures( [newFeature.id()] )                   # pyqgis2
        fvl.selectByIds( [newFeature.id()] )

        fvl.triggerRepaint()
        #QgsMessageLog.logMessage("Polygon translated!", 'CIGeoE Translate To Fit To Adjacent Polygon')
        self.iface.messageBar().pushMessage("CIGeoE Translate To Fit To Adjacent Polygon", "Polygon translated" , level=Qgis.Info, duration=2)











    def translateFullMethodFromRun(self): #UNUSED
        """Run method that loads and starts the plugin"""
        selected_features=[]
        #check all selected features in all layers; only advance with exactly 2
        layers = self.iface.legendInterface().layers()                            # pyqgis2
        layers = QgsProject.instance().layerTreeRoot().layerOrder()
        for layer in layers:
            if(layer.type() == QgsMapLayer.VectorLayer):
                for f in layer.selectedFeatures():
                    selected_features.append( f )
        #wont advance if there's more or less than 2 selected features
        if(len(selected_features)!=2):
            QMessageBox.information(self.iface.mainWindow(), "Error", 'To execute the translation, you must exactly select 2 polygons.')
            return

        #check which feature is the first or the second
        feat1full=None
        feat2full=None
        #if layers are different, first feature is of map "0" and second is of map "1"
        if self.selectedFeaturesMap["0"]["layer"] != self.selectedFeaturesMap["1"]["layer"]:
            feat1full={"layer" : self.selectedFeaturesMap["0"]["layer"], "feature" : self.selectedFeaturesMap["0"]["features"][0]}
            feat2full={"layer" : self.selectedFeaturesMap["1"]["layer"], "feature" : self.selectedFeaturesMap["1"]["features"][0]}
        else:
            #if is the same layer, the list that has only one element is the first
            if len(self.selectedFeaturesMap["0"]["features"])==1:
                feat1full={"layer":self.selectedFeaturesMap["0"]["layer"], "feature":self.selectedFeaturesMap["0"]["features"][0]}
                for f in self.selectedFeaturesMap["1"]["features"]:
                    if f.id()!=feat1full["feature"].id():
                        feat2full={"layer":self.selectedFeaturesMap["1"]["layer"], "feature":f}
            else:
                feat1full={"layer":self.selectedFeaturesMap["1"]["layer"], "feature":self.selectedFeaturesMap["1"]["features"][0]}
                for f in self.selectedFeaturesMap["0"]["features"]:
                    if f.id()!=feat1full["feature"].id():
                        feat2full={"layer":self.selectedFeaturesMap["0"]["layer"], "feature":f}
        feat1=feat1full["feature"]
        feat2=feat2full["feature"]

        #wont advance if the selected features are not polygons
        #if feat1.geometry().type()!=QGis.Polygon or feat2.geometry().type()!=QGis.Polygon:                 # pyqgis2
        if feat1.geometry().type()!=QgsWkbTypes.PolygonGeometry or feat2.geometry().type()!=QgsWkbTypes.PolygonGeometry:    
            QMessageBox.information(self.iface.mainWindow(), "Error", 'At least one of the selected features is not a polygon.')
            return

        ##wont advance if the selected feature is still a temporary feature in memory
        #if thisFeat.id()<=0:
        #    QMessageBox.information(self.iface.mainWindow(), "Error", 'This feature is temporary. Please commit changes on it\'s layer and execute this plugin again.')
        #    return

        shortestDistance=-1
        thisNearestPoint=None
        thatNearestPoint=None

        n = 0
        pt = feat2.geometry().vertexAt(0)
        while(pt != QgsPoint(0,0)):
            closestVertexFull=feat1.geometry().closestVertex(pt)#get this closest vertex of that feature vertex
            #0 point, 1 atVertex, 2 beforeVertex, 3 afterVertex, 4 sqrDist; sqrDist is negative if not found
            tempDistance=closestVertexFull[4]
            if tempDistance>=0 and (thisNearestPoint==None or shortestDistance>tempDistance):
                thisNearestPoint=QgsPoint(closestVertexFull[0].x(), closestVertexFull[0].y())
                thatNearestPoint=QgsPoint(pt.x(), pt.y())
                shortestDistance=tempDistance
            n+=1
            pt = feat2.geometry().vertexAt(n)

        if shortestDistance!=0 and thisNearestPoint!=None and thatNearestPoint!=None:
            feat1full["layer"].startEditing()
                
            #feat1full["layer"].commitChanges()
            #feat1full["layer"].startEditing()
            dx=thatNearestPoint.x()-thisNearestPoint.x()
            dy=thatNearestPoint.y()-thisNearestPoint.y()
                    
            res = feat1.geometry().translate(dx, dy)#QgsGeometry::OperationResult value; 0 means success
                    
            newFeature=QgsFeature()
            newFeature.setGeometry( feat1.geometry() )
            newFeature.setFields( feat1.fields())
            for field in feat1.fields():
                newFeature.setAttribute(field.name(), feat1[field.name()])

            #feat1full["layer"].addFeature(newFeature, True)                   # pyqgis2
            feat1full["layer"].addFeature(newFeature)

            feat1full["layer"].deleteFeature(feat1.id())   

            #feat1full["layer"].setSelectedFeatures( [newFeature.id()] )       # pyqgis2
            feat1full["layer"].selectByIds( [newFeature.id()] )

            feat1full["layer"].triggerRepaint()
            del self.selectedFeaturesMap["0"]
            del self.selectedFeaturesMap["1"]     
            #QgsMessageLog.logMessage("Polygon translated!", 'CIGeoE Translate To Fit To Adjacent Polygon')
            self.iface.messageBar().pushMessage("CIGeoE Translate To Fit To Adjacent Polygon", "Polygon translated!" , level=Qgis.Info, duration=2)
        else:
            QMessageBox.information(self.iface.mainWindow(), "Error", 'Couldn\'t find a suitable polygon near this one.')
            return


    def mapSelectionChanged(self, layer):#UNUSED
        if("0" not in self.selectedFeaturesMap):
            self.selectedFeaturesMap["0"]={"layer":layer, "features":layer.selectedFeatures()}
        elif("1" not in self.selectedFeaturesMap):
            self.selectedFeaturesMap["1"]={"layer":layer, "features":layer.selectedFeatures()}
        else:
            self.selectedFeaturesMap["0"]=self.selectedFeaturesMap["1"]
            self.selectedFeaturesMap["1"]={"layer":layer, "features":layer.selectedFeatures()}
        return
        
