# -*- 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__ = '2025-01-09'
__copyright__ = '(C) 2025 by Tiago Prudencio e Leandro França'

from qgis.PyQt.QtCore import QCoreApplication, QVariant
from qgis.core import (QgsProcessing,
                       QgsFeatureSink,
                       QgsProcessingException,
                       QgsProcessingAlgorithm,
                       QgsWkbTypes,
                       QgsCoordinateReferenceSystem,
                       QgsProcessingParameterFile,
                       QgsVectorLayer,
                       QgsFields,
                       QgsField,
                       QgsFeature,
                       QgsGeometry,
                       QgsLineString,
                       QgsMultiPolygon,
                       QgsPolygon,
                       QgsPoint,
                       QgsProcessingParameterFeatureSink)
from qgis import processing
from qgis.PyQt.QtGui import QIcon
from GeoINCRA.images.Imgs import *
from .Funcs import LerPDF
from datetime import datetime
import os

class LayersFromPDF(QgsProcessingAlgorithm):

    PDF = 'PDF'
    VERTICE = 'VERTICE'
    LIMITE = 'LIMITE'
    PARCELA = 'PARCELA'

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

    def createInstance(self):
        return LayersFromPDF()

    def name(self):
        return 'LayersFromPDF'.lower()

    def displayName(self):

        return self.tr('Memorial do Sigef para Camadas')

    def group(self):

        return self.tr(self.groupId())

    def groupId(self):

        return ''

    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,memorial,pdf,conversão,tranformar,descritivo,documento,cartório,matrícula,regularização,fundiária,layer,geopackage'.split(',')

    def shortHelpString(self):
        txt = 'Esta ferramenta faz a leitura do arquivo <b>PDF do Memorial Descritivo Tabular</b> do SIGEF/INCRA, convertendo nas camadas vétice (ponto), limite (linha) e parcela (polígono) no padrão GeoRural.'
        footer = '''<div>
                      <div align="center">
                      <img style="width: 100%; height: auto;" src="data:image/jpg;base64,'''+ INCRA_GeoOne +'''
                      </div>
                      <div align="right">
                      <p align="right">
                      <a href="https://geoone.com.br/pvgeoincra2/"><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/georreferenciamento-de-imveis-rurais-com-o-plugin-geoincra-1690158094835"><span style="font-weight: bold;">Acesse seu curso na GeoOne</span></a>
                      </p>
                      <a target="_blank" rel="noopener noreferrer" href="https://geoone.com.br/"><img height="80" title="GeoOne" src="data:image/png;base64,'''+ GeoOne +'''"></a>
                      <p><i>"Mapeamento automatizado, fácil e direto ao ponto é na GeoOne!"</i></p>
                      </div>
                    </div>'''
        return txt + footer

    def initAlgorithm(self, config=None):

        self.addParameter(
        QgsProcessingParameterFile(
            self.PDF,
            self.tr('Memorial Descritivo Sigef (PDF)'),
            fileFilter= 'Arquivo PDF (*.pdf)'
            )
        )

        self.addParameter(
            QgsProcessingParameterFeatureSink(
                self.VERTICE,
                self.tr('Vértices do Memorial')
            )
        )

        self.addParameter(
            QgsProcessingParameterFeatureSink(
                self.LIMITE,
                self.tr('Limites do Memorial')
            )
        )

        self.addParameter(
            QgsProcessingParameterFeatureSink(
                self.PARCELA,
                self.tr('Parcela do Memorial')
            )
        )

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

        pdf_path = self.parameterAsString(
            parameters,
            self.PDF,
            context
        )

        if pdf_path is None:
            raise QgsProcessingException(self.invalidSourceError(parameters, self.PDF))


        # Sistema de Referência de Coordenadas
        SRC = QgsCoordinateReferenceSystem('EPSG:4674')

        # Criar camada de Pontos
        feedback.pushInfo('Criando camada Vértice...')
        Fields1 = QgsFields()
        itens  = {   'indice': QVariant.Int,
                     'vertice': QVariant.String,
                     'tipo_verti': QVariant.String,
                     'metodo_pos' : QVariant.String,
                     'sigma_x' : QVariant.Double,
                     'sigma_y' : QVariant.Double,
                     'sigma_z' : QVariant.Double,
                     }
        for item in itens:
            Fields1.append(QgsField(item, itens[item]))

        (sink1, dest_id1) = self.parameterAsSink(
            parameters,
            self.VERTICE,
            context,
            Fields1,
            QgsWkbTypes.PointZ,
            SRC
        )
        if sink1 is None:
            raise QgsProcessingException(self.invalidSinkError(parameters, self.VERTICE))

        # Criar camada de Linhas
        feedback.pushInfo('Criando camada Limite...')
        Fields2 = QgsFields()
        itens  = {   'tipo': QVariant.String,
                     'confrontan': QVariant.String,
                     'cns': QVariant.String,
                     'matricula' : QVariant.String,
                     }
        for item in itens:
            Fields2.append(QgsField(item, itens[item]))

        (sink2, dest_id2) = self.parameterAsSink(
            parameters,
            self.LIMITE,
            context,
            Fields2,
            QgsWkbTypes.LineStringZ,
            SRC
        )
        if sink2 is None:
            raise QgsProcessingException(self.invalidSinkError(parameters, self.LIMITE))

        # Criar camada de Polígono
        feedback.pushInfo('Criando camada Parcela...')
        Fields3 = QgsFields()
        itens  = {   'nome': QVariant.String,
                     'nat_serv': QVariant.Int,
                     'pessoa': QVariant.Int,
                     'cpf_cnpj' : QVariant.String,
                     'denominacao': QVariant.String,
                     'situacao': QVariant.Int,
                     'natureza': QVariant.Int,
                     'sncr': QVariant.String,
                     'matricula': QVariant.String,
                     'cod_cartorio': QVariant.String,
                     'municipio': QVariant.String,
                     'uf': QVariant.String,
                     'resp_tec': QVariant.String,
                     'reg_prof': QVariant.String,
                     'data': QVariant.Date,
                     }
        for item in itens:
            Fields3.append(QgsField(item, itens[item]))

        (sink3, dest_id3) = self.parameterAsSink(
            parameters,
            self.PARCELA,
            context,
            Fields3,
            QgsWkbTypes.MultiPolygonZ,
            SRC
        )
        if sink3 is None:
            raise QgsProcessingException(self.invalidSinkError(parameters, self.PARCELA))
        
        # Ler PDF
        dic, lista_cod, dic_cod, ind_encravado = LerPDF(pdf_path, feedback = feedback)

        if len(lista_cod) == 0 or dic['Denominação:'] == '':
            raise QgsProcessingException('PDF de entrada não é um Memorial do Sigef!')

        feedback.pushInfo('Alimentando camada Vértice (pontos)...')
        cont = 0
        pnts = {}
        for codigo in lista_cod:
            lon = dic_cod[codigo]['lon'].replace('°', ' ').replace('"', ' ').replace("'", ' ').replace(',','.').split(' ')
            X = (abs(float(lon[0])) + float(lon[1])/60 + float(lon[2])/3600)*(-1 if dic_cod[lista_cod[-1]]['lon'][0] == '-' else 1)
            lat = dic_cod[codigo]['lat'].replace('°', ' ').replace('"', ' ').replace("'", ' ').replace(',','.').split(' ')
            Y = (abs(float(lat[0])) + float(lat[1])/60 + float(lat[2])/3600)*(-1 if dic_cod[lista_cod[-1]]['lat'][0] == '-' else 1)
            Z = float(dic_cod[codigo]['h'].replace(',','.'))
            feat = QgsFeature(Fields1)
            pnts[codigo] = QgsPoint(X,Y,Z)
            feat.setGeometry(QgsGeometry(QgsPoint(X,Y,Z)))
            cont += 1
            feat['indice'] = cont
            feat['vertice'] = codigo
            feat['tipo_verti'] = codigo.split('-')[1]
            feat['metodo_pos'] = ''
            feat['sigma_x'] = None
            feat['sigma_y'] = None
            feat['sigma_z'] = None
            sink1.addFeature(feat, QgsFeatureSink.FastInsert)
            if feedback.isCanceled():
                break

        feedback.pushInfo('Alimentando camada Limite (linhas)...')
        # Se encravado, fatiar lista_cod
        def fatiar_lista(a, ind):
            ind = [0] + ind + [len(a)]
            return [a[ind[i]:ind[i+1]] for i in range(len(ind)-1)]

        if len(ind_encravado) > 0:
            listas_fat = fatiar_lista(lista_cod, ind_encravado)
        else:
            listas_fat = [lista_cod]

        for lista_cod_fat in listas_fat:
            linha = []
            anterior_cns = dic_cod[lista_cod_fat[0]]['cns'].replace('NULL','')
            anterior_mat = dic_cod[lista_cod_fat[0]]['matr'].replace('NULL','')
            anterior_confr = dic_cod[lista_cod_fat[0]]['confr'].replace('NULL','')

            for k, codigo in enumerate(lista_cod_fat):
                linha += [pnts[codigo]]
                cns = dic_cod[codigo]['cns'].replace('NULL','')
                matricula = dic_cod[codigo]['matr'].replace('NULL','')
                confrontante = dic_cod[codigo]['confr'].replace('NULL','')
                if ((cns+matricula+confrontante) != (anterior_cns+anterior_mat+anterior_confr)):
                    feat = QgsFeature(Fields2)
                    feat.setGeometry(QgsGeometry(QgsLineString(linha)))
                    feat['tipo'] = ''
                    feat['confrontan'] = anterior_confr
                    feat['cns'] = anterior_cns
                    feat['matricula'] = anterior_mat
                    sink2.addFeature(feat, QgsFeatureSink.FastInsert)
                    anterior_mat = matricula
                    anterior_cns = cns
                    anterior_confr = confrontante
                    linha = [pnts[codigo]]
                if feedback.isCanceled():
                    break

            # Último segmento
            feat = QgsFeature(Fields2)
            if (cns+matricula+confrontante) == (anterior_cns+anterior_mat+anterior_confr):
                linha += [pnts[lista_cod_fat[0]]]
                feat['tipo'] = ''
                feat['confrontan'] = anterior_confr
                feat['cns'] = anterior_cns
                feat['matricula'] = anterior_mat
            else:
                linha = [pnts[lista_cod_fat[k]], pnts[lista_cod_fat[0]]]
                feat['tipo'] = ''
                feat['confrontan'] = confrontante
                feat['cns'] = cns
                feat['matricula'] = matricula
            feat.setGeometry(QgsGeometry(QgsLineString(linha)))
            sink2.addFeature(feat, QgsFeatureSink.FastInsert)

        # Conversões
        dic_nat_ser = {'Particular':1, 'Contrato com Administração Pública':2}
        dic_pessoa, dic_situacao  = {'Física':1, 'Jurídica':2}, {'Imóvel Registrado':1, 'Área Titulada não Registrada':2, 'Área não Titulada':3}
        dic_natureza = {'Assentamento':1,'Assentamento Parcela':2,'Estrada':3,'Ferrovia':4,'Floresta Pública':5,'Gleba Pública':6,'Particular':7,'Perímetro Urbano':8,'Terra Indígena':9,'Terreno de Marinha':10,'Terreno Marginal':11,'Território Quilombola':12,'Unidade de Conservação':13}

        # Data da certificação
        try:
            dataAss = datetime.strptime(dic['Data Certificação:'], '%d/%m/%Y %H:%M')
        except:
            try:
                dataAss = datetime.strptime(dic['Data Certificação:'], '%d/%m/%y %H:%M')
            except:
                dataAss = None
        if dataAss:
            dataCert = dataAss.isoformat()
        else:
            try:
                dataAss = datetime.strptime(dic['Data da Geração:'], '%d/%m/%Y %H:%M')
            except:
                try:
                    dataAss = datetime.strptime(dic['Data da Geração:'], '%d/%m/%y %H:%M')
                except:
                    dataAss = None
            if dataAss:
                dataCert = dataAss.isoformat()
            else:
                dataCert = datetime.now().date().isoformat()

        feedback.pushInfo('Alimentando camada Parcela (polígono)...')
        feat = QgsFeature(Fields3)
        feat['nome'] = dic['Proprietário(a):'] if dic['Proprietário(a):'] else dic['Proprietário:']
        feat['pessoa'] =  1 if dic['CPF:'] != '' else 2
        feat['cpf_cnpj'] = dic['CPF:'] if dic['CPF:'] != '' else dic['CNJP:']
        feat['denominacao'] = dic['Denominação:']
        feat['natureza'] = dic_natureza[dic['Natureza da Área:']]
        feat['sncr'] = dic['Código INCRA/SNCR:']
        feat['matricula'] = dic['Matrícula do imóvel:'] if dic['Matrícula do imóvel:'] else dic['Transcrição do imóvel:']
        feat['cod_cartorio'] = dic['Cartório (CNS):']
        feat['municipio'] = dic['Município/UF:'].split('-')[0]
        feat['uf'] = dic['Município/UF:'].split('-')[-1]
        feat['resp_tec'] = dic['Responsável Técnico(a):'] if dic['Responsável Técnico(a):'] else dic['Responsável Técnico:']
        feat['reg_prof'] = dic['Conselho Profissional:']
        feat['data'] = dataCert


        # Lista de pontos
        for k, lista_cod_fat in enumerate(listas_fat):
            lista_pontos = []
            for codigo in lista_cod_fat:
                lista_pontos += [pnts[codigo]]
            lista_pontos+[lista_pontos[0]]

            # Anel externo
            if k == 0:
                anel_ext = QgsLineString(lista_pontos)
                pol = QgsPolygon(anel_ext)
            else: # interno
                anel_int = QgsLineString(lista_pontos)
                pol.addInteriorRing(anel_int)

        mPol = QgsMultiPolygon()
        mPol.addGeometry(pol)
        newGeom = QgsGeometry(mPol)
        feat.setGeometry(newGeom)
        sink3.addFeature(feat, QgsFeatureSink.FastInsert)

        return {self.VERTICE: dest_id1,
                self.LIMITE: dest_id2,
                self.PARCELA: dest_id3}
