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

"""
***************************************************************************
    Doc_PointsFromText.py
    ---------------------
    Date                 : Jun 12
    Copyright            : (C) 2022 by Leandro França
    Email                : geoleandro.franca@gmail.com
***************************************************************************
*                                                                         *
*   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 França'
__date__ = 'Jun 12'
__copyright__ = '(C) 2022, Leandro França'

from qgis.PyQt.QtCore import QVariant
from qgis.core import (QgsApplication,
                       QgsProcessingParameterString,
                       QgsProcessingParameterCrs,
                       QgsProcessingParameterBoolean,
                       QgsFields,
                       QgsWkbTypes,
                       QgsField,
                       QgsFeature,
                       QgsGeometry,
                       QgsPointXY,
                       QgsFeatureSink,
                       QgsProcessingException,
                       QgsProcessingAlgorithm,
                       QgsProcessingUtils,
                       QgsProcessingParameterFeatureSink)

import re, os
from lftools.geocapt.imgs import *
from lftools.geocapt.cartography import LabelConf, SymbolSimplePoint
from lftools.translations.translate import translate
from lftools.geocapt.topogeo import dms2dd
from qgis.PyQt.QtGui import QIcon, QColor

class PointsFromText(QgsProcessingAlgorithm):

    RE_NAME = 'RE_NAME'
    RE_X = 'RE_X'
    RE_Y = 'RE_Y'
    TEXT = 'TEXT'
    CRS = 'CRS'
    OUTPUT ='OUTPUT'
    DEC_SEPARATOR = 'DEC_SEPARATOR'
    GMS = 'GMS'

    LOC = QgsApplication.locale()[:2]

    def tr(self, *string):
        return translate(string, self.LOC)

    def createInstance(self):
        return PointsFromText()

    def name(self):
        return 'pointsfromtext'

    def displayName(self):
        return self.tr('Points from Deed Description', 'Reconstituição de Memorial')

    def group(self):
        return self.tr('Documents', 'Documentos')

    def groupId(self):
        return 'documents'

    def tags(self):
        return 'GeoOne,deed,regex,regular,expression,expressão,description,descriptive,memorial,property,topography,surveying,real,estate,georreferencing,plan,cadastral,cadastre,document'.split(',')

    def icon(self):
        return QIcon(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'images/document.png'))

    txt_en = 'Performs the reconstitution of a Deed Description using Regular Expressions (RegEx).'
    txt_pt = 'Realiza a reconstituição de Memorial descritivo utilizando Expressões Regulares (RegEx).'
    figure = 'images/tutorial/doc_pointsFromText.jpg'

    def shortHelpString(self):
        social_BW = Imgs().social_BW
        footer = '''<div align="center">
                      <img src="'''+ os.path.join(os.path.dirname(os.path.dirname(__file__)), self.figure) +'''">
                      </div>
                      <div align="right">
                      <p align="right">
                      <b><a href="https://regex101.com/" target="_blank">'''+ self.tr('Click here for testing your regular expression (RegEx).',
                                    'Clique aqui para testar sua expressão regular (RegEx).') +'''</a><br>
                                    <b>Longitude Regex:</b>
                                    (-?\d{1,3}[\s°º˚]\s*\d{1,2}[\s'`´]\s*\d{1,2}[,.]\d+\s*["''″]?\s*[WE])
                                    <b>Latitude Regex:</b>
                                    (-?\d{1,3}[\s°º˚]\s*\d{1,2}[\s'`´]\s*\d{1,2}[,.]\d+\s*["''″]?\s*[SN])
                                    <b>INCRA code:</b>
                                    \s*[A-Z0-9]{3,4}-[MPV]-\d{1,5}
                                    <br>
                                    '''+ self.tr('Author: Leandro Franca', 'Autor: Leandro França')+'''</b>
                      </p>'''+ social_BW + '''</div>
                    </div>'''
        return self.tr(self.txt_en, self.txt_pt) + footer

    def initAlgorithm(self, config=None):

        self.addParameter(
            QgsProcessingParameterString(
                self.RE_NAME,
                self.tr('RegEx for Vertex Code','RegEx do Código do vértice'),
                defaultValue = r'\w+[\s-]+\d{2,3}'
            )
        )

        self.addParameter(
            QgsProcessingParameterString(
                self.RE_X,
                self.tr('X coordinate RegEx', 'RegEx da coordenada X'),
                defaultValue = self.tr(r'E[\s-]+[\d\,-]+[\d\.-]+[\d]', r'E[\s-]+[\d\.-]+[\d\,-]+[\d]')
            )
        )

        self.addParameter(
            QgsProcessingParameterString(
                self.RE_Y,
                self.tr('X coordinate RegEx', 'RegEx da coordenada Y'),
                defaultValue = self.tr(r'N[\s-]+[\d\,-]+[\d\,-]+[\d\.-]+[\d]', r'N[\s-]+[\d\.-]+[\d\.-]+[\d\,-]+[\d]')
            )
        )

        self.addParameter(
            QgsProcessingParameterBoolean(
            self.GMS,
            self.tr('Coordinates in Degrees, Minutes, and Seconds (DMS)', 'Coordenadas em Graus, Minutos e Segundos (GMS)'),
            defaultValue = False
            )
        )

        self.addParameter(
            QgsProcessingParameterCrs(
                self.CRS,
                self.tr('CRS','SRC'),
                'ProjectCrs'))

        self.addParameter(
            QgsProcessingParameterString(
                self.TEXT,
                self.tr('Text','Texto do Memorial Descritivo'),
                multiLine = True
            )
        )

        self.addParameter(
            QgsProcessingParameterBoolean(
            self.DEC_SEPARATOR,
            self.tr('Decimal separator is dot', 'Separador decimal é ponto'),
            defaultValue = False if self.LOC == 'pt' else True
            )
        )

        self.addParameter(
            QgsProcessingParameterFeatureSink(
                self.OUTPUT,
                self.tr('Point layer','Vértices do Memorial')
            )
        )


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

        regex_nome = self.parameterAsString(
            parameters,
            self.RE_NAME,
            context
        )

        regex_x = self.parameterAsString(
            parameters,
            self.RE_X,
            context
        )

        regex_x = regex_x.replace(' ', '')

        regex_y = self.parameterAsString(
            parameters,
            self.RE_Y,
            context
        )

        regex_y = regex_y.replace(' ', '')

        crs = self.parameterAsCrs(
            parameters,
            self.CRS,
            context
        )

        texto = self.parameterAsString(
            parameters,
            self.TEXT,
            context
        )

        sep_decimal = self.parameterAsBool(
            parameters,
            self.DEC_SEPARATOR,
            context
        )

        GMS = self.parameterAsBool(
            parameters,
            self.GMS,
            context
        )

        if GMS and not crs.isGeographic():
            raise QgsProcessingException(self.tr('Choose a geographic CRS!', 'Escolha um SRC geográfico!'))

        Fields = QgsFields()
        itens  = {
                     'ord' : QVariant.Int,
                     self.tr('code') : QVariant.String,
                     self.tr('x') : QVariant.Double,
                     self.tr('y') : QVariant.Double
                     }
        for item in itens:
            Fields.append(QgsField(item, itens[item]))

        (sink, dest_id) = self.parameterAsSink(
            parameters,
            self.OUTPUT,
            context,
            Fields,
            QgsWkbTypes.Point,
            crs
        )
        if sink is None:
            raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT))

        # Eliminando espaços duplos e nova linha no texto
        texto = texto.replace('\n',' ').replace('\t',' ').replace('  ',' ')

        # Extraindo dados
        nm_list = re.findall(regex_nome, texto)
        x_list = re.findall(regex_x, texto)
        y_list = re.findall(regex_y, texto)

        tam_nome = len(nm_list)
        tam_x = len(x_list)
        tam_y = len(y_list)

        # Eliminando \n (nova linha)
        for k in range(tam_nome):
            nm_list[k] = nm_list[k].replace('\n',' ')
        for k in range(tam_x):
            x_list[k] = x_list[k].replace('\n',' ')
        for k in range(tam_y):
            y_list[k] = y_list[k].replace('\n',' ')

        # Validando dados
        if tam_x != tam_nome or tam_y!= tam_nome:
            feedback.pushInfo(self.tr('Códigos: ({})'.format(tam_nome)))
            feedback.pushInfo(self.tr(str(nm_list)))
            feedback.pushInfo(self.tr('Coordenadas X: ({})'.format(tam_x)))
            feedback.pushInfo(self.tr(str(x_list)))
            feedback.pushInfo(self.tr('Coordenadas Y:  ({})'.format(tam_x)))
            feedback.pushInfo(self.tr(str(y_list)))
            raise QgsProcessingException(self.tr('Error: The number of input values does not match.', 'Erro na quantidade de coordenadas'))

        tam = tam_nome
        feedback.pushInfo(self.tr('Codes and coordinates', 'Código e coordenadas:'))
        for k, nome in enumerate(nm_list):
            feedback.pushInfo(self.tr('{};   {};  {}'.format(nome ,x_list[k], y_list[k])))


        lista_X, lista_Y = [],[]

        if GMS:
            for k in range(tam):
                try:
                    lista_X += [dms2dd(x_list[k])]
                    lista_Y += [dms2dd(y_list[k])]
                except:
                    raise QgsProcessingException(self.tr('Error in coordinate {}!', 'Erro na coordenada {}!').format(x_list[k]))

        else:

            if sep_decimal:
                # Removendo caracteres não digito
                for k in range(tam):
                    txt = ''
                    for s in x_list[k]:
                        if s.isdigit() or s == '.':
                            txt += s
                    try:
                        lista_X += [float(txt)]
                    except:
                        raise QgsProcessingException(self.tr('Error in coordinate {}!', 'Erro na coordenada {}!').format(x_list[k]))

                    txt = ''
                    for s in y_list[k]:
                        if s.isdigit() or s == '.':
                            txt += s
                    try:
                        lista_Y += [float(txt)]
                    except:
                        raise QgsProcessingException(self.tr('Error in coordinate {}!', 'Erro na coordenada {}!').format(y_list[k]))
            else:
                for k in range(tam):
                    txt = ''
                    for s in x_list[k]:
                        if s.isdigit() or s == ',':
                            txt += s
                    txt = txt.replace(',','.')
                    try:
                        lista_X += [float(txt)]
                    except:
                        raise QgsProcessingException(self.tr('Error in coordinate {}!', 'Erro na coordenada {}!').format(x_list[k]))

                    txt = ''
                    for s in y_list[k]:
                        if s.isdigit() or s == ',':
                            txt += s
                    txt = txt.replace(',','.')
                    try:
                        lista_Y += [float(txt)]
                    except:
                        raise QgsProcessingException(self.tr('Error in coordinate {}!', 'Erro na coordenada {}!').format(y_list[k]))


        # Varrer pontos
        feat = QgsFeature()
        total = 100.0 / tam if tam else 0

        for k in range(tam):
            NOME = nm_list[k]
            X = lista_X[k]
            Y = lista_Y[k]
            feat.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(X,Y)))
            feat.setAttributes([k+1, NOME, X, Y])
            sink.addFeature(feat, QgsFeatureSink.FastInsert)
            if feedback.isCanceled():
                break
            feedback.setProgress(int((k+1) * total))

        feedback.pushInfo(self.tr('Operation completed successfully!', 'Operação finalizada com sucesso!'))
        feedback.pushInfo('Leandro França - Eng Cart')

        self.SAIDA = dest_id
        return {self.OUTPUT: dest_id}
    
    def postProcessAlgorithm(self, context, feedback):
        layer = QgsProcessingUtils.mapLayerFromString(self.SAIDA, context)
        # Rotulação
        labeling = LabelConf(self.tr('code'))
        layer.setLabeling(labeling)
        layer.setLabelsEnabled(True)
        layer.triggerRepaint()
        # Simbologia
        renderer = SymbolSimplePoint(layer, cor=QColor(30, 60, 255), tamanho=2.0)
        layer.setRenderer(renderer)
        layer.triggerRepaint()

        return {}

