/*
editeurconfig - Editeur de Fichiers de Config de Jaxe

Copyright (C) 2011 Observatoire de Paris-Meudon

Ce programme est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier conformément aux dispositions de la Licence Publique Générale GNU, telle que publiée par la Free Software Foundation ; version 2 de la licence, ou encore (à votre choix) toute version ultérieure.

Ce programme est distribué dans l'espoir qu'il sera utile, mais SANS AUCUNE GARANTIE ; sans même la garantie implicite de COMMERCIALISATION ou D'ADAPTATION A UN OBJET PARTICULIER. Pour plus de détail, voir la Licence Publique Générale GNU .

Vous devez avoir reçu un exemplaire de la Licence Publique Générale GNU en même temps que ce programme ; si ce n'est pas le cas, écrivez à la Free Software Foundation Inc., 675 Mass Ave, Cambridge, MA 02139, Etats-Unis.
*/

package editeurconfig;

import javax.xml.transform.dom.*;
import java.io.IOException.*;
import javax.xml.transform.stream.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.validation.SchemaFactory;
import javax.xml.XMLConstants;

import org.w3c.dom.*;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import java.util.*;

/**
 * Classe principale, permet d'ouvrir un fichier ou de créer un nouveau fichier de config
 */
public class EditeurFichierConfig extends JFrame {
    
    private Document doc;
    private Element racine;
    private String encodage = "ISO-8859-1";
    
    public static boolean modif = false;
    
    public File fsave = null; // référence sur le disque vers le fichier XML

    private ArrayList<String> elementsSchemaSimple = new ArrayList<String>();

    public EditeurFichierConfig() {
        super(Strings.get("titre.EditeurFichierConfig"));
        doc = null;
        racine = null;
        
        setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
        
        affichageMenuInitial();
        
        setVisible(true);
    }
    
    /**
     * Affiche le menu principale, nouveau, ouvrir ou quitter
     */
    public void affichageMenuInitial() {
        final JPanel panelFenetre = new JPanel(new FlowLayout());
        panelFenetre.removeAll();
        add(panelFenetre);
        
        final JButton bNouveau = new JButton(new AbstractAction(Strings.get("bouton.Nouveau")) {
            public void actionPerformed(ActionEvent e) {
                nouveau();
            }
        });
        panelFenetre.add(bNouveau);
        
        final JButton bOuvrir = new JButton(new AbstractAction(Strings.get("bouton.Ouvrir")) {
            public void actionPerformed(ActionEvent e) {
                ouvrir();
            }
        });
        panelFenetre.add(bOuvrir);
        
        final JButton bQuitter = new JButton(new AbstractAction(Strings.get("bouton.Quitter")) {
            public void actionPerformed(ActionEvent e) {
                quitter();
            }
        });
        panelFenetre.add(bQuitter);
        
        getRootPane().setDefaultButton(bNouveau);
        ajusterTailleFenetre();
        
    }
    
    public void ajusterTailleFenetre() {
        pack();
        final Dimension dim = getSize();
        final Dimension ecran = getToolkit().getScreenSize();
        setLocation((ecran.width - dim.width)/2, (ecran.height - dim.height)/2);
    }
    
    
    /**
     * Crée un nouveau fichier de config
     */
    private void nouveau() {
        creationRacine();
        setVisible(false);
        new DefinirLaLangue(this, doc, racine);
    }
    
    /**
     * Ouvre le fichier de config
     */
    private void ouvrir() {
        JFileChooser c = new JFileChooser();
		c.setFileFilter(new FileFilter() {
			public boolean accept(File f) {
				if (f.isDirectory())
					return true;
				String s = f.getName();
				int i = s.lastIndexOf('.');

				if (i > 0 && i < s.length() - 1)
					if (s.substring(i + 1).toLowerCase().equals("xml"))
						return true;

				return false;
			}

			public String getDescription() {
				return Strings.get("texte.FichiersXML");
			}
		});
		
        if (c.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
			File f = c.getSelectedFile();
			ouvrir(f);
		}
    }
    
    /**
     * Ouvre le fichier f et charge le document DOM
     */
    public void ouvrir(final File f) {
        URL urlXML;
        try {
            urlXML = f.toURI().toURL();
        } catch (final MalformedURLException ex) {
            erreur(ex);
            return;
        }
        doc = lectureDocumentXML(urlXML);
        if (doc != null) {
            setTitle(f.getName());
            racine = doc.getDocumentElement();
            ListeElements listeElements = null;
            
            Element langage = Outils.premierEnfantDeNom(racine, "LANGAGE");
            if (langage != null) {
                Element fichier_schema = Outils.premierEnfantDeNom(langage, "FICHIER_SCHEMA");
                if (fichier_schema != null) {
                    String nomFichierSchema = fichier_schema.getAttribute("nom");
                    try {
                        URL urlSchema = new URL(urlXML,nomFichierSchema);
                        URI uriFic = null;
                        try {
                            uriFic = urlSchema.toURI();
                        }
                        catch (URISyntaxException use) {
                            System.err.println(use);
                        }
                        File fi = new File(uriFic);
                        if (fi.exists()) {
                            listeElements = new ListeElements(urlSchema, null, null);
                            setVisible(false);
                            new FenetreEdition(this, f, doc, racine, listeElements);
                        }
                        else
                            JOptionPane.showMessageDialog(this, Strings.get("message.SchemaInexistant"), Strings.get("titre.Erreur"), JOptionPane.ERROR_MESSAGE);
                            
                    } catch(MalformedURLException ex) {
                        System.err.println("Erreur urlFichier "+ex);
                        
                    }
                }
                else {
                    Element schema_simple = Outils.premierEnfantDeNom(langage, "SCHEMA_SIMPLE");
                    if (schema_simple != null) {
                        listeElements = new ListeElements(null, null, schema_simple);
                        setVisible(false);
                        new FenetreEdition(this, f, doc, racine, listeElements);
                    }
                    else
                        JOptionPane.showMessageDialog(this, Strings.get("message.SchemaInexistant"), Strings.get("titre.Erreur"), JOptionPane.ERROR_MESSAGE);
                }
            }
            else 
                JOptionPane.showMessageDialog(this, Strings.get("message.SchemaInexistant"), Strings.get("titre.Erreur"), JOptionPane.ERROR_MESSAGE);
        }
    }
    
    
    /**
     * Crée la racine du document XML
     */
    private void creationRacine() {
        try {
            final DocumentBuilder docbuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            doc = docbuilder.newDocument();
        } catch (final ParserConfigurationException ex) {
            erreur(ex);
            return;
        }
        racine = doc.createElement("CONFIG_JAXE");
        doc.appendChild(racine);
    }
    
    
    /**
     * Lit le document XML à partir d'une URL, vérification si le document est valide, gestion des erreurs
     */
    private Document lectureDocumentXML(final URL url) {
        Document ddoc = null;
        //Element langage = Outils.premierEnfantDeNom(racine, "LANGAGE");
        //Element fichier_schema = Outils.premierEnfantDeNom(langage, "FICHIER_SCHEMA");
        //String fichierSchema = "XPAGES.xsd";//fichier_schema.getAttribute("nom");
        
        try {
            final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            //dbf.setSchema(SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(new URL(url, fichierSchema)));
            dbf.setIgnoringElementContentWhitespace(true);
            final DocumentBuilder docbuilder = dbf.newDocumentBuilder();
            docbuilder.setErrorHandler( new ErrorHandler() {
                public void warning(SAXParseException ex) throws SAXException {
                    String infos = "Petite erreur de syntaxe dans le fichier XML:\n";
                    infos += ex.getMessage() + " à la ligne " + ex.getLineNumber();
                    erreur(infos);
                }
                public void error(SAXParseException ex) throws SAXException {
                    throw(ex);
                }
                public void fatalError(SAXParseException ex) throws SAXException {
                    throw(ex);
                }
            } );
            ddoc = docbuilder.parse(url.toExternalForm());
            if (ddoc.getXmlEncoding() != null)
                encodage = ddoc.getXmlEncoding();
        } catch (final SAXException ex) {
            String infos = "Erreur de syntaxe dans le fichier XML:\n";
            infos += ex.getMessage();
            if (ex instanceof SAXParseException)
                infos += " à la ligne " + ((SAXParseException)ex).getLineNumber();
            erreur(infos);
            return(null);
        } catch (final IOException ex) {
            erreur("Erreur d'E/S: " + ex.getMessage());
            return(null);
        } catch (final ParserConfigurationException ex) {
            erreur(ex);
            return(null);
        }
        
        return(ddoc);
    }
    
    /**
     * Affiche un dialogue pour enregistrer le fichier
     */
    private File dialogueEnregistrer() {
		JFileChooser c = new JFileChooser();
        c.setFileFilter(new FileFilter() {
			public boolean accept(File f) {
				if (f.isDirectory())
					return true;
				String s = f.getName();
				int i = s.lastIndexOf('.');

				if (i > 0 && i < s.length() - 1)
					if (s.substring(i + 1).toLowerCase().equals("xml"))
						return true;

				return false;
			}

			public String getDescription() {
				return Strings.get("texte.FichiersXML");
			}
		});
        c.setDialogTitle(Strings.get("titre.EnregistrerFichierXML"));
		c.showSaveDialog(this);
        c.setVisible(true);
        final String sf = c.getName(c.getSelectedFile());
        if (sf != null) {
            fsave = new File(c.getCurrentDirectory().toString(), sf);
            if (fsave.getName().indexOf('.') == -1) {
                fsave = new File(fsave.getPath() + "_config.xml");
                if (fsave.exists()) {
                    if (JOptionPane.showConfirmDialog(this, Strings.get("message.Remplacerfichierexistant"), "", JOptionPane.YES_NO_OPTION) == JOptionPane.NO_OPTION)
                        return(null);
                }
            }
            if (JOptionPane.showConfirmDialog(this, Strings.get("message.EnregistrerCheminSchema"), "", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
                enregistrerCheminSchema(fsave);
            return(fsave);
		
		
        } else
            return(null);
    }
    
    /**
     * Enregistre le fichier XML
     */
    public boolean enregistrerFichier(final File f) {
        File fichierXML;
        if (f == null) {
            fichierXML = dialogueEnregistrer();
            if (fichierXML == null)
                return(false);
        } else
            fichierXML = f;
        boolean nopb = true;
        FileOutputStream fos;
        try {
            fos = new FileOutputStream(fichierXML);
        } catch (final FileNotFoundException ex) {
            erreur(ex);
            return(false);
        }
        Writer fw;
        try {
            fw = new OutputStreamWriter(fos, encodage);
        } catch (final UnsupportedEncodingException ex) {
            erreur(ex);
            try { fos.close(); } catch (final IOException ex2) { }
            return(false);
        }
        try {
            final DOMSource domSource = new DOMSource(doc);
            final StreamResult streamResult = new StreamResult(fw);
            final TransformerFactory tf = TransformerFactory.newInstance();
            //tf.setAttribute("indent-number", new Integer(4));
            final Transformer serializer = tf.newTransformer();
            serializer.setOutputProperty(OutputKeys.ENCODING, encodage);
            serializer.setOutputProperty(OutputKeys.INDENT, "yes");
            serializer.transform(domSource, streamResult);
        } catch (final TransformerConfigurationException ex) {
            erreur(ex);
            nopb = false;
        } catch (final TransformerException ex) {
            erreur(ex);
            nopb = false;
        }
        try {
            fw.close();
            fos.close();
        } catch (final IOException ex) {
            erreur(ex);
            nopb = false;
        }
        return(nopb);
    }
    
    /**
     * Enregistre le chemin du schema XML
     */
    public void enregistrerCheminSchema(final File fichierXML) {
        JFileChooser c = new JFileChooser();        
        c.setFileFilter(new FileFilter() {
			public boolean accept(File f) {
				if (f.isDirectory())
					return true;
				String s = f.getName();
				int i = s.lastIndexOf('.');

				if (i > 0 && i < s.length() - 1)
					if (s.substring(i + 1).toLowerCase().equals("xsd") || s.substring(i + 1).toLowerCase().equals("rng"))
						return true;

				return false;
			}

			public String getDescription() {
				return Strings.get("texte.Schemas");
			}
		});
        c.setDialogTitle(Strings.get("titre.EnregistrerCheminSchema"));
        c.setCurrentDirectory(fichierXML.getParentFile());
		c.showSaveDialog(this);
        c.setVisible(true);  
         
        final File sf = c.getSelectedFile();
        if (sf != null) {
            URI url = null;
            URI base = null;
            try {
                url = new URI(sf.toString());
            }
            catch (URISyntaxException e) {
                System.err.println(e);
            }
            
            try {
                base = new URI(fichierXML.getParentFile().toString());
            }
            catch (URISyntaxException e) {
                System.err.println(e);
            }
            
            URI relative = base.relativize(url);
            
            Element langage = Outils.premierEnfantDeNom(racine, "LANGAGE");
            Element fichier_schema = Outils.premierEnfantDeNom(langage, "FICHIER_SCHEMA");
            fichier_schema.setAttribute("nom", relative.toString());
            enregistrerFichier(fichierXML);
        }
    }
    
    
    /**
     * Indique si le document a été modifié depuis la dernière sauvegarde ou pas.
     */
    public boolean getModif() {
        return(modif);
    }
    
    /**
     * Spécifie si le document a été modifié depuis la dernière sauvegarde ou pas.
     */
    public static void setModif(final boolean modifie) {
        if (modif != modifie) {
            //this.getRootPane().putClientProperty("Window.documentModified", new Boolean(modifie)); // pour MacOS X
            modif = modifie;
        }
    }
    
    
    public static void quitter() {
        System.exit(0);
    }
    
    public void erreur(Exception ex) {
        erreur(ex.getClass().getName() + ": " + ex.getMessage());
    }
    
    public void erreur(String message) {
        JOptionPane.showMessageDialog(this, message, "Erreur", JOptionPane.ERROR_MESSAGE);
    }
    
    
    public static void main(String[] args) {
        new EditeurFichierConfig();
    }
    
    
    public static void convertToString(Document document) {
        for(int i=0;i<30;i++)
            System.out.println();
        DOMSource domSource = new DOMSource(document);
        StringWriter sw = new StringWriter();
        Result result = new StreamResult(sw);
        TransformerFactory transFact = TransformerFactory.newInstance();
        //transFact.setAttribute("indent-number", new Integer(4));
        try {
            Transformer trans = transFact.newTransformer();
            trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
            trans.setOutputProperty(OutputKeys.ENCODING, "ISO-8859-1");
            trans.setOutputProperty(OutputKeys.INDENT, "yes");
            try {trans.transform(domSource, result);}
            catch (TransformerException e) {System.err.println(e);}
        } 
        catch (TransformerConfigurationException e) {System.err.println(e);}
        System.out.println(sw.toString());
    }
    
}
