# -*- coding: utf-8 -*-
"""
Created on 1 oct. 2015

version 3.0.0 , 26/11/2018

@author: AChang-Wailing
"""

import sqlite3
import re
from .core.RipartLoggerCl import RipartLogger
from qgis.core import QgsGeometry, QgsCoordinateReferenceSystem, QgsCoordinateTransform, QgsProject, \
    QgsEditorWidgetSetup
from .RipartHelper import RipartHelper
from .core.BBox import BBox
from .core import ConstanteRipart as cst
from .core.NoProfileException import NoProfileException
from .Contexte import Contexte
from .core.ProgressBar import ProgressBar


class ImporterRipart(object):
    """Importation des remarques dans le projet QGIS
    """
    logger = RipartLogger("ImporterRipart").getRipartLogger()

    # le contexte de la carte
    context = None

    # barre de progression (des remarques importées)
    progress = None
    progressVal = 0

    def __init__(self, context):
        """
        Constructor
        Initialisation du contexte et de la progressbar
        
        :param context: le contexte de la carte actuelle
        :type context: Contexte
        """
        self.context = context

    def doImport(self):
        """Téléchargement et import des remarques sur la carte   
        """
        self.logger.debug("doImport")

        params = {}  # paramètres pour la requête au service Ripart

        if self.context.profil.geogroup.name is None:
            raise NoProfileException(
                "Vous n'êtes pas autorisé à effectuer cette opération. Vous n'avez pas de profil actif.")

        # filtre spatial
        bbox = BBox(self.context)
        box = bbox.getFromLayer(RipartHelper.load_CalqueFiltrage(self.context.projectDir).text)

        # si la box est à None alors, l'utilisateur veut extraire France entière
        # si la box est égale 0.0 pour ces 4 coordonnées alors l'utilisateur
        # ne souhaite pas extraire les données France entière et on sort
        if box is not None and box.XMax == 0.0 and box.YMax == 0.0 and box.XMin == 0.0 and box.YMin == 0.0:
            raise Exception("Les coordonnées de la box entourant la zone d'extraction sont égales à 0, "
                            "veuillez importer une nouvelle zone.")

        filtreLay = None
        filtre = RipartHelper.load_CalqueFiltrage(self.context.projectDir).text
        if filtre is not None and len(filtre.strip()) > 0:
            self.logger.debug("Spatial filter :" + filtre)
            filtreLay = self.context.getLayerByName(filtre)

        message = "Placement des signalements sur la carte"
        self.progress = ProgressBar(200, message)

        # vider, détruire et créer les tables de l'espace collaboratif
        self.context.emptyAllRipartLayers()

        # Ajout des couches dans le projet
        self.context.addRipartLayersToMap()

        pagination = RipartHelper.load_ripartXmlTag(self.context.projectDir, RipartHelper.xml_Pagination, "Map").text
        if pagination is None:
            pagination = RipartHelper.defaultPagination

        date = RipartHelper.load_ripartXmlTag(self.context.projectDir, RipartHelper.xml_DateExtraction, "Map").text
        date = RipartHelper.formatDate(date)

        groupFilter = RipartHelper.load_ripartXmlTag(self.context.projectDir, RipartHelper.xml_Group, "Map").text
        if groupFilter == 'true':
            groupId = self.context.profil.geogroup.id

            params['group'] = str(groupId)

        self.context.client.setIface(self.context.iface)

        if box is not None:
            params['box'] = box.boxToString()

        params['pagination'] = pagination
        params['updatingDate'] = date

        rems = self.context.client.getGeoRems(params)

        # Filtrage spatial affiné des remarques.
        if box is not None:
            remsToKeep = {}

            for key in rems:
                ptx = rems[key].position.longitude
                pty = rems[key].position.latitude
                pt = "POINT(" + ptx + " " + pty + ")"
                ptgeom = QgsGeometry.fromWkt(pt)

                if RipartHelper.isInGeometry(ptgeom, filtreLay):
                    remsToKeep[key] = rems[key]

        else:
            remsToKeep = rems

        cnt = len(remsToKeep)
        if cnt == 0:
            self.progress.close()
            self.context.iface.messageBar().pushMessage("", "Pas de signalements extraits ou intersectant cette zone.",
                                                        level=2, duration=5)
            return

        try:
            i = 100
            nbRem = 0
            try:
                # self.context.conn = spatialite_connect(self.context.dbPath)
                self.context.conn = sqlite3.connect(self.context.dbPath)
                self.context.conn.enable_load_extension(True)
                self.context.conn.execute("SELECT load_extension('mod_spatialite')")
                for remId in remsToKeep:
                    bCommit = False
                    if remId == '618195' or remId == '618197':
                        debug = True
                    if nbRem == 200:
                        bCommit = True
                        nbRem = 0
                    RipartHelper.insertRemarques(self.context.conn, remsToKeep[remId], bCommit)
                    i += 1
                    nbRem += 1
                    if cnt > 0:
                        self.progressVal = int(round(i * 100 / cnt))
                        self.progress.setValue(self.progressVal)

                self.context.conn.commit()

            except Exception as e:
                self.logger.error(format(e))
                raise
            finally:
                self.context.conn.close()

            if cnt > 1:
                remLayer = self.context.getLayerByName(RipartHelper.nom_Calque_Signalement)
                remLayer.updateExtents(True)
                box = remLayer.extent()
                self.setMapExtent(box)

            elif filtreLay is not None:
                box = filtreLay.extent()
                self.setMapExtent(box)

            # Résultat
            self.showImportResult(cnt)

            # Modification du formulaire pour afficher l'attribut "Thèmes" sous forme de "Vue JSON"
            # Vue par défaut : Arborescence
            # Formater le JSON : Indenté
            # self.setFormAttributes()
            self.progress.close()

        except Exception as e:
            self.progress.close()
            raise

        finally:
            self.progress.close()

    def setFormAttributes(self):
        listLayers = QgsProject.instance().mapLayersByName(RipartHelper.nom_Calque_Signalement)
        if len(listLayers) == 0:
            return
        features = None
        for layer in listLayers:
            features = layer.getFeatures()
            break
        fields = None
        for feature in features:
            fields = feature.fields()
            break
        if fields is None or len(fields) == 0:
            return

        index = -1
        for field in fields:
            name = field.name()
            if name == 'Thèmes':
                index = fields.indexOf(name)
                break
        # Si l'attribut "Thèmes" n'existe pas, on ne fait rien, on laisse faire QGIS
        if index == -1:
            return
        # modification du formulaire QGIS pour l'attribut "Thèmes"
        # Type:JsonEdit
        QgsEWS_type = 'JsonEdit'
        # Config:{'DefaultView': 1, 'FormatJson': 0}
        QgsEWS_config = {'DefaultView': 1, 'FormatJson': 0}
        setup = QgsEditorWidgetSetup(QgsEWS_type, QgsEWS_config)
        listLayers[0].setEditorWidgetSetup(index, setup)

    def setMapExtent(self, box):
        """set de l'étendue de la carte
        
        :param box: bounding box
        """
        # Cas particuliers
        crs_map = {
            'LAMB93': 2154,
            'WGS84': 4326
        }
        source_crs = QgsCoordinateReferenceSystem.fromEpsgId(cst.EPSGCRS)
        mapCrs = self.context.mapCan.mapSettings().destinationCrs().authid()
        tmp = mapCrs.split(":")[1]
        if re.fullmatch(r"\d+", tmp):
            psgCode = int(tmp)
        else:
            psgCode = crs_map.get(tmp)
        dest_crs = QgsCoordinateReferenceSystem.fromEpsgId(psgCode)
        transform = QgsCoordinateTransform(source_crs, dest_crs, QgsProject.instance())
        new_box = transform.transformBoundingBox(box)

        # distance pour le buffer: 10% de la distance minimale (hauteur ou largeur)
        dist = min(new_box.width(), new_box.height()) * 0.1
        # zoom sur la couche Signalement
        self.context.mapCan.setExtent(new_box.buffered(dist))

    def showImportResult(self, cnt):
        """Résultat de l'import
        
        :param cnt: le nombre de remarques importées
        :type cnt: int
        """
        submit = self.context.countRemarqueByStatut(cst.STATUT.submit.__str__())
        pending = self.context.countRemarqueByStatut(cst.STATUT.pending.__str__()) + \
                  self.context.countRemarqueByStatut(cst.STATUT.pending0.__str__()) + \
                  self.context.countRemarqueByStatut(cst.STATUT.pending1.__str__()) + \
                  self.context.countRemarqueByStatut(cst.STATUT.pending2.__str__())
        reject = self.context.countRemarqueByStatut(cst.STATUT.reject.__str__()) + \
                 self.context.countRemarqueByStatut(cst.STATUT.reject0.__str__())
        valid = self.context.countRemarqueByStatut(cst.STATUT.valid.__str__()) + self.context.countRemarqueByStatut(
            cst.STATUT.valid0.__str__())

        resultMessage = "Extraction réussie avec succès de " + str(cnt) + " signalement(s) depuis le serveur \n" + \
                        "avec la répartition suivante : \n\n" + \
                        "- " + str(submit) + " signalement(s) nouveau(x).\n" + \
                        "- " + str(pending) + " signalement(s) en cours de traitement.\n" + \
                        "- " + str(valid) + " signalement(s)  validé(s).\n" + \
                        "- " + str(reject) + " signalement(s) rejeté(s).\n"

        RipartHelper.showMessageBox(resultMessage)
