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

"""
/***************************************************************************
 GeoINCRA
                                 A QGIS plugin
 Georreferenciamento de Imóveis Rurais
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2022-02-13
        copyright            : (C) 2022 by Tiago Prudencio e Leandro França
        email                : contato@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__ = 'Tiago Prudencio e Leandro França'
__date__ = '2022-02-13'
__copyright__ = '(C) 2022 by Tiago Prudencio e Leandro França'

from qgis.PyQt.QtCore import QCoreApplication
from qgis.core import (QgsProcessing,
                       QgsProcessingParameterField,
                       QgsProcessingParameterBoolean,
                       QgsWkbTypes,
                       QgsProcessingException,
                       QgsCoordinateReferenceSystem,
                       QgsCoordinateTransform,
                       QgsFeature,
                       QgsProject,
                       QgsProcessingAlgorithm,
                       QgsProcessingParameterFeatureSource,
                       QgsProcessingParameterVectorLayer)
from qgis import processing
from qgis.PyQt.QtGui import QIcon
import os


class addFeat(QgsProcessingAlgorithm):

    INPUT = 'INPUT'
    OUTPUT = 'OUTPUT'

    def tr(self, string):
        return QCoreApplication.translate('Processing', string)

    def createInstance(self):
        return addFeat()

    def name(self):
        return 'addFeat'

    def displayName(self):
        return self.tr('a. Alimentar camada vértice')

    def group(self):
        return self.tr('2. ⚙️ Fluxo de processamento')

    def groupId(self):
        return 'fluxo_processamento'

    def icon(self):
        return QIcon(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'images/geoincra_pb.png'))
    
    def tags(self):
        return 'GeoOne,GeoRural,INCRA,Sigef,alimentar,carregar,load,importar,vertice,colar,copiar,pontos,planilha,coordenadas,GNSS,RTK'.split(',')
    
    def shortHelpString(self):
        txt = 'Esta ferramenta carrega as feições selecionadas de uma camada de pontos para dentro da camada vértices do banco de dados GeoRural.'

        footer = '''<div>
                      <div align="center">
                      <a target="_blank" rel="noopener noreferrer" href="https://geoone.com.br/pvgeoincra/"><img title="Inscreva-se no curso de GeoINCRA" style="width: 100%; height: auto;" src="'''+ os.path.join(os.path.dirname(os.path.dirname(__file__)), 'images/INCRA_GeoOne.jpg') +'''"></a>
                      </div>
                      <div align="right">
                      <p align="right">
                      <a href="https://geoone.com.br/pvgeoincra/"><span style="font-weight: bold;">Conheça o curso de GeoINCRA no QGIS</span></a>
                      </p>
                      <p align="right">
                      <a href="https://portal.geoone.com.br/m/lessons/geoincra?classId=2230"><span style="font-weight: bold;">Acesse a aula sobre esta ferramenta no curso de GeoINCRA no GeoOne</span></a>
                      </p>
                      <a target="_blank" rel="noopener noreferrer" href="https://geoone.com.br/"><img title="GeoOne" width="280"  src="'''+ os.path.join(os.path.dirname(os.path.dirname(__file__)), 'images/GeoOne.png') +'''"></a>
                      <p><i>"Mapeamento automatizado, fácil e direto ao ponto é na GeoOne!"</i></p>
                      </div>
                    </div>'''
        return txt + footer

    sigma_x = 'sigma_x'
    sigma_y = 'sigma_y'
    sigma_z = 'sigma_z'
    metodo_pos = 'metodo_pos'
    vertice = 'vertice'
    tipo_verti = 'tipo_verti'
    qrcode = 'qrcode'
    VER_Z = 'VER_Z'
    ARREDONDAR = 'ARREDONDAR'

    def initAlgorithm(self, config=None):

        self.addParameter(
            QgsProcessingParameterFeatureSource(
                self.INPUT,
                self.tr('Camada de pontos a serem carregados'),
                [QgsProcessing.TypeVectorPoint]
            )
        )

        self.addParameter(
            QgsProcessingParameterVectorLayer(
                self.OUTPUT,
                self.tr('Camada de vértices do banco de dados GeoRural'),
                [QgsProcessing.TypeVectorPoint]
            )
        )

        self.addParameter(
            QgsProcessingParameterField(
                self.sigma_x,
                self.tr('Precisão em X'),
                parentLayerParameterName=self.INPUT,
                optional = True
            )
        )

        self.addParameter(
            QgsProcessingParameterField(
                self.sigma_y,
                self.tr('Precisão em Y'),
                parentLayerParameterName=self.INPUT,
                optional = True
            )
        )

        self.addParameter(
            QgsProcessingParameterField(
                self.sigma_z,
                self.tr('Precisão em Z'),
                parentLayerParameterName=self.INPUT,
                optional = True
            )
        )

        self.addParameter(
            QgsProcessingParameterField(
                self.metodo_pos,
                self.tr('Método de posicionamento'),
                parentLayerParameterName=self.INPUT,
                optional = True
            )
        )

        self.addParameter(
            QgsProcessingParameterField(
                self.vertice,
                self.tr('Código do vértice'),
                parentLayerParameterName=self.INPUT,
                optional = True
            )
        )

        self.addParameter(
            QgsProcessingParameterField(
                self.tipo_verti,
                self.tr('Tipo de Vértice (M,P ou V)'),
                parentLayerParameterName=self.INPUT,
                optional = True
            )
        )

        self.addParameter(
            QgsProcessingParameterField(
                self.qrcode,
                self.tr('qrcode'),
                parentLayerParameterName=self.INPUT,
                optional = True
            )
        )

        self.addParameter(
            QgsProcessingParameterBoolean(
                self.VER_Z,
                self.tr('Verificar preenchimento de cota Z'),
                defaultValue = True
            )
        )

        self.addParameter(
            QgsProcessingParameterBoolean(
                self.ARREDONDAR,
                self.tr('Arredondar casas decimais das coordenadas.'),
                defaultValue = True
            )
        )


    def processAlgorithm(self, parameters, context, feedback):

        source_in = self.parameterAsSource(
            parameters,
            self.INPUT,
            context
        )
        if not source_in:
            raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT))

        if source_in.wkbType() != QgsWkbTypes.PointZ:
            raise QgsProcessingException('Camada de pontos de entrada deve ter geometria do tipo PointZ!')

        sigma_x = self.parameterAsFields(parameters, self.sigma_x, context)
        if sigma_x:
            sigma_x = sigma_x[0]
        sigma_y = self.parameterAsFields(parameters, self.sigma_y, context)
        if sigma_y:
            sigma_y = sigma_y[0]
        sigma_z = self.parameterAsFields(parameters, self.sigma_z, context)
        if sigma_z:
            sigma_z = sigma_z[0]
        metodo_pos = self.parameterAsFields(parameters, self.metodo_pos, context)
        if metodo_pos:
            metodo_pos = metodo_pos[0]
        vertice = self.parameterAsFields(parameters, self.vertice, context)
        if vertice:
            vertice = vertice[0]
        tipo_verti = self.parameterAsFields(parameters, self.tipo_verti, context)
        if tipo_verti:
            tipo_verti = tipo_verti[0]
        qrcode = self.parameterAsFields(parameters, self.qrcode, context)
        if qrcode:
            qrcode = qrcode[0]
        ver_z = self.parameterAsBool(parameters, self.VER_Z, context)
        arredondar = self.parameterAsBool(parameters, self.ARREDONDAR, context)

        source_out = self.parameterAsVectorLayer(
            parameters,
            self.OUTPUT,
            context
        )
        if not source_out:
            raise QgsProcessingException(self.invalidSourceError(parameters, self.OUTPUT))

        # Validações
        def valida_sigma(sigma, eixo, id):
            if sigma < 0 or sigma > 7.5:
                raise QgsProcessingException('Erro: Feição de ID {} com precisão {} em {}!'.format(id, str(sigma), eixo))
            if str(sigma) == 'NULL':
                feedback.reportError('Aviso: Feição de ID {} com precisão {} em {}!!'.format(id, str(sigma), eixo))

        for feature in source_in.getFeatures():
            # Validando as precisões de entrada
            if sigma_x:
                sigma = float(feature[sigma_x].replace(',','.')) if isinstance(feature[sigma_x], str) else feature[sigma_x]
                valida_sigma(sigma, 'x', feature.id())
            if sigma_y:
                sigma = float(feature[sigma_y].replace(',','.')) if isinstance(feature[sigma_y], str) else feature[sigma_y]
                valida_sigma(sigma, 'y', feature.id())
            if sigma_z:
                sigma = float(feature[sigma_z].replace(',','.')) if isinstance(feature[sigma_z], str) else feature[sigma_z]
                valida_sigma(sigma, 'z', feature.id())
            if ver_z:
                # Validando as altitudes de entrada
                z = float(feature.geometry().constGet().z())
                if str(z) == 'nan' or z == 0:
                    feedback.reportError('Aviso: Cota Z não preenchida ou igual a zero para a feição de id {}!'.format(feature.id()))
                if z > 3000 or z < -10:
                    raise QgsProcessingException('Erro: Cota Z com valor "{}" na feição de id "{}" fora dos limites permitidos!'.format(z, feature.id()))


        total = 100.0 / source_in.featureCount() if source_in.featureCount() else 0

        for current, feature in enumerate(source_in.getFeatures()):
            feat = QgsFeature(source_out.fields())
            if sigma_x:
                sigma = float(feature[sigma_x].replace(',','.')) if isinstance(feature[sigma_x], str) else feature[sigma_x]
                feat.setAttribute('sigma_x', sigma)
            if sigma_y:
                sigma = float(feature[sigma_y].replace(',','.')) if isinstance(feature[sigma_y], str) else feature[sigma_y]
                feat.setAttribute('sigma_y', sigma)
            if sigma_z:
                sigma = float(feature[sigma_z].replace(',','.')) if isinstance(feature[sigma_z], str) else feature[sigma_z]
                feat.setAttribute('sigma_z', sigma)
            if metodo_pos:
                feat.setAttribute('metodo_pos',feature[metodo_pos])
            if vertice:
                feat.setAttribute('vertice',feature[vertice])
            if tipo_verti:
                feat.setAttribute('tipo_verti',feature[tipo_verti])
            if qrcode:
                feat.setAttribute('qrcode',feature[qrcode])

            crsSrc = QgsCoordinateReferenceSystem(source_in.sourceCrs())
            crsDest = QgsCoordinateReferenceSystem('EPSG:4674')
            proj2geo = QgsCoordinateTransform(crsSrc, crsDest, QgsProject.instance())
            geom = feature.geometry()
            geom.transform(proj2geo)
            if arredondar:
                feat.setGeometry(geom.snappedToGrid(hSpacing=1e-7, vSpacing=1e-7, dSpacing=1e-2)) # arredonda para 7 casas decimais as coordenadas XY e 2 casas decimais a cota Z, o que é suficiente para garantir a precisão necessária para para que o cálculo de área e azimutes coincidam com os cálculos feitos no Sigef.
            else:
                feat.setGeometry(geom)
            source_out.dataProvider().addFeatures([feat])
            feedback.setProgress(int(current * total))

        return {}
