/*
 * NAT - An universal Translator
 * Copyright (C) 2005 Bruno Mascret
 * Contact: bmascret@free.fr
 * 
 * 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.
 * 
 * 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 Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

package ui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.io.IOException;

import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextPane;
import javax.swing.JButton;
import javax.swing.border.LineBorder;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import nat.ConfigNat;
import nat.Nat;
import nat.convertisseur.Convertisseur2ODT;
import net.sourceforge.jeuclid.swing.JMathComponent;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.FileInputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;

import outils.Embosseur;
import outils.FileToolKit;
import outils.Path;
import outils.TextConverter;
import ui.listener.EntityResolverNull;
import ui.listener.FileChangeListener;
import ui.listener.FileMonitor;

import java.util.ArrayList;

/**
 * Cette classe de l'interface graphique permet d'afficher le fichier transcrit et d'intéragir avec lui
 * (édition, mode perkins, etc.).
 * @author Bruno Mascret
 */
public class EditeurTan extends EditeurBraille implements MouseListener, FileChangeListener
{
	/** identifiant par défaut pour la sérialisation (non utilisé dans NAT)*/ 
	private static final long serialVersionUID = 1L;
	/** nom du fichier braille généré pour obtenir l'apercu */
	public static final String tmpApercu = ConfigNat.getUserTempFolder()+"tmpApercuTan.tan";
	/** nom du fichier xhtml réalisé en détranscrivant {@link #tmpApercu} */
	public static final String tmpXHTML = ConfigNat.getUserTempFolder()+"tmpTan.xhtml";
	
	/** Constante de style: emphase */
	private static final int STYLE_EM = 1;
	/** Constante de style: exposant */
	private static final int STYLE_SUP = 2;
	/** Constante de style: indice */
	private static final int STYLE_SUB = 3;
	
	/** la zone d'affichage d'aperçu en noir du fichier */
	private JTextPane apercu = new JTextPane();
	//private JPanel apercu = new JPanel();
	/** le ScrollPane associé au JTextPane apercu */
	private JScrollPane scrollAp;
	/** JButton aperçu dans un navigateur */
	private JButton btNavigateur = new JButton("<html>Aperçu dans un<br>navigateur <u>w</u>eb</html>", new ImageIcon("ui/icon/web-browser.png"));
	/** JButton aperçu en noir du texte braille saisi*/
	private JButton btApercuNoir = new JButton("Aperçu en noir", new ImageIcon("ui/icon/view-refresh.png"));
	/** JButton enregistrer fich noir */
	private JButton btEnregNoir = new JButton("<html>Enreg<u>i</u>strer le<br>fichier noir</html>",new ImageIcon("ui/icon/document-save-as.png"));
	/** JButton mettre à jour le texte braille*/
	private JButton btMajBraille = new JButton("<html>Mettre à jour<br>le fichier <u>b</u>raille</html>",new ImageIcon("ui/icon/go-up.png"));
	
	/** Liste des JMathComponent utilisés en noir */
	private ArrayList<JMathComponent> lMaths = new ArrayList<JMathComponent>();
	
	/** constante identifiant la base du nom du fichier temporaire d'édition des maths */
	private static String TMP_MATHML = ConfigNat.getUserTempFolder()+"tmp";
	/** adresse du script de lancement de l'éditeur d'openoffice */
	private String script = ConfigNat.getUserTempFolder()+"/scriptOOMath";

	
	/** instance de nat pour lancer les détranscriptions*/
	private Nat nat;
	
	/** ligne de commande à exécuter pour lancer OpenOffice */
	private String ldc = "soffice ";
	
	/** FileMonitor suit les modifications faites sur le ou les fichiers mathml édités par openoffice */
	private FileMonitor fMon = FileMonitor.getInstance();
	
	/**
     * Afficheur graphique de logs
     * @since 2.0
     */
    private AfficheurJTASwing panneauLog;// = new JTextArea (15,40);
	
	/** 
	 * Construit un objet Editeur
	 * @param emb l'objet Embosseur à utiliser pour l'embossage
	 * @param f adresse du fichier édité
	 * @param n instance de nat pour lancer les détranscriptions
	 */
	public EditeurTan(Embosseur emb, String f,Nat n)
	{
	    super("Editeur TAN",emb,n.getGestionnaireErreur());
		nat=n;
		apercu.setBackground(Color.lightGray);
		setSize(600,400);
		
		btNavigateur.addActionListener(this);
		btNavigateur.getAccessibleContext().setAccessibleName("Bouton Aperçu dans un navigateur web");
		btNavigateur.getAccessibleContext().setAccessibleDescription("Valider pour ouvrir la sortie noire dans le navigateur par défaut");
		btNavigateur.setToolTipText("Aperçu dans le navigateur web par défaut du système (Alt+w)");
		btNavigateur.setMnemonic('w');
		btNavigateur.setEnabled(false);
		
		btApercuNoir.addActionListener(this);
		btApercuNoir.getAccessibleContext().setAccessibleName("Bouton aperçu en noir");
		btApercuNoir.getAccessibleContext().setAccessibleDescription("Valider pour raffraichir l'aperçu en noir");
		btApercuNoir.setToolTipText("Raffraichir le rendu en noir du texte saisi (Alt+a)");
		btApercuNoir.setMnemonic('a');
		
		btMajBraille.addActionListener(this);
		btMajBraille.getAccessibleContext().setAccessibleName("Bouton mettre à jour le fichier braille");
		btMajBraille.getAccessibleContext().setAccessibleDescription("Valider pour mettre à jour le fichier braille");
		btMajBraille.setToolTipText("Met à jour le fichier braille (Alt+b)");
		btMajBraille.setMnemonic('b');
		
		scrollAp = new JScrollPane (apercu);
		scrollAp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
		scrollAp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
		
		JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,scrollRes,scrollAp);
		splitPane.setOneTouchExpandable(true);
		//splitPane.setDividerLocation(scrollAp.getHeight());
		
		//Provide minimum sizes for the two components in the split pane
		Dimension minimumSize = new Dimension(100, 50);
		scrollRes.setMinimumSize(minimumSize);
		scrollAp.setMinimumSize(minimumSize);
		
		panneauLog = new AfficheurJTASwing(13, 25);
	    panneauLog.setEditable(false);
	    panneauLog.setLineWrap(true);
	    
	    n.getGestionnaireErreur().addAfficheur(panneauLog);
	    
	    JScrollPane scrollLog = new JScrollPane (panneauLog);
	    scrollLog.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
	    scrollLog.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
	    scrollLog.getAccessibleContext().setAccessibleName("Panneau d'affichage des messages de détranscription");
	    scrollLog.getAccessibleContext().setAccessibleDescription("Ici sont affichées les étapes de la détranscription");
	    scrollLog.setToolTipText("Affichage des messages de la détranscription");
	    
	    btEnregNoir.addActionListener(this);
	    btEnregNoir.getAccessibleContext().setAccessibleName("Bouton enregistrer le fichier noir");
	    btEnregNoir.getAccessibleContext().setAccessibleDescription("Valider pour enregistrer le fichier noir en xhtml");
	    btEnregNoir.setToolTipText("Enregistrer le fichier noir en xhtml (Alt+i)");
	    btEnregNoir.setMnemonic('i');

		
		/*
		 * Mise en page
		 */
		lesBoutons.setLayout(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();

		
		c.gridx=0;
		c.gridy=0;
		c.gridwidth = 3;
		lesBoutons.add(message,c);
		c.gridwidth=1;
		c.gridy++;
		lesBoutons.add(btEnregistrer,c);
		c.gridx++;
		lesBoutons.add(btEnregistrersous,c);
		c.gridx++;
		lesBoutons.add(btFermer,c);
		
		panneauAffichage.setLayout(new BorderLayout());
		panneauAffichage.add("Center",splitPane);
		//panneauAffichage.add("South",scrollAp);
		
		JPanel pLateral = new JPanel();
		LineBorder l = new LineBorder(pLateral.getBackground(),12);
		pLateral.setBorder(l);
		
		pLateral.setLayout(new GridBagLayout());
		c.anchor = GridBagConstraints.CENTER;
		c.gridx = 0;
		c.gridy = 0;
		c.insets=new Insets(3,3,3,3);
		pLateral.add(btUndo,c);
		
		c.gridx++;
		pLateral.add(btRedo,c);
		
		c.gridx=0;
		c.ipady=50;
		c.gridy++;
		c.gridwidth = 2;
		pLateral.add(jcbPerkins,c);
		
		c.gridy++;
		c.ipady=10;
		c.gridwidth = 1;
		pLateral.add(btEmbosser,c);
		
		c.gridx++;
		pLateral.add(btMajBraille,c);
		pLateral.add(btMajBraille,c);
		
		c.gridy++;
		c.gridx=0;
		c.gridheight = 2;
		c.insets=new Insets(50,3,3,3);
		pLateral.add(btEnregNoir,c);
		
		c.gridx++;
		c.gridheight = 1;
		c.insets=new Insets(50,3,3,3);
		pLateral.add(btApercuNoir,c);
		
		c.gridy++;
		c.insets=new Insets(3,3,3,3);
		pLateral.add(btNavigateur,c);
		
		c.gridy++;
		c.gridwidth = 2;
		c.gridx=0;
		pLateral.add(scrollLog,c);
		
		/*
		 * Mise en page générale
		 */
		JPanel panneau = new JPanel();
		panneau.setLayout(new BorderLayout());
		panneau.add("North",lFichier);
		panneau.add("Center",panneauAffichage);
		panneau.add("South",lesBoutons);
		panneau.add("East",pLateral);
		JPanel p = new JPanel();
		p.setPreferredSize(new Dimension(10,10));
		panneau.add("West",p);
		setContentPane(panneau);
		if(ConfigNat.getCurrentConfig().getMemoriserFenetre())
		{
			int x= ConfigNat.getCurrentConfig().getWidthEditeur();
			int y=ConfigNat.getCurrentConfig().getHeightEditeur();
			if(x+y != 0){setPreferredSize(new Dimension(x,y));}
		}
		if(ConfigNat.getCurrentConfig().getCentrerFenetre())
		{
			Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
			Dimension size = this.getPreferredSize();
			screenSize.height = screenSize.height/2;
			screenSize.width = screenSize.width/2;
			size.height = size.height/2;
			size.width = size.width/2;
			int y = screenSize.height - size.height;
			int x = screenSize.width - size.width;
			setLocation(x, y);
		}
		encodage=ConfigNat.getCurrentConfig().getBrailleEncoding();
        afficheFichier(f, ConfigNat.getCurrentConfig().getPoliceEditeur(), ConfigNat.getCurrentConfig().getTaillePolice());
        if(resultat.getText().length()> 0)
        {
        	afficheNoir();
        }
	}
	/**
	 * Demande aussi s'il faut effacer le fichier temporaire mémoriser 
	 * @see java.awt.Window#setVisible(boolean)
	 */
	@Override
    public void setVisible(boolean v)
	{
		super.setVisible(v);
		if(v && resultat.getText().length()>0 && fichier.equals(ConfigNat.fichTmpTan) &&
	        	JOptionPane.showConfirmDialog(this,"Effacer le fichier précédent?\n Vivement conseillé si vous avez changé d'encodage ou de table braille.","Vider l'éditeur", JOptionPane.YES_NO_OPTION)==JOptionPane.OK_OPTION)
		{
	        	resultat.setText("");
	        	enregistrerFichier();
	    }
	}
	/*
	 *	Enregistre le fichier
	 * TODO: On doit plus en avoir besoin de lui 
	 *
	@Override
	protected void enregistrerFichier()
	{
		if (FileToolKit.saveStrToFile(getText(), fichier,ConfigNat.getCurrentConfig().getSourceEncoding()))
		{
			message.setText("Fichier enregistré");
			modif = false;
		}	
		else{message.setText("<html><p color=\"red\">Erreur lors de l'enregistrement</p></html>");}
	}*/
	
	/**
	 * Enregistre aussi le fichier noir (en XHTML)
	 * @param fichierNoir adresse du fichier noir
	 * @param changeNom vrai si il faut enregistrer le fichier sous un autre nom
	 */
	protected void enregistrerFichierNoir(String fichierNoir, boolean changeNom)
	{
		String texte="";/*
		ElementIterator iterator = new ElementIterator(htmlDoc);
	    Element element;
	    while ((element = iterator.next()) != null) {
	      AttributeSet attributes = element.getAttributes();
	      Object name = attributes.getAttribute(StyleConstants.NameAttribute);
	      if ((name instanceof HTML.Tag)
	          && ((name == HTML.Tag.H1) || (name == HTML.Tag.H2) || (name == HTML.Tag.H3))) {
	        // Build up content text as it may be within multiple elements
	        StringBuffer text = new StringBuffer();
	        int count = element.getElementCount();
	        for (int i = 0; i < count; i++) {
	          Element child = element.getElement(i);
	          AttributeSet childAttributes = child.getAttributes();
	          if (childAttributes
	              .getAttribute(StyleConstants.NameAttribute) == ) {
	            int startOffset = child.getStartOffset();
	            int endOffset = child.getEndOffset();
	            int length = endOffset - startOffset;
	            text.append(htmlDoc.getText(startOffset, length));
	          }
	        }
	        System.out.println(name + ": " + text.toString());
	      }
	    }*/
		Element [] elem = apercu.getDocument().getRootElements();
		for(Element e : elem)
		{
			int numeq = 0;
			try
            {
				for(int i = 0;i<e.getElementCount();i++)
				{
					Element f = e.getElement(i);
					//System.out.println("****"+f.getName()+":"+f.getDocument().getText(0, f.getDocument().getLength()));
					for(int j = 0;j<f.getElementCount();j++)
					{
						Element g = f.getElement(j);
						if(g.getName()=="component")
						{
							System.out.println("**** MATHS:");
							
							DocumentBuilderFactory fabrique = DocumentBuilderFactory.newInstance();
					    	fabrique.setValidating(false);
							DocumentBuilder constructeur;

				            constructeur = fabrique.newDocumentBuilder();
				            org.w3c.dom.Document doc = constructeur.newDocument();
						    // Propriétés de docParam
						    doc.setXmlVersion("1.0");
						    doc.setXmlStandalone(true);
						    
						    //racine
						    Node racine = doc.importNode(lMaths.get(numeq).getDocument(),true);
						    Node attr = doc.createAttribute("xmlns:m");
						    attr.setNodeValue("http://www.w3.org/1998/Math/MathML");
						    Node attr2 = doc.createAttribute("xmlns:maths");
						    attr.setNodeValue("http://www.w3.org/1998/Math/MathML");
						    racine.getAttributes().setNamedItem(attr);
						    racine.getAttributes().setNamedItem(attr2);
						    
						    doc.appendChild(racine);		    
						    /* Sauvegarde de document dans un fichier */
					        Source source = new DOMSource(doc);
					        
					        // Création de la sortie
					        StringWriter sr = new StringWriter();
					        Result out = new StreamResult(sr);
					        
					        // Configuration du transformer
					        TransformerFactory tfabrique = TransformerFactory.newInstance();
					        Transformer transformer = tfabrique.newTransformer();
					        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
					        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");  
					        transformer.setOutputProperty(OutputKeys.VERSION, "1.1");
					        //transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, ConfigNat.getCurrentConfig().getDTD());
					        // Transformation
					        transformer.transform(source, out);
					        texte+=sr.getBuffer();
					        numeq++;
						}
						else if(g.getName().equals("content"))
						{
							System.out.println("NOM:"+g.getName());
							int startOffset = g.getStartOffset();
				            int endOffset = g.getEndOffset();
				            int length = endOffset - startOffset;
				            AttributeSet childAttributes = g.getAttributes();
				            /*java.util.Enumeration enu = childAttributes.getAttributeNames();
				            while(enu.hasMoreElements())
				            {
				            	System.out.println(enu.nextElement());
				            }*/
				            String noeud="";
				            boolean [] ferme = {false,false,false};
				            if(StyleConstants.isBold(childAttributes))
				            {
				            	ferme[0]=true;
				            	noeud+="<em>";
				            }
				            if(StyleConstants.isSubscript(childAttributes))
				            {
				            	ferme[1]=true;
				            	noeud+="<sub>";
				            }
				            if(StyleConstants.isSuperscript(childAttributes))
				            {
				            	ferme[2]=true;
				            	noeud+="<sup>";
				            }		
				            String contenu = apercu.getDocument().getText(startOffset,length);
				            noeud+= contenu;
				            if(ferme[0]){noeud+="</em>";}
				            if(ferme[1]){noeud+="</sub>";}
				            if(ferme[2]){noeud+="</sup>";}
				            System.out.println("****"+noeud);
				            if(contenu.endsWith("\n"))
				            {
				            	noeud+="</p>\n\t<p>";
				            }
				            texte+=noeud;
						}
						else
						{
							System.out.println("NOM:"+g.getName());
						}
					}
				}
				
	            //System.out.println(e.getName()+":"+e.getDocument().getText(0, e.getDocument().getLength()));
				
            }
            catch (BadLocationException e1)
            {
	            // TODO Auto-generated catch block
	            e1.printStackTrace();
            }
            catch (ParserConfigurationException pce)
            {
	            // TODO Auto-generated catch block
	            pce.printStackTrace();
            }
            catch (TransformerConfigurationException tce)
            {
	            // TODO Auto-generated catch block
	            tce.printStackTrace();
            }
            catch (TransformerException te)
            {
	            // TODO Auto-generated catch block
	            te.printStackTrace();
            }
		}
		JFileChooser selectionneFichier = new JFileChooser();
		FileFilter xhtmlFilter = new FileNameExtensionFilter("XHTML 1.1 (*.xhtml)", "xhtml");
		selectionneFichier.addChoosableFileFilter(xhtmlFilter);
		selectionneFichier.setApproveButtonText("Choisir ce fichier");
		//TODO déplacer ce if au tout début
		texte = texte.replaceAll("<[?]xml.*[?]>", "");
		texte = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
			"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN\" "+
			"\"http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd\">\n"+
			"<html xmlns:m=\"http://www.w3.org/1998/Math/MathML\""+
			" xmlns=\"http://www.w3.org/1999/xhtml\" " +
			" xmlns:math=\"http://www.w3.org/1998/Math/MathML\">"+
			"<head></head>" +
			"<body>"+
			"\t<p>"+ texte + "\n</p></body></html>";
		if (!changeNom)
		{
			FileToolKit.saveStrToFile(texte, fichierNoir, "UTF-8");
		}
		else if(selectionneFichier.showOpenDialog(this) == JFileChooser.APPROVE_OPTION)
        {    
 			//si un fichier est selectionné, récupérer le fichier puis son path
			fichierNoir = selectionneFichier.getSelectedFile().getAbsolutePath();
			
			FileToolKit.saveStrToFile(texte, fichierNoir, "UTF-8");		
        }
		System.out.println("Texte:\n" + texte);
		//else{message.setText("<html><p color=\"red\">Erreur lors de l'enregistrement du fichier noir</p></html>");}
	}
	
	/**
	 * Affiche le fichier dans le JTextPane resultat et configure la ligne secondaire
	 * @param nomFichier nom du fichier transcrit
	 * @param police police principale
	 * @param taillePolice taille de la police principale
	 * @param police2 police secondaire
	 * @param taillePolice2 taille de la police secondaire
	 */
	public void afficheFichier(String nomFichier,String police, int taillePolice, String police2, int taillePolice2)
	{
		afficheFichier(nomFichier, police, taillePolice);
	}
	
	/**
	 * Affiche le fichier dans le JTextPane
	 * @param nomFichier nom du fichier transcrit
	 * @param police police principale
	 * @param taillePolice taille de la police principale
	 */
	public void afficheFichier(String nomFichier,String police, int taillePolice)
    {
		setTitle("NAT " + nomFichier);
	    fichier = nomFichier;
	    lFichier.setText("Document:" + fichier);
		//Préparation des styles pour le JTextPane
	    StyledDocument doc = resultat.getStyledDocument();
	    //pour éviter que l'affichage prenne 3 plombes à chaque fois...
	    Document blank = new DefaultStyledDocument();
	    resultat.setDocument(blank);
	    
	    MutableAttributeSet attrs = resultat.getInputAttributes();
	    Font fonteBraille = new Font(police, Font.PLAIN, taillePolice);

	    StyleConstants.setFontFamily(attrs, fonteBraille.getFamily());
	    StyleConstants.setFontSize(attrs,taillePolice);
	    
		resultat.setPreferredSize(new Dimension(400,280));
		apercu.setPreferredSize(new Dimension(400,100));
		this.pack();
		
	    try
	    {
	    	BufferedReader raf = new BufferedReader(new InputStreamReader(new FileInputStream(nomFichier),encodage));
	    	String ligne;
	    	while ( (ligne = raf.readLine()) != null )
	    	{
	    		doc.insertString(doc.getLength(), ligne + "\n", attrs);
	    	}
	     }
	    catch (BadLocationException ble){System.err.println("Impossible d'afficher le texte");ble.printStackTrace();}
	    catch (IOException e){System.err.println("erreur dans: " + e + " ");e.printStackTrace();}
			
		initialiseMap();
		ajouteListenerDoc(doc);
		jcbPerkins.doClick();
    }	  
	
	/**
	 * Détranscrit le texte contenu dans {@link EditeurBraille#resultat}
	 * @return true si détranscription sans erreur
	 */
	private boolean detranscrit()
	{
		boolean retour = false;
		//apercu.repaint();
		ArrayList<String>noir = new ArrayList<String>();
		ArrayList<String>braille = new ArrayList<String>();
		//création d'un fichier temporaire d'aperçu
		FileToolKit.saveStrToFile(resultat.getText(), tmpApercu,ConfigNat.getCurrentConfig().getBrailleEncoding());
		noir.add(tmpXHTML);
		braille.add(tmpApercu);
		if (nat.fabriqueTranscriptions(noir, braille, true))
	    {			
		//lancement de la détranscription
		   btNavigateur.setEnabled(nat.lanceScenario());
		   retour = true;
	    }
		return retour;
	}
	
	/**
	 * Transcrit le fichier d'aperçu {@link #tmpXHTML}
	 * @return true si transcription sans erreur
	 */
	private boolean transcrit()
	{
		boolean retour = false;
		
		//apercu.repaint();
		ArrayList<String>noir = new ArrayList<String>();
		ArrayList<String>braille = new ArrayList<String>();
		//création d'un fichier temporaire d'aperçu
		enregistrerFichierNoir(tmpXHTML,false);
		noir.add(tmpXHTML);
		braille.add(tmpApercu);
		String noirEncoding = ConfigNat.getCurrentConfig().getNoirEncoding();
		ConfigNat.getCurrentConfig().setNoirEncoding("UTF-8");
		if (nat.fabriqueTranscriptions(noir, braille, false)&& nat.lanceScenario())
	    {	
			try
            {
	            resultat.getDocument().remove(0, resultat.getDocument().getLength());
            }
            catch (BadLocationException e)
            {
	            // TODO Auto-generated catch block
	            e.printStackTrace();
            }
			afficheFichier(tmpApercu, ConfigNat.getCurrentConfig().getPoliceEditeur(), ConfigNat.getCurrentConfig().getTaillePolice());
			retour = true;
	    }
		ConfigNat.getCurrentConfig().setNoirEncoding(noirEncoding);
		return retour;
	}
	/**
	 * Affiche en noir dans {@link #apercu} la détranscription du texte de {@link #resultat}
	 */
	private void afficheNoir()
	{		
		try
        {
	        apercu.getDocument().remove(0, apercu.getDocument().getLength());
        }
        catch (BadLocationException ble)
        {
	        // TODO Auto-generated catch block
	        ble.printStackTrace();
        }
		lMaths.clear();
		//création du DOM
		org.w3c.dom.Document doc;
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setValidating(ConfigNat.getCurrentConfig().getNiveauLog()==Nat.LOG_DEBUG);
        dbf.setNamespaceAware(false);
    
        DocumentBuilder db;
		try
		{
			dbf.setXIncludeAware(false);
			db = dbf.newDocumentBuilder();
			doc = db.parse(new File(tmpXHTML));
			
			NodeList phrases = doc.getElementsByTagName("p");
			MutableAttributeSet attrs = apercu.getInputAttributes();
			StyleConstants.setFontSize(attrs, ConfigNat.getCurrentConfig().getTaillePolice2());
			//MathDocHandler handler = new MathDocHandler();
			//ArrayList<MathPanel> lMathPan = new ArrayList<MathPanel>();
			for(int i=0;i<phrases.getLength();i++)
			{	
				//apercu.setLayout(null);
				NodeList fils = phrases.item(i).getChildNodes();
				for(int j=0;j<fils.getLength();j++)
				{
					Node f = fils.item(j);
					if(f.getNodeName().equals("m:math")|| f.getNodeName().equals("math:math"))
					{
						//System.out.println(f.getNodeName()+" "+f.getNodeValue());

						JMathComponent jmc = new JMathComponent();
						jmc.addMouseListener(this);
						jmc.addKeyListener(this);
						lMaths.add(jmc);
						
						jmc.setDocument(f);
						jmc.setFontSize(ConfigNat.getCurrentConfig().getTaillePolice2());
						
						jmc.setAlignmentX(JComponent.LEFT_ALIGNMENT);
						jmc.setAlignmentY(JComponent.BOTTOM_ALIGNMENT);
						apercu.insertComponent(jmc);
						StyleConstants.setFontSize(attrs, ConfigNat.getCurrentConfig().getTaillePolice2());
						apercu.replaceSelection(" ");
					}
					else
					{
						int style = getStyle(phrases.item(i));
						drawLit(f, style);
					}
				}
				apercu.replaceSelection("\n");//fin de la phrase
			}
		}
		catch (ParserConfigurationException e) {e.printStackTrace();}//TODO
		catch (SAXException e) {e.printStackTrace();}//TODO
		catch (IOException e) {e.printStackTrace();}//TODO
		apercu.repaint();
		btNavigateur.setEnabled(true);
	}
	
	/**
	 * Renvoie le code du style pour le noeud item
     * @param item le noeud à tester
     * @return code du style
     */
    private int getStyle(Node item)
    {
	    int retour = 0;
	    if(item != null)
	    {
		    String nom = item.getNodeName();
		    if(nom != null)
		    {
			    if(nom.equals("em")){retour=STYLE_EM;}
			    else if(nom.equals("sup")){retour = STYLE_SUP;}
			    else if(nom.equals("sub")){retour = STYLE_SUB;}
		    }
	    }
	    return retour;
    }

	/**
	 * Ecrit récursivement les contenus littéraires en fonction de leur attributs de mise en forme 
	 * @param f le noeud courant
	 * @param pere le type de pere du noeud courant
	 */
	private void drawLit(Node f, int pere)
	{
		MutableAttributeSet attrs = apercu.getInputAttributes();
		if(f.getNodeType()==Node.TEXT_NODE)
		{
			//System.out.println(f.getNodeName()+" TEXT "+f.getNodeValue());
			apercu.replaceSelection(f.getNodeValue().replaceAll("\n", "").trim()+" ");
			//apercu.replaceSelection("***"+f.getNodeValue().replaceAll("\n|\t|  ", "")+"***");
			switch(pere)
			{
				case STYLE_EM:
				{
					//System.out.println("suppr EM");
					StyleConstants.setBold(attrs, false);
					break;
				}
				case STYLE_SUP:
				{
					//System.out.println("suppr SUP");
					StyleConstants.setSuperscript(attrs, false);
					break;
				}
				case STYLE_SUB:
				{
					//System.out.println("suppr SUB");
					StyleConstants.setSubscript(attrs, false);
					break;
				}
			}
		}
		else
		{
			switch(getStyle(f))
			{
				case STYLE_EM:
				{
					StyleConstants.setBold(attrs, true);
					//System.out.println(f.getNodeName()+" +EM "+f.getNodeValue());
					break;
				}
				case STYLE_SUP:
				{
					StyleConstants.setSuperscript(attrs, true);
					//System.out.println(f.getNodeName()+" +SUP "+f.getNodeValue());
					break;
				}
				case STYLE_SUB:
				{
					StyleConstants.setSubscript(attrs, true);
					//System.out.println(f.getNodeName()+" +SUB "+f.getNodeValue());
					break;
				}
			}
			NodeList fils = f.getChildNodes();
			for(int i=0;i<fils.getLength();i++)
			{
				drawLit(fils.item(i), getStyle(f));
			}
		}
	}
	
	/**
	 * Renvoie le texte contenu dans {@link #resultat}
	 * @return Le texte contenu dans {@link #resultat}
	 */
	@Override
	public String getText(){return resultat.getText();}
	
	/** 
	 * Implémente la méthode actionPerformed d'ActionListener
	 * Gère les actions des boutons et met à jour l'InputMap du JTextPane resultat en fonction de
	 * l'état du JCheckBox jcbPerkins
	 * @param evt l'objet ActionEvent
	 */
	@Override
	public void actionPerformed(ActionEvent evt)
	{
		super.actionPerformed(evt);
		if (evt.getSource()==btApercuNoir && detranscrit()){afficheNoir();}
		else if (evt.getSource()==btNavigateur)
		{
			if(Desktop.getDesktop().isSupported(Desktop.Action.BROWSE))
			{
				
				try 
				{
					File f = new File(ConfigNat.getUserTempFolder()+"/tmpTan.xhtml");
					Desktop.getDesktop().browse(f.toURI());
				}
				catch (IOException e){e.printStackTrace();}
			}
			else
			{
				JOptionPane.showMessageDialog(this,"<html><b>Attention!</b><br>" +
						"Les modifications n'ont pas été enregistrées!<br>" +
						"Continuer le test?","Modifications non chargées",JOptionPane.INFORMATION_MESSAGE);
			}
		}
		else if (evt.getSource()==btEnregNoir)
		{
			enregistrerFichierNoir("", true);
		}
		else if (evt.getSource()==btMajBraille)
		{
			transcrit();
		}
	}
	/**
     * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
     */
    @Override
    public void mouseClicked(MouseEvent me)
    {
    	if(me.getClickCount()==2)
	    {
	    	editFormule((JMathComponent)(me.getSource()));
	    }
	    
    }

	/**
	 * Edite dans openoffice le composant mathématique passé en paramètre
     * @param jmc le JMathComponent à éditer
     */
    private void editFormule(JMathComponent jmc)
    {
    	//mathEdit = lMaths.indexOf(jmc);
    	//System.out.println("Clic sur " + lMaths.indexOf(jmc));
    	Node n = jmc.getDocument();
    	DocumentBuilderFactory fabrique = DocumentBuilderFactory.newInstance();
    	fabrique.setValidating(false);
		DocumentBuilder constructeur;
        try
        {
            constructeur = fabrique.newDocumentBuilder();
            org.w3c.dom.Document doc = constructeur.newDocument();
		    // Propriétés de docParam
		    doc.setXmlVersion("1.0");
		    doc.setXmlStandalone(true);
		    
		    //racine
		    Node racine = doc.importNode(n,true);
		    Node attr = doc.createAttribute("xmlns:m");
		    attr.setNodeValue("http://www.w3.org/1998/Math/MathML");
		    Node attr2 = doc.createAttribute("xmlns:maths");
		    attr.setNodeValue("http://www.w3.org/1998/Math/MathML");
		    racine.getAttributes().setNamedItem(attr);
		    racine.getAttributes().setNamedItem(attr2);
		    
		    doc.appendChild(racine);		    
		    /* Sauvegarde de document dans un fichier */
	        Source source = new DOMSource(doc);
	        
	        // Création du fichier de sortie
	        File f = new File(TMP_MATHML+"."+lMaths.indexOf(jmc)+".mml");
	        f.delete();
	        Result out = new StreamResult(f);
	        
	        // Configuration du transformer
	        TransformerFactory tfabrique = TransformerFactory.newInstance();
	        Transformer transformer = tfabrique.newTransformer();
	        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
	        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");  
	        transformer.setOutputProperty(OutputKeys.VERSION, "1.1");
	        //transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, ConfigNat.getCurrentConfig().getDTD());
	        // Transformation
	        transformer.transform(source, out);
	        out = null;
	        f.setWritable(true, false);
	        //f.setLastModified(System.currentTimeMillis()-2000);
	        //transformer = null; out = null;
	        
	        fMon.addFileChangeListener(this, f.getAbsolutePath(), 20);
	        
	        //ouverture de l'éditeur openoffice
	        //Path p = new Path(gestErreur);
	        
	        gestErreur.afficheMessage("\nDétection de l'éditeur openoffice", Nat.LOG_NORMAL);		        
	        boolean OO_Ok;
			if(System.getProperty("os.name").startsWith("Linux"))
			{
				gestErreur.afficheMessage("\n**Ecriture du script", Nat.LOG_VERBEUX);
	            OO_Ok = fabriqueExec(Convertisseur2ODT.OS_LINUX,f.getAbsolutePath());
	            if (OO_Ok)
	            {
	            	gestErreur.afficheMessage("\n**Lancement de l'éditeur OpenOffice", Nat.LOG_VERBEUX);
					Runtime runTime = Runtime.getRuntime();
					int res = 0;
					
		            try
					{
						//Process p = runTime.exec(script);
		            	runTime.exec(script);
						//res = p.waitFor();
					}
					catch (IOException e) {gestErreur.afficheMessage("\nErreur d'entrée/sortie1", Nat.LOG_NORMAL); OO_Ok = false;}
					//catch (InterruptedException e) {gestErreur.afficheMessage("\nErreur de communication avec openoffice", Nat.LOG_NORMAL); OO_Ok = false;}
					if (res != 0)
					{//le processus p ne s'est pas terminé normalement
						gestErreur.afficheMessage("\nLe script de l'éditeur d'OOo a renvoyé une erreur", Nat.LOG_NORMAL);
						OO_Ok = false;
					}
	            }
			}
			else if(System.getProperty("os.name").startsWith("Windows"))
			{
				Runtime runTime = Runtime.getRuntime();
				int res = 0;
				gestErreur.afficheMessage("\n**Ecriture du script de lancement de l'éditeur d'OOo", Nat.LOG_VERBEUX);
				OO_Ok = fabriqueExec(Convertisseur2ODT.OS_WINDOWS,f.getAbsolutePath());
				if (OO_Ok)
				{
					gestErreur.afficheMessage("\n**Lancement de l'éditeur d'OOo", Nat.LOG_VERBEUX);
					TextConverter tc = new TextConverter (script);
					try
					{
						tc.convert(); //conversion des lf en crlf pour impression
					}
					catch (Exception e) {gestErreur.afficheMessage("\nErreur de TextConverter", Nat.LOG_NORMAL);}
					try
					{
						// la ligne de commande est lancée directement pour éviter les problèmes d'encoding dans un fichier .bat
						runTime.exec(ldc);
					}
					catch (IOException e) {gestErreur.afficheMessage("\nErreur d'entrée/sortie2 "+e.getLocalizedMessage()+" "+ldc, Nat.LOG_NORMAL);}
					if (res != 0)
					{//le processus p ne s'est pas terminé normalement
						gestErreur.afficheMessage("\nLe processus de lancement de l'éditeur d'OOo a renvoyé une erreur", Nat.LOG_NORMAL);
						OO_Ok = false;
					}
				}
			}
			else{gestErreur.afficheMessage("\nSystème d'exploitation inconnu", Nat.LOG_NORMAL); OO_Ok = false;}
        }
        catch (ParserConfigurationException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        catch (TransformerConfigurationException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        catch (TransformerException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        catch (FileNotFoundException e)
        {
            // TODO Auto-generated catch block
        	//fichier temp non trouvé
            e.printStackTrace();
        }
	    
    }
	/**
	 * Fabrique le script de lancement de l'éditeur mathématique d'OOo en fonction de l'OS
	 * @param os indique le système d'exploitation
	 * @param fichMath nom du fichier temporaire mathml
	 * @return true si OO a été détecté et que le script a bien été créé
	 */
	public boolean fabriqueExec(int os, String fichMath)
	{
		Boolean exec_ok = true;
		
        String pathOO = "";
        
		if(os == Convertisseur2ODT.OS_LINUX)
		{
	        BufferedReader bufferedReader = null;
	        InputStream out = null;
	
	        try {
	            /**recherche d'une instance d'openOffice déjà lancé**/
	            gestErreur.afficheMessage("\n***Recherche " +
	                    "d'OpenOffice.org : ", Nat.LOG_VERBEUX);
	            out = Runtime.getRuntime().exec("which soffice").getInputStream();
	            bufferedReader = new BufferedReader(new InputStreamReader(out));
	            pathOO = bufferedReader.readLine();
	            if (pathOO==null)
	            {
	                gestErreur.afficheMessage("\n\n******************************\n" +
	                        "OpenOffice.org doit être installé pour que" +
	                        " NAT accepte les formats propriétaires. Ce " +
	                        "logiciel est disponible gratuitement à l'adresse" +
	                        " http://fr.openoffice.org\n" +
	                        "******************************\n\n", Nat.LOG_SILENCIEUX);
	                exec_ok = false;
	                	
	            }
	            else
	            {
	                String cmd =ldc.replaceFirst("soffice",pathOO + " -nolockcheck");
	                cmd+= " "+fichMath;
	                gestErreur.afficheMessage("\n***Système d'exploitation: Linux", Nat.LOG_VERBEUX);
	    			gestErreur.afficheMessage("\n***Création du script", Nat.LOG_VERBEUX);
	    			try
	    			{
	    				BufferedWriter fich = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(script),"UTF-8"));
	    				//FileWriter fichierXSL = new FileWriter(filtre);
	    				fich.write("#!/bin/sh\n#Generated file/fichier genere par NAT\n");
	    				fich.write("exec=exec\n");
	    				fich.write("exec " + cmd);
	                    Runtime.getRuntime().exec("chmod 755 " + script);
	    				fich.close();
	    			}
	    			catch (IOException e)
	    			{
	    				gestErreur.afficheMessage("\nErreur lors de la création du script de lancement d'OOo" + e,Nat.LOG_NORMAL);
	    				exec_ok = false;
	    			}
	            }
	        } 
	        catch (Exception officeException) 
	        {
	            officeException.printStackTrace();
	            gestErreur.afficheMessage("\nErreur lors de la recherche de "+
	                    "OpenOffice.org sur la machine", Nat.LOG_NORMAL);
	            exec_ok = false;
	        }	
		}
		else if(os == Convertisseur2ODT.OS_WINDOWS)
		{
	
	        if (!script.endsWith(".bat")){script+=".bat";} //ajout de l'extension afin que le script tourne sous Windows
			gestErreur.afficheMessage("\n***Système d'exploitation: Windows", Nat.LOG_VERBEUX);
			gestErreur.afficheMessage("\n***Création du script", Nat.LOG_VERBEUX);
	        Path path = new Path(gestErreur);
	        pathOO = path.getOOPath();
	        if (pathOO.isEmpty())
	        {
	        	gestErreur.afficheMessage("\n\n******************************\n" +
	                    "OpenOffice.org doit être installé "+
	                    "pour que NAT accepte les formats propriétaires. Ce "+
	                    "logiciel est disponible gratuitement à l'adresse "+
	                    "http://fr.openoffice.org\n"+
	                    "******************************\n\n",Nat.LOG_SILENCIEUX);
	            exec_ok = false;
	        }
	        //gest.afficheMessage(pathOO,Nat.LOG_VERBEUX);
			if (exec_ok) try
			{
				ldc = "soffice";
				BufferedWriter fich = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(script), "windows-1252"));
	            String cmd = ldc.replaceFirst("soffice","\""+pathOO.trim()+"\"");
	            gestErreur.afficheMessage("path oo:"+pathOO+":",Nat.LOG_DEBUG);
	            cmd += " \"" + fichMath + "\"";
				fich.write(cmd);
				// le script ne sert pas (problème encoding sous windows)
				ldc=cmd;
				fich.close();
			}
			catch (IOException e)
			{
				gestErreur.afficheMessage("\nErreur lors de la création du script de lancement d'OOo" + e,Nat.LOG_NORMAL);
				exec_ok = false;
			}
		}
		else
		{
			gestErreur.afficheMessage("\n** Système d'exploitation non reconnu; arrêt du traitement \n",Nat.LOG_SILENCIEUX);
			exec_ok = false;
		}
		
		return exec_ok;
	}
	/**
     * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
     */
    @Override
    public void mouseEntered(MouseEvent arg0)
    {
	    // TODO Auto-generated method stub
	    
    }
	/**
     * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
     */
    @Override
    public void mouseExited(MouseEvent arg0)
    {
	    // TODO Auto-generated method stub
	    
    }
	/**
     * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
     */
    @Override
    public void mousePressed(MouseEvent arg0)
    {
	    // TODO Auto-generated method stub
	    
    }
	/**
     * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
     */
    @Override
    public void mouseReleased(MouseEvent arg0)
    {
	    // TODO Auto-generated method stub
	    
    }
	/**
	 * Le fichier a été modifié
     * @see ui.listener.FileChangeListener#fileChanged(java.lang.String)
     */
    @Override
    public void fileChanged(String fileName)
    {
    	//création du DOM
		org.w3c.dom.Document doc;
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setValidating(ConfigNat.getCurrentConfig().getNiveauLog()==Nat.LOG_DEBUG);
    
        DocumentBuilder db;
		try
		{
			db = dbf.newDocumentBuilder();
			db.setEntityResolver(new EntityResolverNull());
			File f=new File(fileName);
			doc = db.parse(f);
			System.out.println("Nom du fichier temporaire:" + fileName);
			String [] decoupNom = fileName.split("\\.");
			int indice = Integer.parseInt(decoupNom[decoupNom.length-2]);
			Node math = doc.getElementsByTagName("math:math").item(0);
			
			lMaths.get(indice).setDocument(math);
			apercu.repaint();
			//suppression de l'écouteur
			//fMon.removeFileChangeListener(this, fileName);
			//f.setWritable(true);
		}
        catch (ParserConfigurationException e)
        {
	        // TODO Auto-generated catch block
	        e.printStackTrace();
        }
        catch (SAXException e)
        {
	        // TODO Auto-generated catch block
	        e.printStackTrace();
        }
        catch (IOException e)
        {
	        // TODO Auto-generated catch block
	        e.printStackTrace();
        }
	    
    }
    
    /**
     * Prend en charge l'utilisation de ENTER sur un JMathComponent en plus des actions héritées de 
     * {@link ui.EditeurBraille#keyPressed(KeyEvent)}
     * @see ui.EditeurBraille#keyPressed(java.awt.event.KeyEvent)
     */
    @Override
    public void keyPressed(KeyEvent ke)
    {
    	if(ke.getKeyCode()==KeyEvent.VK_ENTER && ke.getSource() instanceof JMathComponent)
    	{
    		this.editFormule((JMathComponent)(ke.getSource()));
    	}
    	else
    	{
    		super.keyPressed(ke);
    	}
    }
    
    /**
     * Supprime aussi {@link #panneauLog} des écouteurs de logs
     * @see java.awt.Window#dispose()
     */
    @Override
    public void dispose()
    {
    	nat.getGestionnaireErreur().removeAfficheur(panneauLog);
    	super.dispose();
    }
}

