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 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")


