/*
 * 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 gestionnaires.GestionnaireErreur;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Toolkit;
import java.io.IOException;

import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.JButton;
import javax.swing.SpinnerNumberModel;
import javax.swing.border.LineBorder;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
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 nat.ConfigNat;

import java.awt.event.ActionEvent;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.FileInputStream;
import outils.Embosseur;
import outils.FileToolKit;
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 Editeur extends EditeurBraille implements ChangeListener, CaretListener
{
	/** Pour la sérialisation (non utilisé)*/
	private static final long serialVersionUID = 1L;
	/** les différentes pages du documents */
	private ArrayList<StyledDocument> pages = new ArrayList<StyledDocument>(); 
	/** la page actuellement affichée */
	private int pageActu = 0;
	/** la zone secondaire d'affichage (affiche la ligne en cours dans la police secondaire)*/
	private JTextField ligneRes = new JTextField();
	/** JButton pour afficher la première page */
    private JButton bt1erePage = new JButton(new ImageIcon("ui/icon/go-first.png"));
    /** JButton pour afficher la page suivante */
    private JButton btPageSuiv = new JButton(new ImageIcon("ui/icon/go-next.png"));
    /** JButton pour afficher la page précédente */
    private JButton btPagePrec = new JButton(new ImageIcon("ui/icon/go-previous.png"));
    /** JButton pour afficher la dernière page */
    private JButton btLastPage = new JButton(new ImageIcon("ui/icon/go-last.png"));
    /** JSpinner indiquant le numéro de page actuellement affiché*/
    private JSpinner jsPage;
    /** Le modèle pour le JSpinner jsPage **/
    private SpinnerNumberModel spinMod = new SpinnerNumberModel(1, 1, 1, 1);
	/** longueur de la ligne braille */
	private int longueurLigne;
	
	/** Rapport entre la taille du panneau d'affichage celle de la fenêtre */
	private double proportionPanneauAffichage = 0.8; 
	
	/** 
	 * Construit un objet Editeur
	 * @param taille la longueur de la ligne braille
	 * @param emb l'objet Embosseur à utiliser pour l'embossage
	 * @param g instance de GestionnaireErreur
	 */
	public Editeur(int taille, Embosseur emb, GestionnaireErreur g)
	{
	    super("Editeur",emb,g);
		//resultat.addKeyListener(this);//plus là, mais dans lors de l'activation de jcbPerkins
		longueurLigne = taille;
		jsPage = new JSpinner(spinMod);
		
		bt1erePage.addActionListener(this);
		bt1erePage.getAccessibleContext().setAccessibleName("Bouton aller à la première page");
		bt1erePage.getAccessibleContext().setAccessibleDescription("Valider pour afficher la première page");
		bt1erePage.setToolTipText("Première page (Alt+Home)");
		bt1erePage.setMnemonic(KeyEvent.VK_HOME);
		bt1erePage.setEnabled(true);
		
		btPagePrec.addActionListener(this);
		btPagePrec.getAccessibleContext().setAccessibleName("Bouton aller à la page précédente");
		btPagePrec.getAccessibleContext().setAccessibleDescription("Valider pour afficher la page précédente");
		btPagePrec.setToolTipText("Page précédente (Alt+PageUp)");
		btPagePrec.setMnemonic(KeyEvent.VK_PAGE_UP);
		btPagePrec.setEnabled(true);
		
		btPageSuiv.addActionListener(this);
		btPageSuiv.getAccessibleContext().setAccessibleName("Bouton aller à la page suivante");
		btPageSuiv.getAccessibleContext().setAccessibleDescription("Valider pour afficher la page suivante");
		btPageSuiv.setToolTipText("Page suivante (Alt+PageDown)");
		btPageSuiv.setMnemonic(KeyEvent.VK_PAGE_DOWN);
		btPageSuiv.setEnabled(true);
		
		btLastPage.addActionListener(this);
		btLastPage.getAccessibleContext().setAccessibleName("Bouton aller à la dernière page");
		btLastPage.getAccessibleContext().setAccessibleDescription("Valider pour afficher la dernière page");
		btLastPage.setToolTipText("Dernière page (Alt+End)");
		btLastPage.setMnemonic(KeyEvent.VK_END);
		btLastPage.setEnabled(true);
		
		/*
		 * Page
		 */
		jsPage.addChangeListener(this);
		jsPage.getAccessibleContext().setAccessibleName("Liste pour changer de page");
		jsPage.getAccessibleContext().setAccessibleDescription("Utilisez les flèches pour changer de page");
		jsPage.setToolTipText("Page actuelle");
		jsPage.setEnabled(true);
		jsPage.setPreferredSize(new Dimension(50,30));
		
		ligneRes.setEditable(false);//ne fait que de l'affichage
		
		/*
		 * Mise en page
		 */
		lesBoutons.setLayout(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();

		JPanel pNavigation = new JPanel();
		pNavigation.add(bt1erePage);
		pNavigation.add(btPagePrec,c);
		pNavigation.add(jsPage,c);
		pNavigation.add(btPageSuiv,c);
		pNavigation.add(btLastPage,c);
		
		c.gridx = 0;
		c.gridy = 0;
		c.gridwidth = 3;
		lesBoutons.add(pNavigation,c);
		
		c.gridx=0;
		c.gridy++;
		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",scrollRes);
		panneauAffichage.add("South",ligneRes);
		
		JPanel pLateral = new JPanel();
		LineBorder l = new LineBorder(pLateral.getBackground(),12);
		pLateral.setBorder(l);
		
		pLateral.setLayout(new GridBagLayout());
		c.gridx = 0;
		c.gridy = 0;
		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;
		pLateral.add(btEmbosser,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));}
			//System.out.println(Toolkit.getDefaultToolkit().isFrameStateSupported(Frame.MAXIMIZED_BOTH));
		}
		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);
		}
		// ajout action pour saut de page
		/*InputMap inputMap = resultat.getInputMap();
        KeyStroke key1 = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.CTRL_MASK, false);
        inputMap.put(key1, DefaultEditorKit.defaultKeyTypedAction);*/
		resultat.addCaretListener(this);
	}
	
	/** Méthode d'accès, rend visible la ligne secondaire de l'éditeur 
	 * @param b affiche {@link #ligneRes} si true*/
	public void setAfficheLigneSecondaire(boolean b){ligneRes.setVisible(b);}
	
	/**
	 * 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);
		
	    Font f = new Font(police2, Font.PLAIN, taillePolice2);
		ligneRes.setFont(f);
	}
	
	/**
	 * 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(lFichier.getText() + 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);
	    
	    FontMetrics fm = getFontMetrics(fonteBraille);
	    tailleCaractere =  fm.charWidth('a');
		resultat.setPreferredSize(new Dimension(tailleCaractere*(longueurLigne+3),380));
		this.pack();
		//System.err.println( fonteBraille.getSize() + " " + taille );;
		//resultat.setText("");
	    try
	    {
	    	BufferedReader raf = new BufferedReader(new InputStreamReader(new FileInputStream(nomFichier),encodage));
	    	String ligne;
	    	int j=0;
	    	int p=0;
	    	pages.add(new DefaultStyledDocument());
	    	while ( (ligne = raf.readLine()) != null )
	    	{

	    		if(j==ConfigNat.getCurrentConfig().getNbLigne())
	    		{
	    			//System.err.println(p+" "+j+" "+ligne);
	    			j=0;
	    			p++;
	    			pages.add(new DefaultStyledDocument());
	    		}
	    		//suppression des caractères de saut de page
	    		if(ligne.startsWith((""+(char)12))){ligne = ligne.replaceFirst(""+(char)12, "");}
	    		doc.insertString(doc.getLength(), ligne + "\n", attrs);
	    		if(j==ConfigNat.getCurrentConfig().getNbLigne()-1)
	    		{
	    			pages.get(p).insertString(pages.get(p).getLength(), ligne, attrs);
	    		}
	    		else
	    		{
	    			pages.get(p).insertString(pages.get(p).getLength(), ligne + "\n", attrs);
	    		}
	    		j++;
	    	}
	    	//supression de la dernière page si elle est vide
	    	StyledDocument last = pages.get(pages.size()-1);
	    	if(last.getText(0, last.getLength()).equals("\n")){pages.remove(last);}
	     }
	    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();
		/*
		 * test de la map perkins pour la saisie braille
		for(int i =0; i<ptPerkins.size();i++)
		{
			System.err.println(i +" " + ptPerkins.get(Integer.toString(i)));
		}*/

		spinMod.setMaximum(new Integer(pages.size()));
		super.ajouteListenerDoc(pages.get(0));
    }
	
	/**
	 * Renvoie le texte contenu dans les pages du document
	 * @return Le texte contenu dans les pages du document
	 */
	@Override
	public String getText()
	{
		String sauv ="";
		try
		{
			int i=0;
			for(i=0;i<pages.size()-1;i++){sauv = sauv + pages.get(i).getText(0, pages.get(i).getLength()) + "\n" +(char)12;}
			sauv = sauv + pages.get(i).getText(0, pages.get(i).getLength());
		}
		catch (BadLocationException e) {return null;}		
		return sauv;
	}
	
	/**
	 * Renvoie le texte contenu dans les pages du tableau p
	 * @param p tableau des pages à traiter
	 * @return le texte contenu dans les pages du tableau p
	 */
	private String getText(boolean[] p)
	{
		String sauv ="";
		try
		{
			int i=0;
			for(i=0;i<p.length-1;i++)
			{
				if(p[i]){sauv = sauv + pages.get(i).getText(0, pages.get(i).getLength()) + "\n" +(char)12;}
			}
			if(p[i]){sauv = sauv + pages.get(i).getText(0, pages.get(i).getLength());}
		}
		catch (BadLocationException e) {return null;}		
		return sauv;
	}
	
	/** Affiche la page newPage dans le JTextPane resultat  
	 * @param newPage le numéro de la page à afficher (commence à 0)
	 * @param debut vrai si il faut placer le curseur au début du texte 
	 */
	private void changePage(int newPage, boolean debut)
	{
		if(!(newPage>=pages.size())&&newPage>=0)
		{
			resultat.setDocument(pages.get(newPage));
			resultat.getDocument().addDocumentListener(this);
			resultat.getDocument().addUndoableEditListener(this);
			resultat.grabFocus();
			//resultat.setCaretPosition(positionCurseur);
			/*if(debut){resultat.setCaretPosition(0);}
			else{resultat.setCaretPosition(resultat.getText().length()-1);}*/
			pageActu = newPage;
			majLigneRes();
		}
	}
	
	/**
	 * Mise à jour de la ligne secondaire
	 */
	private void majLigneRes()
	{
		if(ligneRes.isVisible())
		{
			int posCurseur = resultat.getCaretPosition();//.getDot();
			//recherche du début de ligne
			/*int debut = posCurseur-1;
			if(debut<0){debut = 0;}*/
			int fin = posCurseur;
			boolean trouve = false;
			try 
			{
				/*while (!trouve && debut > 0)
				{
					if (!(resultat.getText(debut, 1).compareTo("\n")==0)){debut--;}
					else{trouve = true;debut++;}
				}
				trouve = false;*/
				while (!trouve && fin < resultat.getDocument().getLength())
				{
					if (!(resultat.getText(fin, 1).compareTo("\n")==0)){fin++;}
					else{trouve = true;fin--;}
				}
				//calcul du début de la ligne à représenter
				//debut = posCurseur - (posCurseur - debut)%longueurLigne;
				ligneRes.setText(resultat.getText(posCurseur, fin-posCurseur+1));//fin - debut + 1));
				ligneRes.setCaretPosition(0);
			} 
			catch (BadLocationException e){e.printStackTrace();}		
		}
	}
	/**
	 * Recalcule la taille de resultat pour que le JTextPane prenne le maximum de place en hauteur
	 * mais conserve le nombre de caractères de la ligne braille en longueur
	 */
	private void repaintResultat()
	{
		int hauteurPanneau = getContentPane().getHeight(); 
		if(ligneRes.isVisible()){proportionPanneauAffichage = 1 -(lFichier.getHeight() - lesBoutons.getHeight() - ligneRes.getHeight())/hauteurPanneau ;}
		else{proportionPanneauAffichage = 1 - (hauteurPanneau - lFichier.getHeight() - lesBoutons.getHeight())/hauteurPanneau;}
		resultat.setPreferredSize(new Dimension(tailleCaractere*(longueurLigne+3),(int)(hauteurPanneau*proportionPanneauAffichage)));
		resultat.setSize(new Dimension(tailleCaractere*(longueurLigne+3),(int)(hauteurPanneau*proportionPanneauAffichage)));
		scrollRes.setPreferredSize(new Dimension(resultat.getWidth(),(int)(hauteurPanneau*proportionPanneauAffichage)));
		scrollRes.setSize(new Dimension(resultat.getWidth(),(int)(hauteurPanneau*proportionPanneauAffichage)));
		scrollRes.validate();
		panneauAffichage.setPreferredSize(new Dimension(resultat.getWidth(),hauteurPanneau- lFichier.getHeight()-lesBoutons.getHeight()));
		panneauAffichage.setSize(new Dimension(resultat.getWidth(),hauteurPanneau- lFichier.getHeight()-lesBoutons.getHeight()));
		panneauAffichage.validate();
		//panneauAffichage.validate();
	}
	
	/** Méthode redéfinie de ComponentListener (héritée de EditeurBaille)
	 * Mis à jour de l'affichage lors du redimensionement
	 * @param arg0 Le ComponentEvent
	 */
	@Override
	public void componentResized(ComponentEvent arg0)
	{
		if (getExtendedState()==Frame.MAXIMIZED_BOTH)
		{
		ConfigNat.getCurrentConfig().setMaximizedEditeur(true);
		}
		else
		{
		ConfigNat.getCurrentConfig().setWidthEditeur(getWidth());
		ConfigNat.getCurrentConfig().setHeightEditeur(getHeight());
		ConfigNat.getCurrentConfig().setMaximizedEditeur(false);
		}
		repaint();
	}

	/** Méthode redéfinie de JFrame
	 * met à jour les dimensions du JTextPane avant de faire le paint()
	 * 
	 * @param g L'objet Graphics
	 */
	@Override
	public void paint(Graphics g)
	{
		repaintResultat();
		super.paint(g);
	}
	
	/** Méthode redéfinie de KeyListener (héritée de EditeurBraille)
	 * Gère la navigation
	 * L'affichage est réalisé dans la méthode keyReleased
	 * @param e L'objet KeyEvent intercepté
	 */
	@Override
	public void keyPressed(KeyEvent e)
	{
		//System.err.println(e.getSource().getClass().getCanonicalName());
		char ch = ' ';
		ch = e.getKeyChar();
		int code = e.getKeyCode();
		int position = resultat.getCaretPosition();
		/* SAUT DE PAGE */
		if(ch ==KeyEvent.VK_ENTER && e.getModifiers()==KeyEvent.CTRL_MASK)
		{
			try
			{
				int pos = resultat.getCaretPosition();
				
				//on compte les lignes précédentes 
				//(ajout d'un espace pour le cas limite de la fin de ligne)
				String avant = resultat.getDocument().getText(0, pos) + " ";
				int nbLigneAvant = avant.split("\n").length;
				//on remplis avec des lignes vides
				int i=0;
				for(i=nbLigneAvant;i<=ConfigNat.getCurrentConfig().getNbLigne();i++)
				{
					resultat.getDocument().insertString(resultat.getCaretPosition(), "\n", resultat.getInputAttributes());
				}
				//System.out.println("i="+i+" nbAv"+nbLigneAvant);
			}
			catch (BadLocationException e1)
			{
				e1.printStackTrace();
			}
		}
		/* MOUVEMENTS ET NAVIGATION */
		else if(code == KeyEvent.VK_LEFT)
		{
			if(position==0 && pageActu!=0)
			{
				jsPage.setValue(new Integer(pageActu));
				positionCurseur=resultat.getDocument().getLength();
				resultat.setCaretPosition(resultat.getDocument().getLength());
				//System.err.println("posfin:" + resultat.getDocument().getLength());
			}
		}
		else if(code == KeyEvent.VK_RIGHT)
		{
			if(position==resultat.getDocument().getLength() && pageActu+1<pages.size())
			{
				jsPage.setValue(new Integer(pageActu+2));
				positionCurseur=0;
				resultat.setCaretPosition(0);
			}
		}
		else if(code == KeyEvent.VK_UP)
		{
			try
			{
				if((position==0||resultat.getText(0, position).indexOf("\n")==-1) && pageActu!=0)
				{
					jsPage.setValue(new Integer(pageActu));
					positionCurseur=resultat.getDocument().getLength();
					resultat.setCaretPosition(resultat.getDocument().getLength());
					//resultat.u
				}
				else{positionCurseur=0;}
			}
			catch (BadLocationException ble) {ble.printStackTrace();}
		}
		else if(code == KeyEvent.VK_DOWN)
		{
			int lgDoc = resultat.getDocument().getLength();
			try
			{
				if((position==lgDoc||!resultat.getText(position,lgDoc-position).contains("\n")) && pageActu+1<pages.size())
				{
					jsPage.setValue(new Integer(pageActu+2));
					positionCurseur=0;
					resultat.setCaretPosition(0);
				}
			}
			catch (BadLocationException ble) {ble.printStackTrace();}
		}
		else if(code == KeyEvent.VK_HOME && e.getModifiers()==KeyEvent.CTRL_MASK + KeyEvent.SHIFT_MASK)
		{
			if(pageActu!=0)
			{
				jsPage.setValue(new Integer(1));
				positionCurseur=0;
				resultat.setCaretPosition(0);
			}
		}
		else if(code == KeyEvent.VK_END && e.getModifiers()==KeyEvent.CTRL_MASK + KeyEvent.SHIFT_MASK)
		{
			if(position==resultat.getDocument().getLength() && pageActu+1<pages.size())
			{
				jsPage.setValue(new Integer(pages.size()));
				positionCurseur=resultat.getDocument().getLength();
				resultat.setCaretPosition(resultat.getDocument().getLength());
			}
		}
		super.keyPressed(e);
	}
	
	/** Méthode redéfinie de CaretListener
	 * Si ligne secondaire (ligneRes) présente, charge le contenu de la ligne de résultat dans
	 * la ligne secondaire, avec le curseur en position
	 * Vérifie la position du curseur dans résultat
	 * @param ce Le CaretEvent
	 */
	public void caretUpdate(CaretEvent ce) 
	{
		if(resultat.getCaretPosition()==0)
		{
			//maj de la page => positionCurseur >0
			//System.out.println(""+positionCurseur);
			if(positionCurseur>0)
			{
				if(positionCurseur<=resultat.getDocument().getLength()){resultat.setCaretPosition(positionCurseur);}
				else{resultat.setCaretPosition(resultat.getDocument().getLength());}//sécurité
			}
			positionCurseur=resultat.getCaretPosition();
		}
		majLigneRes();
		//System.err.println("posCurAP="+resultat.getCaretPosition()+";pos="+positionCurseur);
	}

	/** Méthode redéfinie de DocumentListener (héritée de EditeurBaille)
	 * Affiche un message si le test area est modifié
	 * @see javax.swing.event.DocumentListener#insertUpdate(javax.swing.event.DocumentEvent)
	 */
	@Override
	public void insertUpdate(DocumentEvent de) 
	{
		super.insertUpdate(de);
		//enModif sert à gérer le fait que 2 évènements sont produits lorsqu'on fait une insertion, je sais pas pquoi...
		if(!enModif)
		{
			try {verifLinesCountChange();}
			catch (BadLocationException e) {e.printStackTrace();}
		}
		enModif=!enModif;
	}
	/**
	 * gère les ajout et suppression de lignes pour la mise en page
	 * @throws BadLocationException erreur de positionnement dans {@link #resultat}
	 */
	private void verifLinesCountChange() throws BadLocationException
	{
		//nombre de lignes (paragraphes) dans la page
		int nbSauts = pages.get(pageActu).getDefaultRootElement().getElementCount();
		/************************/
		//System.err.println("Sauts:" + nbSauts);
		//il y a des lignes en trop?
		if(nbSauts>ConfigNat.getCurrentConfig().getNbLigne())
		{
			//faut-il ajouter une page?
			if(pages.get(pages.size()-1).getDefaultRootElement().getElementCount()==ConfigNat.getCurrentConfig().getNbLigne())
			{
				pages.add(new DefaultStyledDocument());
			}
			//mise à jour des pages
			for(int i = pageActu;i<pages.size()-1;i++)//i<pageActu+1;i++)//
			{
				/****************/
				//System.err.println("-----------------\nPAGE " + i);
				//on récupère les lignes en trop
				ArrayList<String>enTrop = new ArrayList<String>();
				Element racine=pages.get(i).getDefaultRootElement();
				int j,deb,lg=0;
				for(j=ConfigNat.getCurrentConfig().getNbLigne();j<nbSauts;j++)
				{
					deb=racine.getElement(j).getStartOffset();
					lg=racine.getElement(j).getEndOffset()-deb;
					enTrop.add(pages.get(i).getText(deb,lg));
					/*******/
					//System.err.println("En trop:" + enTrop.get(enTrop.size()-1));
				}
				// on reconstruit la page avec les lignes à garder
				StyledDocument newPage = new DefaultStyledDocument();
				for(j=0;j<ConfigNat.getCurrentConfig().getNbLigne()-1;j++)//pas la dernière ligne
				{
					deb=racine.getElement(j).getStartOffset();
					lg=racine.getElement(j).getEndOffset()-deb;
					/****************/
					//System.err.println("deb="+deb+" lg=" + lg + "phrase:" + pages.get(i).getText(deb,lg));
					newPage.insertString(newPage.getLength(),pages.get(i).getText(deb,lg),resultat.getInputAttributes());
				}
				//dernière ligne
				j=ConfigNat.getCurrentConfig().getNbLigne()-1;
				deb=racine.getElement(j).getStartOffset();
				lg=racine.getElement(j).getEndOffset()-deb;
				newPage.insertString(newPage.getLength(),pages.get(i).getText(deb,lg-1),resultat.getInputAttributes());//on échappe le saut de ligne final
				pages.set(i, newPage);
				/*******/
				//System.err.println("Nouvelle page " + i + ":");
				//System.err.println(newPage.getText(0, newPage.getLength()));
				
				//on refait la page suivante
				StyledDocument newPageSuiv = new DefaultStyledDocument();
				//on ajoute les lignes existantes sans se préoccupper des dépassements
				Element racinePageSuiv = pages.get(i+1).getDefaultRootElement();
				int nbLignePageSuiv = racinePageSuiv.getElementCount();
				// la(es) nouvelle(s) ligne(s)
				for(j=0;j<enTrop.size();j++)
				{
					newPageSuiv.insertString(newPageSuiv.getLength(),enTrop.get(j),resultat.getInputAttributes());
				}
				for(j=0;j<nbLignePageSuiv;j++)
				{
					deb=racinePageSuiv.getElement(j).getStartOffset();
					lg=racinePageSuiv.getElement(j).getEndOffset()-deb;
					newPageSuiv.insertString(newPageSuiv.getLength(),pages.get(i+1).getText(deb,lg),resultat.getInputAttributes());
				}
				//on remplace la page suivante et c'est reparti pour un tour
				pages.set(i+1, newPageSuiv);
			}
			pages.get(pageActu).addDocumentListener(this);
			pages.get(pageActu).addUndoableEditListener(this);
			resultat.setDocument(pages.get(pageActu));
		}
		//il y a des lignes en moins ?
		else if(nbSauts<ConfigNat.getCurrentConfig().getNbLigne())
		{
			// cas particulier de la dernière page
			if(pageActu==pages.size()-1)
			{
				System.err.println("dernière page");
			}
			else
			{
				//mise à jour des pages
				for(int i = pageActu;i<pages.size()-1;i++)//i<pageActu+1;i++)//
				{
					int nbAjouts= ConfigNat.getCurrentConfig().getNbLigne()-nbSauts;
					/****************/
					//System.err.println("-----------------\nPAGE " + i + " ajouts:" + nbAjouts);
					//on récupère les lignes de la pages suivantes
					ArrayList<String>aAjouter = new ArrayList<String>();
					Element racinePageSuiv=pages.get(i+1).getDefaultRootElement();
					for(int j=0;j<nbAjouts;j++)
					{
						int deb=racinePageSuiv.getElement(j).getStartOffset();
						int lg=racinePageSuiv.getElement(j).getEndOffset()-deb;
						aAjouter.add(pages.get(i+1).getText(deb,lg));
						/*******/
						//System.err.println("A ajouter:" + aAjouter.get(aAjouter.size()-1));
					}
					// on reconstruit la page courante avec les lignes à ajouter
					StyledDocument newPage = new DefaultStyledDocument();
					Element racinePage = pages.get(i).getDefaultRootElement();
					int nbLignes = racinePage.getElementCount()-1;//-1 pour ne pas prendre le saut de ligne
					for(int j=0;j<nbLignes;j++)
					{
						int deb=racinePage.getElement(j).getStartOffset();
						int lg=racinePage.getElement(j).getEndOffset()-deb;
						/****************/
						//System.err.println("deb="+deb+" lg=" + lg + "phrase:" + pages.get(i).getText(deb,lg));
						newPage.insertString(newPage.getLength(),pages.get(i).getText(deb,lg),resultat.getInputAttributes());
					}
					//on ajoute les lignes
					for(int j=0;j<aAjouter.size();j++)
					{
						newPage.insertString(newPage.getLength(),aAjouter.get(j),resultat.getInputAttributes());
					}
					pages.set(i, newPage);
					/*******/
					//System.err.println("Nouvelle page " + i + ":");
					//System.err.println(newPage.getText(0, newPage.getLength()));
					
					//on refait la page suivante
					StyledDocument newPageSuiv = new DefaultStyledDocument();
					//on supprime les lignes reportées
					int nbLignePageSuiv = racinePageSuiv.getElementCount()-1;//pour le saut de ligne
					for(int j=aAjouter.size();j<nbLignePageSuiv;j++)
					{
						int deb=racinePageSuiv.getElement(j).getStartOffset();
						int lg=racinePageSuiv.getElement(j).getEndOffset()-deb;
						newPageSuiv.insertString(newPageSuiv.getLength(),pages.get(i+1).getText(deb,lg),resultat.getInputAttributes());
					}
					//on remplace la page suivante et c'est reparti pour un tour
					pages.set(i+1, newPageSuiv);
				}
				// faut-il supprimer la dernière page?
				if(pages.get(pages.size()-1).getDefaultRootElement().getElementCount()==1)
				{
					pages.remove(pages.size()-1);
					if(pageActu==pages.size()){pageActu--;}
				}
				pages.get(pageActu).addDocumentListener(this);
				pages.get(pageActu).addUndoableEditListener(this);
				resultat.setDocument(pages.get(pageActu));
			}
		}
	}

	/**
	 * Rédéfinie removeUpdate de DocumentListener (héritée de EditeurBaille)
	 * @see javax.swing.event.DocumentListener#removeUpdate(javax.swing.event.DocumentEvent)
	 */
	@Override
	public void removeUpdate(DocumentEvent de) 
	{
		super.removeUpdate(de);
		try {verifLinesCountChange();}
		catch (BadLocationException e) {e.printStackTrace();}
	}
	/**
	 * Redéfinie stateChanged de ChangeListener (héritée de EditeurBaille)
	 * Change la page courante
	 * @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)
	 */
	public void stateChanged(ChangeEvent ce) 
	{
		if(ce.getSource()==jsPage)
		{
			int np = ((Integer)jsPage.getValue()).intValue();
			if(np>=pageActu){changePage(np-1,true);}
			else{changePage(np-1,false);}
		}
		
	}
	/** 
	 * Implémente la méthode actionPerformed d'ActionListener (héritée de EditeurBaille)
	 * 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()==btEmbosser)
		{
			String table1 = ConfigNat.getCurrentConfig().getTableBraille();
			String table2 = ConfigNat.getCurrentConfig().getTableEmbossage();
			if(ConfigNat.getCurrentConfig().getIsSysTable())
			{
			 table1 = "xsl/tablesBraille/"+table1;
			}
			else
			{
			 table1 =  ConfigNat.getUserTempFolder()+"tablesBraille/"+table1;
			}
			if(ConfigNat.getCurrentConfig().getIsSysEmbossTable())
			{
			 table2 = "xsl/tablesEmbosseuse/"+table2;
			}
			else
			{
			 table2 =  ConfigNat.getUserTempFolder()+"tablesEmbosseuse/"+table2;
			}
			/* quelles pages imprimer?*/
			boolean[]p = new boolean[pages.size()];
			new DialogueEmbossage(this,p,pageActu);
				
			boolean emboss = true;
			for(int i=0;i<p.length;i++){emboss = emboss && !p[i];}
			if (!emboss)//emboss est faux si il y a au moins une page à embosser
			{
				String fichierTemp = ConfigNat.getUserTempFolder()+"sourceText.txt" ;
				String fichImp = ConfigNat.getUserTempFolder()+"tmpImp.txt";
				FileToolKit.saveStrToFile (getText(p), fichierTemp);
				//System.out.println(table1 + " " +table2);
				FileToolKit.convertBrailleFile(fichierTemp, fichImp,table1,table2,embosseur.getGest());
				embosseur.Embosser(fichImp);
			}
			
				
			/*}
			else{embosseur.Embosser(fichier);}*/
		}
		else if(evt.getSource()==bt1erePage){jsPage.setValue(new Integer(1));}
		else if(evt.getSource()==btPagePrec){if(pageActu!=0)jsPage.setValue(new Integer(pageActu));}
		else if(evt.getSource()==btPageSuiv){if(pageActu+1<pages.size())jsPage.setValue(new Integer(pageActu+2));}
		else if(evt.getSource()==btLastPage){jsPage.setValue(new Integer(pages.size()));}
	}
	
}

