# -*- coding: iso-8859-1 -*-
from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0)

##--------------------------------------#######
#                 Sauvegarde                  #
##--------------------------------------#######
#    WxGeometrie
#    Dynamic geometry, graph plotter, and more for french mathematic teachers.
#    Copyright (C) 2005-2007  Nicolas Pourcelot
#
#    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.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

# version unicode


# importation des librairies classiques
from LIB import *
import API
from xml.dom.minidom import parseString
from xml.parsers.expat import ExpatError


#----------------------------------
#     DOM pour lire un fichier
#----------------------------------



class FichierGEO:
    u"""Classe utilisee pour manipuler un fichier .geo.
    On peut aussi utiliser le DOM XML, mais les spécifications du format de fichier .geo
    sont plus restreintes, et cette classe offre une surcouche au DOM XML pour un accès plus simple."""
    
    def __init__(self, texte = "", encoding = "utf-8", **args):
        self.infos = args
        self.encoding = encoding
        self.contenu = {}
        for attribut, defaut in [("type", "Fichier WxGeometrie"), ("version", param.version), ("module", ""), ("nom", "")]:
            setattr(self, attribut, self.infos.get(attribut, defaut))

        if texte: self.importer(texte)

            
                        
    def importer(self, texte):
        def contenu_node(node):
            # Le contenu d'une node (contenu entre 2 balises) est soit du texte, soit (ou exclusif!) d'autres balises.
            # Le contenu autorise est ainsi passablement plus restreint que dans la specification XML.
            # Pour plus de detail, lire la doc sur le format de fichier.
            
            # 1er cas: contenu vide -> assimile a du texte.
            if not node.childNodes:
                return ""

            # 2eme cas: le contenu est purement du texte.
            if not "Element" in [subnode.__class__.__name__ for subnode in node.childNodes]:
                return node.childNodes[0].nodeValue.strip("\n").replace("&lt;", "<").replace("&gt;", ">").replace("&amp;", "&").encode(param.encodage) # un certain nombres de fonctions python supportent encore mal l'unicode (par exemple, f(**{u"a":3}) ne fonctionne pas)
                                       
            # 3eme cas: le contenu est un ensemble de balises -> regroupees dans un dictionnaire.
            # ce qui n'est pas entre deux balises est ignore.
            dico = {}
            for elt in node.childNodes:
                if elt.__class__.__name__ == "Element":
                    contenu = contenu_node(elt)
                    if dico.has_key(elt.nodeName):
                        dico[elt.nodeName] += [contenu]
                    else:
                        dico[elt.nodeName] = [contenu]
            return dico


        xml = parseString(texte)
        self.encoding = xml.encoding
        self.document = xml.childNodes[0]
        self.infos = {} # contient tous les attributs du document
        for attribut in self.document._attrs.keys():
            self.infos[attribut] = self.document._attrs[attribut].value
        for attribut in ("type", "version", "module", "nom"):
            setattr(self, attribut, self.infos.get(attribut, ""))

        self.contenu = contenu_node(self.document)





    def exporter(self):
        def convertir_contenu(dictionnaire):
            texte = ""
            for balise in dictionnaire.keys():
                for elt in dictionnaire[balise]:
                    texte += "<%s>\n" %balise
                    if isinstance(elt, (str, unicode)):
                        texte += elt.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").strip("\n") + "\n"
                    elif isinstance(elt, dict):
                        texte += convertir_contenu(elt)
                    texte += "</%s>\n" %balise
                    
            return texte

        texte = u"<?xml version='1.0' encoding='%s'?>\n" %self.encoding
        texte += u"<Document type='%s' version='%s' module='%s'>\n" %(self.type, self.version, self.module)
        texte += convertir_contenu(self.contenu)
        texte += u"</Document>"
        return texte
        

    def ajouter(self, nom, racine = None, contenu = None):
        u"""Ajoute une ou plusieurs nouvelle(s) node(s) nommée(s) 'nom' à 'racine', leur contenu étant donné par 'contenu'.
        Renvoie le contenu, ou la nouvelle racine crée (si le contenu était vide)."""
        if racine is None:
            racine = self.contenu
        if contenu is None:
            contenu = {}
        
        if not racine.has_key(nom):
            racine[nom] = []
        racine[nom].append(contenu)
        return contenu
        
        
    def ecrire(self, path, zip = False):
        u"""Ecrit dans un fichier dont l'adresse est donnée par 'path'.
        
        L'encodage est fixé par 'self.encoding'.
        Eventuellement, le contenu peut-être compressé au format zip."""
        
        contenu = self.exporter().encode(self.encoding)
        f = None
        rep = os.path.split(path)[0]
        if not os.path.exists(rep):
            os.makedirs(rep)
        try:
            if zip:
                f = zipfile.ZipFile(path, "w", zipfile.ZIP_DEFLATED)
                f.writestr("content.geo", contenu)
            else:
                f = open(path, "w")
                f.write(contenu)
        finally:
            if f is not None:
                f.close()
        
        
    def ouvrir(self, path, zip = None):
        u"""Retourne un objet FichierGEO à partir du fichier dont l'adresse est donnée par 'path'.
        
        Si l'attribut 'zip' n'est pas fixé, la détection est automatique."""
    
        f = None
        if not os.path.exists(path):
            return None, u"Le fichier n'existe pas."
        try:
            f = open(path, "rU")
        except IOError:
            print_error()
            return None, u"L'accès au fichier a été refusé."
        except UnicodeError:
            print_error()
            return None, u"Caractères non reconnus."
        except:
            print_error()
            return None, u"Impossible d'ouvrir le fichier."
        try:
            texte = f.read()
        finally:
            f.close()
        try:
            parseString(texte)
        except ExpatError:
            try:
                f = zipfile.ZipFile(path, "r")
                texte = f.read("content.geo")
            finally:
                f.close()
        self.importer(texte)
        
        # Filtre d'import pour les versions antérieures
        if self.version_interne() < self.version_interne(param.version):
            API.filtre_versions_anterieures(self)
        
        return self, u"Le fichier %s a bien été ouvert." %path


    def version_interne(self, version = None):
        if version is None:
            version = self.version
        version = version.strip().lower()
        version = version.replace("alpha", "a").replace("beta", "b").replace("release candidate", "rc").replace("releasecandidate", "rc")
        version = version.replace("a", " -3 ").replace("b", " -2 ").replace("rc", " -1 ").replace(".", " ")
        return [int(n) for n in re.split("[ ]+",  version)]
        
    

##def __old__ouvrir_fichierGEO(path):
##    u"Désuet. Utiliser plutôt FichierGEO().ouvrir(path)."
##    try:
##        f=open(path,"rU")
##    except IOError:
##        return None, u"Le fichier n'existe pas, ou est inaccessible."
##    except UnicodeError:
##        return None, u"Caractères non reconnus."
##    except:
##        return None, u"Impossible d'ouvrir le fichier."
##    try:
##        texte = f.read()
##    finally:
##        f.close()
##    fgeo = FichierGEO(texte)
##    return fgeo, u"Le fichier %s a bien été ouvert." %path


def ouvrir_fichierGEO(path):
    u"Alias un peu désuet de 'FichierGEO().ouvrir(path)'."
    return FichierGEO().ouvrir(path)
