import numbers
import os

import osmnx as ox
from networkx import NetworkXPointlessConcept
from django.conf import settings
from datetime import datetime


class OMSnx_enregistrement:

    @staticmethod
    def tableau_vers_chaine_caractere(tableau, separateur):
        """
        Cette fonction a pour but de transformer un tableau de chaîne de caractère
        vers une chaine de caractère

        :param tableau: tableau de chaine de caractère
        :param separateur: chaîne de caractère servant à séparer les cases du tableau
        :return: concaténation des chaines de caractère du tableau sépparré à l'aide d'un ou plusieurs caractères.
        La chaine de retour commence par le séparateur
        """
        retour = ""
        for chaine in tableau:
            retour = retour + separateur + chaine
        return retour

    @staticmethod
    def sauvegarde_graphique(graphique, _nom_ville, _informations_supplementaires):
        """
        Cette méthode a pour but de sauvegarder un graphique issu de OSMnx sous le format graphml dans le dossier Data


        :param _nom_ville : nom de la ville dont on veut récupérer le graphe viaire
        :param _informations_supplementaires : Les informations supplémentaires nécessaires comme le nom de la région,
        l'état, le pays ...
        :param graphique: graphique viaire à sauvegarder
        :return:
        """
        utn = ox.settings.useful_tags_node
        oxna = ox.settings.osm_xml_node_attrs
        oxnt = ox.settings.osm_xml_node_tags
        utw = ox.settings.useful_tags_way
        oxwa = ox.settings.osm_xml_way_attrs
        oxwt = ox.settings.osm_xml_way_tags
        utn = list(set(utn + oxna + oxnt))
        utw = list(set(utw + oxwa + oxwt))
        ox.settings.all_oneway = True
        ox.settings.useful_tags_node = utn
        ox.settings.useful_tags_way = utw
        ox.save_graphml(graphique, os.path.join(settings.REGISTER_DIR + "/" + _nom_ville +
                                                OMSnx_enregistrement.tableau_vers_chaine_caractere(
                                                    _informations_supplementaires, "_") + ".graphml"
                                                ))
    @staticmethod
    def recuperer_graphe_viaire_ville(_nom_ville, _informations_supplementaires):
        """
        Cette méthode permet de récupérer sur openstreetmap les données concernant le nom de la ville et les
        informations supplémentaires. Le retour peut être autre-chose qu'une ville comme une région.

        :param _nom_ville : nom de la ville dont on veut récupérer le graphe viaire
        :param _informations_supplementaires : Les informations suplémentaires nécessaire comme le nom de la région,
        l'état ou le pays
        :return: graph viaire de la ville. Les nœuds sont les intersections entre les rues et les arêtes sont les rues
        entre les intersections. En cas d'erreurs : si le nom entré ne correspond pas -1 est retourné, si la ville
        entrée ne possède pas de graph viaire -2 est retourné.
        """
        try:
            return ox.graph_from_place(_nom_ville + OMSnx_enregistrement.tableau_vers_chaine_caractere(
                _informations_supplementaires, ", "), simplify=True)
        except ValueError:
            return -1
        except NetworkXPointlessConcept:
            return -2

    @staticmethod
    def renommer_fichier_graphml(ancien_nom, nouveau_nom):
        """
        Cette méthode a pour but de renommer le fichier graphml associé à l'ancien nom obtenu avec la méthode
        chemin_vers_fichier_ville. Le nouveau nom est nouveau nom avec les modifications approtées par la méthode
        chemin_vers_fichier_ville

        :param ancien_nom : ancien nom de ville du fichier graphml
        :param nouveau_nom : nouveau nom de ville du fichier graphml
        :return:
        """
        nouveau_nom_fichier_graphml = OMSnx_enregistrement.chemin_vers_fichier_ville(nouveau_nom)
        if nouveau_nom_fichier_graphml.split("/")[-1] in os.listdir(settings.REGISTER_DIR):
            raise ValueError("Le nouveau nom de fichier : \"" + nouveau_nom_fichier_graphml + "\" est déjà pris")
        else:
            os.rename(OMSnx_enregistrement.chemin_vers_fichier_ville(ancien_nom), nouveau_nom_fichier_graphml)

    @staticmethod
    def lire_graphe(gvvi_nom):
        """
        Cette méthode permet de lire dans le dossier Data un grapml grâce au nom entré en paramètre.
        Ce paramètre doit être le nom de la ville sans le chamin ni l'extension graphml.
        Elle donne aussi à l'atribut name du graph le nom du fichier sans l'extension graphml.

        :param gvvi_nom: Le nom de la ville associé à un graphe dont nous voulons récupérer le graph.
        """
        try:
            path = OMSnx_enregistrement.chemin_vers_fichier_ville(gvvi_nom)
        except FileNotFoundError as e:
            raise e
        G = ox.load_graphml(path)
        G.name = gvvi_nom
        return G

    @staticmethod
    def chemin_vers_fichier_ville(gvvi_nom):
        """
        Cette méthode permet de créer un chemin vers un fichier de ville d'après le nom de la ville

        :param nom_ville: nom de la ville
        :return: le chemin vers le fichier Data et le nom de fichier normalement associé au nom du fichier
        """
        return os.path.join(settings.REGISTER_DIR + "/" + gvvi_nom + ".graphml")

    @staticmethod
    def ajoute_dgs(outf, attribut, valeur):
        """
        Cette méthode permet de rajouter une valeur au fichier dgs en cours d'écriture sans retour à la ligne,
        mais une tabulation à la fin. Elle permet de distinguer les valeurs à encadrer entre deux guillemets des valeurs
        représentant des nombres. Si attribut est None, on ajoute uniquement la variable valeur

        :param outf : variable d'écriture sur le fichier
        :param attribut : clef associée à la valeur à ajouter
        :param valeur : valeur à ajouter aux dgs, c'est la variable dont il faut connaitre le type
        """
        if attribut is not None:
            outf.write(str(attribut) + ":")
        if isinstance(valeur, bool):
            valeur = valeur.real
        if isinstance(valeur, numbers.Real):
            outf.write(str(valeur) + "\t")
        else:
            outf.write("\"" + str(valeur) + "\"\t")

    @staticmethod
    def sauvegarde_graphique_dgs(graphique, _nom_ville, _informations_supplementaires):
        """
        Cette méthode a pour but de sauvegarder un graphique issu de OSMnx sous le format dgs de graphstream
         dans le dossier Data

         les attributs des nœuds sont :
            - y, x : ses coordonnées x et y
            - street_count : le nombre de routes à l'intersection

         les attributs des arrêtes sont :
            - osmid : identifiant openstreetmap de la route à la laquelle appartient le morceau de route
            - ref : la référence openstreetmap de la route comme D 19 : départemental 19
            - name : le nom de la route
            - highway : le type de la route
            - oneway, reversed : la possibilité de savoir si la route est dans un sens ou dans les deux
            - length : la taille du morceau de route

        :param _nom_ville : nom de la ville dont on veut récupérer le graphe viaire
        :param _informations_supplementaires : Les informations supplémentaires nécessaires comme le nom de la région,
        l'état, le pays ...
        :param graphique: graphique viaire à sauvegarder
        :return:
        """
        G = graphique.copy()
        nom_fichier = _nom_ville + "_" + datetime.today().strftime('%Y-%m-%d')
        filename = os.path.join(
            settings.REGISTER_DIR + "/" + nom_fichier +
            OMSnx_enregistrement.tableau_vers_chaine_caractere(
                _informations_supplementaires, "_") + ".dgs"
        )

        with open(filename, 'w') as outf:
            outf.write("DGS004\n")
            outf.write("null 0 0\n")


            # stringify all the node attribute values
            for node_id, data in G.nodes(data=True):
                # identifiant openstreetmap du noeud, c'est un integer
                outf.write("an\t" + str(node_id) + "\t")
                for attr, value in data.items():
                    data[attr] = str(value)
                    OMSnx_enregistrement.ajoute_dgs(outf, attr, value)
                    # outf.write(str(attr) + ":" + str(value) + "\t")
                outf.write("\n")
            # stringify all the edge attribute values
            for node_id_a, node_id_b, data in G.edges(keys=False, data=True):
                # concatenation des identifiants des deux noeuds sous forme de string séparé par un _
                outf.write("ae \"" + str(node_id_a) + "_" + str(node_id_b) + "\"\t" + str(node_id_a) + " > " + str(
                    node_id_b) + "\t")
                for attr, value in data.items():
                    OMSnx_enregistrement.ajoute_dgs(outf, attr, value)
                    # outf.write(str(attr) + ":" + str(value) + "\t")
                outf.write("\n")


