from OSM_outils.OMSnx_enregistrement import OMSnx_enregistrement
from OSM_outils.OSMnx_recuperation_graph_information import OSMnx_recuperation_graph_information
from django import forms
from django.core.exceptions import ValidationError
from recuperateur_reseau_viaire.models import Graph_viaire_ville_information, Ville


class Graph_viaire_ville_informationForm(forms.Form):
    """
    Cette méthode permet de créer une nouvelle donnée dans la table Graph_viaire_ville_information

    """
    gvvi_nom = forms.CharField(required=True, label="nom de la ville ")

    def __init__(self, *args, **kwargs):
        """
        Cette initialisation de formulaire permet de créer la liste de nom de ville si plusieurs sont entrés dans le
        formulaire

        :param args:
        :param kwargs: dictionnaire ayant pour contenu le nom des villes envoyé par le formulaire
        """
        super(Graph_viaire_ville_informationForm, self).__init__(*args, **kwargs)
        self.validation_erreur_liste = []
        initial_arguments = kwargs.get('initial', None)
        self.liste_nom_ville = None
        if len(args) > 0 and len(args[0].getlist("gvvi_nom_ajout", default=[])) > 1:
            self.liste_nom_ville = [args[0].getlist("gvvi_nom")[0].strip()]
            for nom_ville in args[0].getlist("gvvi_nom_ajout"):
                #vérifie que toutes les villes englobées sont uniques
                nom_ville = nom_ville.strip()
                if nom_ville in self.liste_nom_ville:
                    self.validation_erreur_liste.append(ValidationError({"gvvi_nom_ajout": 'plusieurs villes englobées portent le même nom'}, code='present',
                                          params={'gvvi_nom': nom_ville}))
                self.liste_nom_ville.append(nom_ville)

    def clean(self):
        """
        Cette fonction permet de créer notre propre test de validation en fonction du retour de OSMnx
        """
        if len(self.validation_erreur_liste) != 0:
            raise ValidationError(self.validation_erreur_liste)
        cleaned_data = super(Graph_viaire_ville_informationForm, self).clean()
        if self.liste_nom_ville is not None:
            nom_ville = self.liste_nom_ville
        else :
            nom_ville = cleaned_data['gvvi_nom']
        if isinstance(nom_ville, list):
            nom_fichier = nom_ville[0]
            nom_ville = nom_ville[1:]
        else:
            nom_fichier = nom_ville
        G = self.validation_nom_ville(nom_ville, nom_fichier)
        recuperateur = OSMnx_recuperation_graph_information(G)
        OMSnx_enregistrement.sauvegarde_graphique(G, nom_fichier)
        try:
            cleaned_data = recuperateur.recuperation_information_graphe(G)
        except Exception as err:
            raise ValidationError({"gvvi_nom" : err.__str__()}, code='invalid',
                            params={'gvvi_nom': nom_ville})
        cleaned_data['gvvi_nom'] = nom_fichier
        return cleaned_data

    def validation_nom_ville(self, nom_ville, nom_fichier):
        """
                Cette fonction permet d'analyser les retours de l'argument G qui est le retour de la méthode
                OMSnx_enregistrement.recuperer_graphe_viaire_ville et de lever les erreurs.

                :param G: retour
                :raises ValidationError : si la ville est incconnue de OSMnx ou qu'elle ne possède pas de routes
        """
        ville = Ville.objects.filter(ville_nom=nom_fichier)
        if ville.exists() and Graph_viaire_ville_information.objects.filter(ville=ville[0].ville).exists():
            raise ValidationError({"gvvi_nom" : 'ville déjà enregistrée dans la base de donnée'}, code='present',
                              params={'gvvi_nom': nom_ville})
        try:
            G = OMSnx_enregistrement.lire_graphe(nom_fichier)
        except FileNotFoundError:
            try :
                G = OMSnx_enregistrement.recuperer_graphe_viaire_ville(nom_ville)
            except ValueError as v:
                raise ValidationError({"gvvi_nom" : 'ville non trouvée : ' + v.__str__().split("\"")[1]}, code='invalid',
                                      params={'gvvi_nom': nom_ville})
        return G

    def clean_nb_intersection(self):
        data = self.cleaned_data['gvvi_nb_intersection']
        return data

    def clean_nom(self):
        data = self.cleaned_data['gvvi_nom']
        return data


class Graph_viaire_ville_informationFormUpdate(forms.Form):
    """
    Cette méthode permet de modifier une nouvelle donnée dans la table Graph_viaire_ville_information

    """
    from_python = {"int": forms.IntegerField, "float": forms.FloatField, "bool": forms.BooleanField}

    def __init__(self, *args, **kwargs):
        super(Graph_viaire_ville_informationFormUpdate, self).__init__(*args, **kwargs)
        initial_arguments = kwargs.get('initial', None)
        if initial_arguments:
            graph_viaire_ville_information = initial_arguments.get('valeur', None)
            liste = False
            if isinstance(graph_viaire_ville_information, list):
                graph_viaire_ville_information = graph_viaire_ville_information[0]
                liste = True
            #on associe à chaque variable un nom de fonction de OSMnx_recuperation_graph_information
            tableau_associatif_nom_variable_nom_fonction = {}
            var_OSMnx_recuperation_graph_information = OSMnx_recuperation_graph_information(None)
            liste_fonction_recup_graph_information = var_OSMnx_recuperation_graph_information.sgbd_liste_fonction()
            for func in liste_fonction_recup_graph_information:
                retour = getattr(var_OSMnx_recuperation_graph_information, func)(None)
                nom_variable_sgbd = next(iter(retour))
                tableau_associatif_nom_variable_nom_fonction[nom_variable_sgbd] = \
                    {"nom_fonction" : func,
                    "type_retour" : type(getattr(graph_viaire_ville_information, nom_variable_sgbd)).__name__}
            if not liste:
                self.fields['gvvi_nom'] = forms.CharField(required=True, initial=graph_viaire_ville_information.ville.ville_nom,
                                                      label="nom de la ville ")
            for champ in dir(graph_viaire_ville_information):
                if champ.startswith("gvvi_") and tableau_associatif_nom_variable_nom_fonction.get(champ) is not None:
                    if not liste:
                        self.fields[champ] = self.from_python[tableau_associatif_nom_variable_nom_fonction[champ]["type_retour"]](required=True,
                                        initial=getattr(graph_viaire_ville_information, champ))
                    else:
                        self.fields[champ] = self.from_python[
                            tableau_associatif_nom_variable_nom_fonction[champ]["type_retour"]](required=False)
                    self.fields[champ + "_check"] = forms.BooleanField(required=False, label="modification fonction")

    def clean_gvvi_nom(self):
        print("changed : ", self.changed_data)
        if "gvvi_nom" in self.changed_data:
            if Ville.objects.filter(ville_nom=self.cleaned_data["gvvi_nom"]).exists():
                raise ValidationError('ville %(gvvi_nom)s déjà enregistrée dans la base de donnée',
                                      params={'gvvi_nom': self.cleaned_data["gvvi_nom"]})
            try:
                OMSnx_enregistrement.renommer_fichier_graphml(None, self.cleaned_data["gvvi_nom"])
            except ValueError:
                raise ValidationError({"gvvi_nom" : 'le fichier %(gvvi_nom)s existe déjà dans le dossier Data'},
                                      code='invalide', params={'gvvi_nom': self.cleaned_data["gvvi_nom"]})
        return self.cleaned_data["gvvi_nom"]


class Graph_viaire_ville_informationForm_delete(forms.Form):

    def __init__(self, *args, **kwargs):
        initial_arguments = kwargs.get('initial', None)
        super(Graph_viaire_ville_informationForm_delete, self).__init__(*args, **kwargs)
        if initial_arguments:
            graph_viaire_ville_information = initial_arguments.get('valeur', None)
            self.fields['gvvi_nb_intersection'] = forms.IntegerField(
                required=True, initial=graph_viaire_ville_information.gvvi_nb_intersection, disabled=True,
                label="nombre d'intersection "
            )
            self.fields['gvvi_nom'] = forms.CharField(required=True, initial=graph_viaire_ville_information.ville.ville_nom,
                                                      disabled=True, label="nom de la ville ")


class Graph_viaire_ville_informationForm_export(forms.Form):
    """
    Ce formulaire a pour but d'exporter la base de donnée sous un des formats possibles donné par la librairie import_export
    """
    choix_format_export = forms.ChoiceField(required=True, choices=[(0, "csv"), (1, "latex")])

    def __init__(self, *args, **kwargs):
        super(Graph_viaire_ville_informationForm_export, self).__init__(*args, **kwargs)
        initial_arguments = kwargs.get('initial', None)
        if initial_arguments:
            chaine_caractere_format = []
            for format in Graph_viaire_ville_information.formats_exports:
                chaine_caractere_format.append((format[0], format[1].removeprefix("get_")))
            super(Graph_viaire_ville_informationForm_export, self).__init__(*args, **kwargs)
            self.fields['choix_format_export'] = forms.ChoiceField(required=True,
                                                                   choices=chaine_caractere_format,
                                                                   initial=chaine_caractere_format[0])

    def clean_choix_format_export(self):
        data = self.cleaned_data['choix_format_export']
        return data
