# -*- coding: utf-8 -*-

"""
/***************************************************************************
 LFTools
                                 A QGIS plugin
 Tools for cartographic production and spatial analysis.
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2021-03-01
        copyright            : (C) 2021 by Leandro Franca
        email                : suporte@geoone.com.br
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/
"""

__author__ = 'Leandro Franca'
__date__ = '2021-03-01'
__copyright__ = '(C) 2021 by Leandro Franca'
__revision__ = '$Format:%H$'

import os
import sys
import inspect

from qgis.core import (QgsProject,
                       QgsCoordinateTransform,
                       QgsApplication,
                       QgsExpression)
from qgis.PyQt.QtCore import QMetaType, QCoreApplication, QSettings, QTranslator, QUrl
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QAction, QMenu, QToolButton, QMessageBox    
from .lftools_provider import LFToolsProvider
from .translations.translate import translate
from .geocapt.topogeo import dms2dd as DMS2DD
from .geocapt.cartography import LabelConf, SymbolSimplePoint, prepare_temp_qml
from .geocapt.tools import *
from .expressions import *
from .LFTools_Dialog import ImportXYZ_Dialog
from qgis.utils import iface
import processing
import webbrowser

exprs = (coord2inom, fieldstat, dd2dms, projectCRS, layerCRS, magneticdec, mainAzimuth,
         dms2dd, scalefactor, zonehemisf, deedtable, inom2mi, meridianconv, cusum, inter_area,
         removespetialchar, deedtable2, deedtable3, areaLTP, deedtext, geoneighbors, gpsdate,
         str2html, img2html, dinamictable, perimeterLTP, lengthLTP, areaLTP2, perimeterLTP2,
         lengthLTP2, lengthLTP3, layer_schema, azimuth_by_sequence, classify, triangle_area_3d,
         azimuth2bearing, bearing2azimuth)

cmd_folder = os.path.split(inspect.getfile(inspect.currentframe()))[0]

if cmd_folder not in sys.path:
    sys.path.insert(0, cmd_folder)

class LFToolsPlugin(object):

    def __init__(self):
        self.iface = iface
        self.canvas = iface.mapCanvas()
        self.provider = None
        self.camada_copiada = None
        self.plugin_dir = os.path.dirname(__file__)
        self.layerid = ''
        self.layerid2 = ''

    def tr(self, *string):
        return translate(string, QgsApplication.locale()[:2])
    
    def exec_dialog(self, dlg):
        try:
            return dlg.exec()      # PyQt6
        except AttributeError:
            return dlg.exec_()     # PyQt5

    def initProcessing(self):
        """Init Processing provider for QGIS >= 3.8."""
        self.provider = LFToolsProvider()
        QgsApplication.processingRegistry().addProvider(self.provider)

    def initGui(self):
        self.initProcessing()
        for expr in exprs:
            if not QgsExpression.isFunctionName(expr.name()):
                QgsExpression.registerFunction(expr)

        # Iniciar LFTools toolbar
        self.toolbar = self.iface.addToolBar('LFTools')
        self.toolbar.setObjectName('LFToolsToolbar')
        self.toolbar.setToolTip('LFTools')

        # Definir SRC
        icon = QIcon(self.plugin_dir + '/images/tools/UTM.svg')
        self.UTM_Action = QAction(icon, self.tr('Set CRS in UTM', 'Definir SRC em UTM'), self.iface.mainWindow())
        self.UTM_Action.setObjectName('DefineUTM')
        self.UTM_Action.triggered.connect(self.runUTM)
        self.toolbar.addAction(self.UTM_Action)

        # Importar X,Y,Z
        icon = QIcon(self.plugin_dir + '/images/tools/XYZ.svg')
        self.ImportXYZ_Action = QAction(icon, self.tr('Import XYZ', 'Importar XYZ'), self.iface.mainWindow())
        self.ImportXYZ_Action.setObjectName('ImportXYZ')
        self.ImportXYZ_Action.triggered.connect(self.runImportXYZ)
        self.toolbar.addAction(self.ImportXYZ_Action)

        # Linhas de Cotagem (Dimensioning)
        icon = QIcon(self.plugin_dir + '/images/tools/DIMENSIONING.svg')
        self.Dimensioning_Action = QAction(icon, self.tr('Dimensioning', 'Cotagem'), self.iface.mainWindow())
        self.Dimensioning_Action.setObjectName('Dimensioning')
        self.Dimensioning_Action.triggered.connect(self.runDimensioning)
        self.toolbar.addAction(self.Dimensioning_Action)

        # Copiar estilo da camada ativa
        icon = QIcon(self.plugin_dir + '/images/tools/COPY_STYLE.svg')
        self.CopiarEstilo_Action = QAction(icon, self.tr('Copy layer style', 'Copiar estilo da camada'), self.iface.mainWindow())
        self.CopiarEstilo_Action.setObjectName('CopyStyle')
        self.CopiarEstilo_Action.triggered.connect(self.runCopiarEstilo)
        self.toolbar.addAction(self.CopiarEstilo_Action)

        # Colar estilo
        icon = QIcon(self.plugin_dir + '/images/tools/PASTE_STYLE.svg')
        self.ColarEstilo_Action = QAction(icon, self.tr('Paste style to the layer', 'Colar estilo na camada'), self.iface.mainWindow())
        self.ColarEstilo_Action.setObjectName('PasteStyle')
        self.ColarEstilo_Action.triggered.connect(self.runColarEstilo)
        self.toolbar.addAction(self.ColarEstilo_Action)

        # DEM Downloader
        icon = QIcon(self.plugin_dir + '/images/tools/DEM_DOWNLOADER.svg')
        self.DEM_Downloader_Action = QAction(icon, self.tr('DEM Downloader', 'Baixar MDE'), self.iface.mainWindow())
        self.DEM_Downloader_Action.setObjectName('DEMDownloader')
        self.DEM_Downloader_Action.triggered.connect(self.DEM_Downloader)
        self.toolbar.addAction(self.DEM_Downloader_Action)

        # Principais ferramentas LFTools (Mão na roda)
        menu = QMenu()
        menu.setObjectName('MainLFTools')
        # Adicionando principais ferramentas
        icon = QIcon(self.plugin_dir + '/images/tools/THUMBS_UP.svg')
        self.Coord2Layer_Action = menu.addAction(icon, self.tr('Table to point layer', 'Planilha para camada de pontos'), self.Coord2Layer)
        self.Coord2Layer_Action.setObjectName('CoordToLayer')
        self.GetAttribute_Action = menu.addAction(icon, self.tr('Get attribute by location', 'Pegar atributo pela localização'), self.GetAttribute)
        self.GetAttribute_Action.setObjectName('GetAttribute')
        self.MeasureLayer_Action = menu.addAction(icon, self.tr('Measure layers', 'Medir camadas'), self.MeasureLayer)
        self.MeasureLayer_Action.setObjectName('MeasureLayer')
        self.ExportASCII_Action = menu.addAction(icon, self.tr('Export expression as ASCII', 'Exportar expressão como ASCII'), self.ExportASCII)
        self.ExportASCII_Action.setObjectName('ExportASCII')
        self.SelectByKeyAtt_Action = menu.addAction(icon, self.tr('Select by key attribute', 'Selecionar por atributo chave'), self.SelectByKeyAtt)
        self.SelectByKeyAtt_Action.setObjectName('SelectByKeyAtt')
        self.MagicStyles_Action = menu.addAction(icon, self.tr('Magic Styles', 'Estilos Mágicos'), self.MagicStyles)
        self.MagicStyles_Action.setObjectName('MagicStyles')

        # Adicionando conjunto de botões
        self.MainLFToolsButton = QToolButton()
        self.MainLFToolsButton.setMenu(menu)
        self.MainLFToolsButton.setDefaultAction(self.Coord2Layer_Action)

        try:
            # Qt6
            popup_mode = QToolButton.ToolButtonPopupMode.MenuButtonPopup
        except AttributeError:
            # Qt5
            popup_mode = QToolButton.MenuButtonPopup
        self.MainLFToolsButton.setPopupMode(popup_mode)

        self.MainLFToolsButton.triggered.connect(self.toolButtonTriggered)
        self.MainLFToolsToolbar = self.toolbar.addWidget(self.MainLFToolsButton)
        self.MainLFToolsToolbar.setObjectName('MainLFToolsToolbar')

        # Ajuda do LFTools
        icon = QIcon(self.plugin_dir + '/images/tools/GEOONE.svg')
        self.Tutoriais_Action = QAction(icon, self.tr('Tutorials', 'Tutoriais'), self.iface.mainWindow())
        self.Tutoriais_Action.setObjectName('Tutorials')
        self.Tutoriais_Action.triggered.connect(self.runTutoriais)
        self.toolbar.addAction(self.Tutoriais_Action)


    def unload(self):
        QgsApplication.processingRegistry().removeProvider(self.provider)
        for expr in exprs:
            if QgsExpression.isFunctionName(expr.name()):
                QgsExpression.unregisterFunction(expr.name())
        # Remove from toolbar
        self.iface.removeToolBarIcon(self.UTM_Action)
        self.iface.removeToolBarIcon(self.ImportXYZ_Action)
        self.iface.removeToolBarIcon(self.Dimensioning_Action)
        self.iface.removeToolBarIcon(self.CopiarEstilo_Action)
        self.iface.removeToolBarIcon(self.ColarEstilo_Action)
        self.iface.removeToolBarIcon(self.DEM_Downloader_Action)
        self.iface.removeToolBarIcon(self.Coord2Layer_Action)
        self.iface.removeToolBarIcon(self.GetAttribute_Action)
        self.iface.removeToolBarIcon(self.MeasureLayer_Action)
        self.iface.removeToolBarIcon(self.ExportASCII_Action)
        self.iface.removeToolBarIcon(self.SelectByKeyAtt_Action)
        self.iface.removeToolBarIcon(self.MagicStyles_Action)
        self.iface.removeToolBarIcon(self.Tutoriais_Action)
        # remove the toolbar
        del self.toolbar


    def toolButtonTriggered(self, action):
        self.MainLFToolsButton.setDefaultAction(action)


    def Coord2Layer(self):
        processing.execAlgorithmDialog('lftools:coord2layer', {})

    def GetAttribute(self):
        processing.execAlgorithmDialog('lftools:getattributebylocation', {})

    def MeasureLayer(self):
        processing.execAlgorithmDialog('lftools:measure_layers', {})

    def ExportASCII(self):
        processing.execAlgorithmDialog('lftools:exportascii', {})

    def SelectByKeyAtt(self):
        processing.execAlgorithmDialog('lftools:selectbykeyatt', {})

    def DEM_Downloader(self):
        processing.execAlgorithmDialog('lftools:demdownloader', {})
    
    def MagicStyles(self):
        processing.execAlgorithmDialog('lftools:magicstyles', {}) 


    def runUTM(self):
        DefinirUTM(self.iface)

    def runCopiarEstilo(self):
        self.camada_copiada = copiar_estilo_camada_ativa(self.iface)

    def runColarEstilo(self):
        colar_estilo_em_camada_destino(self.iface, self.camada_copiada)

    def runTutoriais(self):
        webbrowser.open_new('https://www.youtube.com/@geoleandrofranca')

    def runImportXYZ(self):
        # Criar caixa de diálogo
        dlg = ImportXYZ_Dialog()
        # Mostrar caixa de diálogo
        dlg.adjustSize()
        dlg.show()
        result = self.exec_dialog(dlg)
        projeto = QgsProject.instance()
        # Quando pressionado
        if result == 1:
            try:
                coordX = dlg.coordX.text().replace(',', '.').strip()
                coordY = dlg.coordY.text().replace(',', '.').strip()
                coordZ = dlg.coordZ.text().replace(',', '.').strip()
                crs = dlg.CRS.crs()
                nome = dlg.Name.text()
                nome_campo = self.tr('name', 'nome')

                # Verificando SRC
                if not crs.isValid():
                    raise ValueError(self.tr('Invalid CRS selected!', 'SRC inválido selecionado!'))

                # Identificação e validação dos dados de entrada
                if crs.isGeographic():
                    X = DMS2DD(coordX)
                    if X is None:
                        try:
                            X = float(coordX)
                        except:
                            raise ValueError(self.tr('Invalid X/longitude coordinate: {}', 'Coordenada X/longitude inválida: {}').format(coordX))

                    Y = DMS2DD(coordY)
                    if Y is None:
                        try:
                            Y = float(coordY)
                        except:
                            raise ValueError(self.tr('Invalid Y/latitude coordinate: {}', 'Coordenada Y/latitude inválida: {}').format(coordY))
                else: # Coordenadas projetadas
                    try:
                        X = float(coordX)
                    except:
                        raise ValueError(self.tr('Invalid X coordinate: {}', 'Coordenada X inválida: {}').format(coordX))

                    try:
                        Y = float(coordY)
                    except:
                        raise ValueError(self.tr('Invalid Y coordinate: {}', 'Coordenada Y inválida: {}').format(coordY))

                try:
                    Z = 0 if coordZ == '' else float(coordZ)
                except:
                    raise ValueError(self.tr('Invalid Z coordinate: {}', 'Coordenada Z inválida: {}').format(coordZ))

                # Criando camada pela primeira vez
                if not projeto.mapLayer(self.layerid):
                    self.layer = QgsVectorLayer("PointZ?crs=" + crs.authid(), self.tr('Points XYZ', 'Pontos XYZ'), "memory")
                    self.DP = self.layer.dataProvider()
                    crs0 = crs
                    # adicionar campos
                    campos = [QgsField(nome_campo, QMetaType.QString),
                              QgsField('X', QMetaType.QString),
                              QgsField('Y', QMetaType.QString),
                              QgsField('Z', QMetaType.QString),
                              QgsField('CRS', QMetaType.QString)]
                    self.DP.addAttributes(campos)
                    self.layer.updateFields()
                    # Estilizar camada
                    labeling = LabelConf(nome_campo)
                    self.layer.setLabeling(labeling)
                    self.layer.setLabelsEnabled(True)
                    self.layer.triggerRepaint()
                    renderer = SymbolSimplePoint(self.layer)
                    self.layer.setRenderer(renderer)
                    self.layer.triggerRepaint()

                    # Adicionar camada
                    projeto.addMapLayer(self.layer)
                    # Armazenar id da camada
                    self.layerid = self.layer.id()
                else:
                    crs0 = self.layer.crs()

                # Adicionar feição
                fields = self.layer.fields()
                feat = QgsFeature(fields)
                feat[nome_campo] = nome
                feat['X'] = str(coordX)
                feat['Y'] = str(coordY)
                feat['Z'] = str(coordZ)
                feat['CRS'] = str(crs.description())
                geom = QgsGeometry(QgsPoint(X, Y, Z))
                if crs0 != crs: # Transformar SRC, se necessário
                    coordinateTransformer = QgsCoordinateTransform()
                    coordinateTransformer.setDestinationCrs(crs0)
                    coordinateTransformer.setSourceCrs(crs)
                    geom.transform(coordinateTransformer)
                feat.setGeometry(geom)
                self.layer.startEditing()
                self.layer.addFeatures([ feat ])
                self.layer.commitChanges()
                self.layer.triggerRepaint()
                
                project_crs = QgsProject.instance().crs()
                pt = QgsPointXY(X, Y)  # no CRS de entrada (crs)

                if crs != project_crs:
                    ct = QgsCoordinateTransform(crs, project_crs, QgsProject.instance())
                    pt = ct.transform(pt)

                self.canvas.setCenter(pt)
                self.canvas.refresh()

            except Exception as e:
                QMessageBox.information(self.iface.mainWindow(), QCoreApplication.translate('LFTools', "LFTools plugin error"), QCoreApplication.translate('LFTools', "There was an error with the input parameter:<br><strong>{}</strong>".format(e)))
                return

    def runDimensioning(self):
        self.iface.messageBar().pushMessage(self.tr('Use snapping, if possible.', 'Use a Aderência, se possível.'), level=Qgis.Info, duration=10)
        projeto = QgsProject.instance()
        crs = projeto.crs()
        # Criando camada pela primeira vez
        if not projeto.mapLayer(self.layerid2):
            #criar camada
            layer = QgsVectorLayer("LineStringZ?crs=" + crs.authid(), self.tr('Dimensioning', 'Cotagem'), "memory")
            self.layerid2 = layer.id()

            # Adicionar estilos
            estilo = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'lftools/styles/cotagem_prof_leandro.qml' )
            expr = '$length' if crs.isGeographic() else 'length($geometry)'
            estilo = prepare_temp_qml(estilo, ['[EXPRESSION]'], [ expr ] )
            layer.loadNamedStyle(estilo)

            # Adicionar camada
            projeto.addMapLayer(layer)
            # Obter o nó da camada
            layer_tree = projeto.layerTreeRoot()
            layer_node = layer_tree.findLayer(layer.id())
            if layer_node:
                # Clonar e mover para o topo
                parent = layer_node.parent()
                clone_node = layer_node.clone()
                layer_tree.insertChildNode(0, clone_node)
                parent.removeChildNode(layer_node)
        else:
            layer = projeto.mapLayer(self.layerid2)
        # Ativar camada
        self.iface.setActiveLayer(layer)
        # colocar no modo edição
        layer.startEditing()
        # habilitar ferramenta de vetorização
        self.iface.actionAddFeature().trigger()
        return
