/************************************************************************
 *
 *  MathConverter.java
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *  Copyright: 2002-2007 by Henrik Just
 *
 *  All Rights Reserved.
 * 
 *  Version 0.4.2 (2007-04-02)
 *
 */

package writer2latex.xhtml;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;

import writer2latex.util.Config;
import writer2latex.office.*;

public class MathConverter extends ConverterHelper {

    private boolean bSupportMathML;
	
    public MathConverter(OfficeReader ofr, Config config, Converter converter,
        boolean bSupportMathML) {

        super(ofr,config,converter);
        this.bSupportMathML = bSupportMathML;
    }
	
    public void convert(Node onode, Node hnode) {
        if (bSupportMathML) {
            convertNode(onode,hnode);
        }
        else {
            Document htmlDOM = hnode.getOwnerDocument();
            NodeList annotationList
                = ((Element) onode).getElementsByTagName(XMLString.MATH_ANNOTATION);
            if (annotationList.getLength()>0 && annotationList.item(0).hasChildNodes()) {
                // Insert the StarMath annotation as a kbd element
                Element kbd = htmlDOM.createElement("kbd");
                hnode.appendChild(kbd);
                NodeList list = annotationList.item(0).getChildNodes();
                int nLen = list.getLength();
                for (int i=0; i<nLen; i++) {
                    Node child = list.item(i);
                    if (child.getNodeType()==Node.TEXT_NODE) {
                        kbd.appendChild(htmlDOM.createTextNode(child.getNodeValue()));
                    }
                }
            }
            else {
                hnode.appendChild(htmlDOM.createTextNode("[Warning: formula ignored]"));
            }
        }
    }

    public void convertNode(Node onode, Node hnode) {
        if (onode.getNodeType()==Node.ELEMENT_NODE) {
            if (onode.getNodeName().equals(XMLString.MATH_SEMANTICS)) {
                // ignore this construction
                convertNodeList(onode.getChildNodes(),hnode);
            }
            else if (onode.getNodeName().equals(XMLString.MATH_ANNOTATION)) {
                // ignore the annotation (StarMath) completely
                // (mozilla renders it for some reason)
            }
            else {
                String sElementName = stripNamespace(onode.getNodeName());
                Element newNode = hnode.getOwnerDocument().createElement(sElementName);
                hnode.appendChild(newNode);
                if (onode.hasAttributes()) {
                    NamedNodeMap attr = onode.getAttributes();
                    int nLen = attr.getLength();
                    for (int i=0; i<nLen; i++) {
                        String sName = attr.item(i).getNodeName();
                        if (sName.equals("xmlns:math")) { sName="xmlns"; }
                        else { sName = stripNamespace(sName); }
                        String sValue = attr.item(i).getNodeValue();
                        newNode.setAttribute(sName,replacePrivateChars(sValue));
                    }
                }            
                convertNodeList(onode.getChildNodes(),newNode);
            }
        }
        else if (onode.getNodeType()==Node.TEXT_NODE) {
            String s = replacePrivateChars(onode.getNodeValue());
            hnode.appendChild(hnode.getOwnerDocument().createTextNode(s));
        }
    }
	
    private void convertNodeList(NodeList list, Node hnode) {
        if (list==null) { return; }
        int nLen = list.getLength();
        for (int i=0; i<nLen; i++) {
            convertNode(list.item(i),hnode);
        }
    }
	
    private String stripNamespace(String s) {
        int nPos = s.indexOf(':');
        return s.substring(nPos+1);
    }
	
    // OOo exports some characters (from the OpenSymbol/StarSymbol font)
    // in the private use area of unicode. These should be replaced
    // with real unicode positions.
    private String replacePrivateChars(String s) {        
        int nLen = s.length();
        StringBuffer buf = new StringBuffer(nLen);
        for (int i=0; i<nLen; i++) {
            buf.append(replacePrivateChar(s.charAt(i)));
        }
        return buf.toString();
    }

    // This method maps {Open|Star}Symbol private use area to real unicode
    // positions. This is the same table as in w2l/latex/style/symbols.xml. 
    private String replacePrivateChar(char c) {
        switch (c) {
	//      Latin Extended-B
			case '\u0192': return "&fnof;";// lettre minuscule latine f hameÃ§on
			case '\u0391': return "&Alpha;";// lettre majuscule grecque alpha
			case '\u0392': return "&Beta;";// lettre majuscule grecque beta
			case '\u0393': return "&Gamma;";// lettre majuscule grecque gamma
			case '\u0394': return "&Delta;";// lettre majuscule grecque delta
			case '\u0395': return "&Epsilon;";// lettre majuscule grecque epsilon
			case '\u0396': return "&Zeta;";// lettre majuscule grecque zeta
			case '\u0397': return "&Eta;";// lettre majuscule grecque eta
			case '\u0398': return "&Theta;";// lettre majuscule grecque theta
			case '\u0399': return "&Iota;";// lettre majuscule grecque iota
			case '\u039A': return "&Kappa;";// lettre majuscule grecque kappa
			case '\u039B': return "&Lambda;";// lettre majuscule grecque lambda
			case '\u039C': return "&Mu;";// lettre majuscule grecque mu
			case '\u039D': return "&Nu;";// lettre majuscule grecque nu
			case '\u039E': return "&Xi;";// lettre majuscule grecque xi
			case '\u039F': return "&Omicron;";// lettre majuscule grecque omicron
			case '\u03A0': return "&Pi;";// lettre majuscule grecque pi
			case '\u03A1': return "&Rho;";// lettre majuscule grecque rho
			case '\u03A3': return "&Sigma;";// lettre majuscule grecque sigma (Il n'y pas de caractère Sigmaf ni U+03A2 non plus)
			case '\u03A4': return "&Tau;";// lettre majuscule grecque tau
			case '\u03A5': return "&Upsilon;";// lettre majuscule grecque upsilon
			case '\u03A6': return "&Phi;";// lettre majuscule grecque phi
			case '\u03A7': return "&Chi;";// lettre majuscule grecque chi
			case '\u03A8': return "&Psi;";// lettre majuscule grecque psi
			case '\u03A9': return "&Omega;";// lettre majuscule grecque omega
			case '\u03B1': return "&alpha;";// lettre minuscule grecque alpha
			case '\u03B2': return "&beta;";// lettre minuscule grecque beta
			case '\u03B3': return "&gamma;";// lettre minuscule grecque gamma
			case '\u03B4': return "&delta;";// lettre minuscule grecque delta
			case '\u03B5': return "&epsilon;";// lettre minuscule grecque epsilon
			case '\u03B6': return "&zeta;";// lettre minuscule grecque zeta
			case '\u03B7': return "&eta;";// lettre minuscule grecque eta
			case '\u03B8': return "&theta;";// lettre minuscule grecque theta
			case '\u03B9': return "&iota;";// lettre minuscule grecque iota
			case '\u03BA': return "&kappa;";// lettre minuscule grecque kappa
			case '\u03BB': return "&lambda;";// lettre minuscule grecque lambda
			case '\u03BC': return "&mu;";// lettre minuscule grecque mu
			case '\u03BD': return "&nu;";// lettre minuscule grecque nu
			case '\u03BE': return "&xi;";// lettre minuscule grecque xi
			case '\u03BF': return "&omicron;";// lettre minuscule grecque omicron
			case '\u03C0': return "&pi;";// lettre minuscule grecque pi
			case '\u03C1': return "&rho;";// lettre minuscule grecque rho
			case '\u03C2': return "&sigmaf;";// lettre minuscule grecque final sigma
			case '\u03C3': return "&sigma;";// lettre minuscule grecque sigma
			case '\u03C4': return "&tau;";// lettre minuscule grecque tau
			case '\u03C5': return "&upsilon;";// lettre minuscule grecque upsilon
			case '\u03C6': return "&phi;";// lettre minuscule grecque phi
			case '\u03C7': return "&chi;";// lettre minuscule grecque chi
			case '\u03C8': return "&psi;";// lettre minuscule grecque psi
			case '\u03C9': return "&omega;";// lettre minuscule grecque omega
			case '\u03D1': return "&thetasym;";// lettre minuscule grecque theta symbol
			case '\u03D2': return "&upsih;";// symbole grec upsilon crochet
			case '\u03D6': return "&piv;";// symbole grec pi
			case '\u2022': return "&bull;";// puce (Ce N'EST PAS la même chose que l'opérateur puce, U+2219)
			case '\u2026': return "&hellip;";// points de suspension
			case '\u2032': return "&prime;";// prime
			case '\u2033': return "&Prime;";// double prime
			case '\u203E': return "&oline;";// tiret en chef
			case '\u2044': return "&frasl;";// barre de fraction
			case '\u2118': return "&weierp;";// fonction elliptique de Weierstrass
			case '\u2111': return "&image;";// majuscule I gothique = partie imaginaire
			case '\u211C': return "&real;";// majuscule R gothique = partie réelle
			case '\u2122': return "&trade;";// symbole marque commerciale
			case '\u2135': return "&alefsym;";// symbole alef = premier nombre transfini (Le symbole alef N'EST PAS pareil à la lettre hébreue alef, U+05D0 même si on pourrait utiliser le même glyphe pour représenter les deux caractères)
			case '\u2190': return "&larr;";// flèche vers la gauche
			case '\u2191': return "&uarr;";// flèche vers le haut
			case '\u2192': return "&rarr;";// flèche vers la droite
			case '\u2193': return "&darr;";// flèche vers le bas
			case '\u2194': return "&harr;";// flèche bilatérale
			case '\u21B5': return "&crarr;";// flèche vers le bas avec coin vers la gauche = retour de chariot
			case '\u21D0': return "&lArr;";// double flèche vers la gauche (ISO 10646 ne dit pas que lArr est la même chose que la flèche 'est impliqué par' et n'a pas non plus d'autre caractère pour cette fonction. Alors ? On peut utiliser lArr pour 'est impliqué par' comme le suggère)
			case '\u21D1': return "&uArr;";// double flèche vers le haut
			case '\u21D2': return "&rArr;";// double flèche vers la droite (ISO 10646 ne dit pas qu'il s'agit du caractère 'implique' et n'a pas non plus d'autre caractère avec cette fonction. Alors ? On peut utiliser rArr pour 'implique' comme le suggère)
			case '\u21D3': return "&dArr;";// double flèche vers le bas
			case '\u21D4': return "&hArr;";// double flèche bilatérale
			case '\u2200': return "&forall;";// pour tous
			case '\u2202': return "&part;";// dérivée partielle
			case '\u2203': return "&exist;";// il existe
			case '\u2205': return "&empty;";// ensemble vide = symbole diamètre
			case '\u2207': return "&nabla;";// nabla
			case '\u2208': return "&isin;";// appartient à
			case '\u2209': return "&notin;";// n'appartient pas à
			case '\u220B': return "&ni;";// contient comme élément (Est-ce qu'il ne pourrait pas y avoir un nom plus parlant que 'ni' ?)
			case '\u220F': return "&prod;";// produit de la famille = signe produit (prod N'EST PAS le même caractère que U+03A0 'lettre capitale grecque pi' même si le même glyphe peut s'utiliser pour les deux)
			case '\u2211': return "&sum;";// sommation de la famille (sum N'EST PAS le même caractère que U+03A3 'ettre capitale grecque sigma' même si le même glyphe peut s'utiliser pour les deux)
			case '\u2212': return "&minus;";// signe moins
			case '\u2217': return "&lowast;";// opérateur astérisque
			case '\u221A': return "&radic;";// racine carrée = signe radical
			case '\u221D': return "&prop;";// proportionnel à
			case '\u221E': return "&infin;";// infini
			case '\u2220': return "&ang;";// angle
			case '\u2227': return "&and;";// ET logique
			case '\u2228': return "&or;";// OU logique
			case '\u2229': return "&cap;";// intersection = cap
			case '\u222A': return "&cup;";// union = cup
			case '\u222B': return "&int;";// intégrale
			case '\u2234': return "&there4;";// par conséquent
			case '\u223C': return "&sim;";// opérateur tilde = varie avec = similaire à (L'opérateur tilde N'EST PAS le même caractère que le tilde U+007E, même si le même glyphe peut s'utiliser pour les deux)
			case '\u2245': return "&cong;";// approximativement égal à
			case '\u2248': return "&asymp;";// presque égal à = asymptotique à
			case '\u2260': return "&ne;";// pas égal à
			case '\u2261': return "&equiv;";// identique à
			case '\u2264': return "&le;";// plus petit ou égal à
			case '\u2265': return "&ge;";// plus grand ou égal à
			case '\u2282': return "&sub;";// sous-ensemble de
			case '\u2283': return "&sup;";// sur-ensemble de (Remarquez que nsup 'pas un sur-ensemble de' 2285, n'est pas couvert par le codage de la police Symbol. Devrait-il l'être par symétrie ? Il est dans)
			case '\u2284': return "&nsub;";// pas un sous-ensemble de
			case '\u2286': return "&sube;";// sous-ensemble ou égal à
			case '\u2287': return "&supe;";// sur-ensemble de ou égal à
			case '\u2295': return "&oplus;";// plus cerclé = somme directe
			case '\u2297': return "&otimes;";// multiplié par cerclé = produit vectoriel
			case '\u22A5': return "&perp;";// taquet vers le haut = orthogonal à = perpendiculaire
			case '\u22C5': return "&sdot;";// opérateur point (L'opérateur point N'EST PAS le même caractère que le 'point médian', U+00B7)
			case '\u2308': return "&lceil;";// plafond à gauche = anglet gauche
			case '\u2309': return "&rceil;";// plafond à droite
			case '\u230A': return "&lfloor;";// plancher à gauche
			case '\u230B': return "&rfloor;";// plancher à droite
			case '\u2329': return "&lang;";// chevron vers la gauche (lang N'EST PAS le même caractère que U+003C 'inférieur à' ou U+2039 'guillemet simple vers la gauche')
			case '\u232A': return "&rang;";// chevron vers la droite (rang iN'EST PAS le même caractère que U+003E 'supérieur à' ou U+203A 'guillemet simple vers la droite')
			case '\u25CA': return "&loz;";// losange
			case '\u2660': return "&spades;";// pique noir (Noir semble dire ici rempli par opposition à ajouré)
			case '\u2663': return "&clubs;";// trèfle noir
			case '\u2665': return "&hearts;";// coeur noir
			case '\u2666': return "&diams;";// carreau noir
			// truc pas prévus
			case '\u2102': return "&Copf;";// ensemble C des complexes
			case '\u2115': return "&Nopf;";// ensemble N des entiers
			case '\u211A': return "&Qopf;";// ensemble Q des rationnels
			case '\u211D': return "&Ropf;";// ensemble R des réels
			case '\u2124': return "&Zopf;";// ensemble R des entiers relatifs
			case '\u2223': return "&mid;";// divise
			case '\u2224': return "&nmid;";// ne divise pas
			case '\u2243': return "&simeq;";// asymptotiquement égal à
			case '\u2244': return "&nsimeq;";// asymptotiquement égal à
			case '\u2225': return "&par;";// parallèle à
			case '\u00B1': return "&PlusMinus;";// plus ou moins
			case '\u2213': return "&MinusPlus;"; // moins ou plus (différent de plus ou moins)
			case '\u2494': return "&leqslant;"; // inférieur ou égal à incliné
			case '\u2270': return "&nle;"; //non inférieur ou égal à incliné
			case '\u00AC': return "&not;";// signe not
			case '\u00B0': return "&circ;";// petit cercle, opérateur concaténation, normalement &deg; mais on va le considéré comme circ
			case '\u224A': return "&ape;";// approxivativement égal à 
			case '\u002B': return "&plus;"; // signe plus
			case '\u00D7': return "&times;"; // signe multiplication (croix)
			case '\u003D': return "&equals;"; // signe égal
			case '\u226E': return "&nlt;"; // non inférieur à
			case '\u2A7D': return "&les;"; // inférieur à incliné = leqslant
			case '\u220A': return "&isin;";// appartient à
			case '\u2216': return "&setminus;";// différence d'ensemble
			case '\u2288': return "&nsube;";// ni un sous-ensemble ni égal à
			case '\u2289': return "&nsupe;";// ni un surensemble ni égal à
			case '\u2285': return "&nsup;";// non un surensemble de
			case '\u301A': return "&lobrk;";// crochet gauche avec barre 
			case '\u301B': return "&robrk;";// crochet droit avec barre 
			case '\u2210': return "&coprod;";// coproduit (Pi à l'envers)
			case '\u222C': return "&Int;";// intégrale double
			case '\u222D': return "&tint;";// intégrale triple
			case '\u222E': return "&conint;";// intégrale de contour
			case '\u222F': return "&Conint;";// intégrale de surface
			case '\u2230': return "&Cconint;";// intégrale de volume
			case '\u210F': return "&hbar;";// const de Planck sur 2Pi
			case '\u2253': return "&;";// BUG points suspensions diagonale descendant à droite
			case '\u22EE': return "&vellip;";// points suspensions verticaux
			case '\u22EF': return "&ctdot;";// points suspensions horizontaux médians
			case '\u22F0': return "&utdot;";// points suspensions diagonale montant à droite
			case '\u22F1': return "&dtdot;";// points suspensions diagonale descendant à droite
			case '\u02DA': return "&ring;"; //rond en chef
			case '\u00A8':	return "&Dot;"; // double point en chef(tréma)
			case '\u02D9':	return "&dot;"; // point en chef
			case '\u2015': return "&horbar;"; // barre horizonthale
			case '\u00AF': return "&macr;"; // barre horizonthale en chef
			case '\u0332': return "&UnderBar;"; // souligné
			case '\u2222': return "&angsph;"; // angle sphérique
			case '\u03F1': return "&rhov;"; // symbole grec rho final
			case '\u226B': return "&Gt;"; // très supérieur à
			case '\u226A': return "&Lt;"; // très inférieur à
			case '\u2277': return "&gl;"; // plus grand ou plus petit que
			// version originale ------------------------------------
		    case '\uE002': return "\u2666"; //diamondsuit ?
		    case '\uE003': return "\u25C6"; //FilledBigDiamondshape ?
		    case '\uE005': return "\u274D"; //CircleShadow ?
		    case '\uE006': return "\u2794"; // ?
		    case '\uE007': return "&check;"; //Checkmark
		    case '\uE008': return "\u25CF"; // FilledBigCircle ?
		    case '\uE009': return "\u274D"; // CircleShadow ?
		    case '\uE00A': return "\u25FC"; // ?
		    case '\uE00B': return "\u2752"; //SquareCastShadowTopRight ?
		    case '\uE00D': return "\u2756"; //OrnamentDiamondSolid ?
		    case '\uE013': return "\u2742"; //EightFlowerPetal
		    case '\uE01B': return "\u270D"; //textcopyright
		    case '\uE01E': return "\u2022"; // bullet
		    case '\uE021': return "\u00A9";
		    case '\uE024': return "\u00AE";
		    case '\uE025': return "\u21E8";
		    case '\uE026': return "\u21E9";
		    case '\uE027': return "\u21E6";
		    case '\uE028': return "\u21E7";
		    case '\uE02B': return "\u279E";
		    case '\uE032': return "\u2741";
		    case '\uE036': return "(";//'\u0028';
		    case '\uE037': return ")";//'\u0029';
		    case '\uE03A': return "\u20AC";
		    case '\uE080': return "&permil;"; // pour mille
		    case '\uE081': return "\uFE38"; // underbrace
		    case '\uE082': return "\uFE37"; // overbrace
		    case '\uE083': return "&plus;";//'\u002B';  plus
		    case '\uE084': return "&lt;";
		    case '\uE085': return "&gt;";
		    case '\uE086': return "&le;"; //inf ou égal à
		    case '\uE087': return "&ge;"; //sip ou égal à
		    case '\uE089': return "&isin;";//'\u2208'; appartient à 
		    case '\uE08B': return "&hellip;";// pt suspension, ellipse horizontale (2026)";
		    case '\uE08C': return "&rarr;";//"\u2192"; flèche droite
		    case '\uE090': return "&par;"; // parallèle à
		    case '\uE091': return "&circ;"; // accent circonflexe
		    case '\uE092': return "&ogon;"; //ressemble à check: ogonek
		    case '\uE093': return "&breve;"; // demi cercle en chef
		    case '\uE094': return "&acute;"; // accent aigu
		    case '\uE095': return "&grave;"; // accent grave
		    case '\uE096': return "&tilde;"; // 02DC (or 007E)
		    case '\uE097': return "&macr;"; // barre horizonthale au dessus
		    case '\uE098': return "&rArr;";//"\u2192 (faux)"; // or 21E1 double flèche droite
		    case '\uE09B': return "&tdot;"; // triple dot, neither MathPlayer nor Mozilla understands this glyph
		    case '\uE09E': return "(";//'\u0028';
		    case '\uE09F': return ")";//'\u0029';
		    case '\uE0A0': return "&angmsd;"; // angle mesuré
		    case '\uE0AA': return "carré_ombré"; // carré ombré en bas à droite. N'a pas de nom!
		    case '\uE0AB': return "&Beta;";
		    case '\uE0AC': return "&Gamma;";//bruno u0393
		    case '\uE0AD': return "&Delta;";//'\u0394';
		    case '\uE0AE': return "&Theta;";//'\u0398';
		    case '\uE0AF': return "&Lambda;";
		    case '\uE0B0': return "&Xi;";
		    case '\uE0B1': return "&Pi;";
		    case '\uE0B2': return "&Sigma;";
		    case '\uE0B3': return "&Upsilon;";
		    case '\uE0B4': return "&Phi;";
		    case '\uE0B5': return "&Psi;";
		    case '\uE0B6': return "&Omega;";
		    case '\uE0B7': return "&alpha;";//'\u03B1';
		    case '\uE0B8': return "&beta;";//'\u03B2';
		    case '\uE0B9': return "&gamma;";//'\u03B3';
		    case '\uE0BA': return "&delta;";
		    case '\uE0BB': return "&epsi;";
		    case '\uE0BC': return "&zeta;";
		    case '\uE0BD': return "&eta;";
		    case '\uE0BE': return "&theta;";
		    case '\uE0BF': return "&iota;";
		    case '\uE0C0': return "&kappa;";
		    case '\uE0C1': return "&lambda;";
		    case '\uE0C2': return "&mu;";
		    case '\uE0C3': return "&nu;";
		    case '\uE0C4': return "&xi;";
		    case '\uE0C5': return "&omicron;";
		    case '\uE0C6': return "&pi;";
		    case '\uE0C7': return "&rho;";
				// A pas u03C2 (sigma final): en fait si: sigmav
		    case '\uE0C8': return "&sigma;";
		    case '\uE0C9': return "&tau;";
		    case '\uE0CA': return "&upsilon;";
		    case '\uE0CB': return "&phi;";
		    case '\uE0CC': return "&chi;";
		    case '\uE0CD': return "&psi;";
		    case '\uE0CE': return "&omega;";
		    case '\uE0CF': return "&epsi;";
		    case '\uE0D0': return "&thetav;"; // symbole grec théta 
		    case '\uE0D1': return "&piv;"; // symbole grec pi
		    case '\uE0D3': return "&sigmav;"; // sigma final
		    case '\uE0D4': return "&phiv;";
		    case '\uE0D5': return "&part;"; //dérivée partielle
		    case '\uE0D9': return "&top;"; // Taquet vers le bas
		    case '\uE0DB': return "&larr;";
		    case '\uE0DC': return "&uarr;";
		    case '\uE0DD': return "&darr;";

            default: 
                return c + "";
        }
    }

}