/************************************************************************
 *
 *  Config.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.5 (2007-07-24)
 *
 */

package writer2latex.util;

/* Configuration 
*/

//import java.util.Iterator;
import java.util.LinkedList;
import java.util.Hashtable;
import java.util.Enumeration;
import java.io.*;
//import java.net.URI;
//import java.net.URL;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Element;
import org.w3c.dom.DOMImplementation;

import writer2latex.xmerge.NewDOMDocument;
import writer2latex.latex.i18n.I18n;
import writer2latex.latex.i18n.ReplacementTrie;
import writer2latex.latex.util.HeadingMap;
import writer2latex.latex.util.StyleMap;
import writer2latex.xhtml.XhtmlStyleMap;
//import writer2latex.xhtml.XhtmlDocument;

public class Config {
    public static final int GENERIC = 0;
    public static final int DVIPS = 1;
    public static final int PDFTEX = 2;
    public static final int UNSPECIFIED = 3;
	
    // Formatting (must be ordered)
    public static final int IGNORE_ALL = 0;
    public static final int IGNORE_MOST = 1;
    public static final int CONVERT_BASIC = 2;
    public static final int CONVERT_MOST = 3;
    public static final int CONVERT_ALL = 4;
    // Xhtml formatting
    public static final int IGNORE_STYLES = 6;
    public static final int IGNORE_HARD = 7;
    // Page formatting
    public static final int CONVERT_HEADER_FOOTER = 5;
    // Handling of other formatting
    public static final int IGNORE = 0;
    public static final int ACCEPT = 1;
    public static final int WARNING = 2;
    public static final int ERROR = 3;
	
    // Notes
    //public static final int IGNORE = 0;
    public static final int COMMENT = 1;
    public static final int PDFANNOTATION = 2;
    public static final int MARGINPAR = 3;
    public static final int CUSTOM = 4;
	
    private static final int BACKEND = 0;
    private static final int NO_PREAMBLE = 1;
    private static final int NO_INDEX = 2;
    private static final int DOCUMENTCLASS = 3;
    private static final int GLOBAL_OPTIONS = 4;
    private static final int INPUTENCODING = 5;
    private static final int MULTILINGUAL = 6;
    private static final int GREEK_MATH = 7;
    private static final int USE_OOOMATH = 8;
    private static final int USE_PIFONT = 9;
    private static final int USE_IFSYM = 10;
    private static final int USE_WASYSYM = 11;
    private static final int USE_BBDING = 12;
    private static final int USE_EUROSYM = 13;
    private static final int USE_TIPA = 14;
    private static final int USE_COLOR = 15;
    private static final int USE_COLORTBL = 16;
    private static final int USE_GEOMETRY = 17;
    private static final int USE_FANCYHDR = 18;
    private static final int USE_HYPERREF = 19;
    private static final int USE_CAPTION = 20;
    private static final int USE_LONGTABLE = 21;
    private static final int USE_SUPERTABULAR = 22;
    private static final int USE_TABULARY = 23;
    private static final int USE_ENDNOTES = 24;
    private static final int USE_ULEM = 25;
    private static final int USE_LASTPAGE = 26;
    private static final int USE_TITLEREF = 27;
    private static final int USE_OOOREF = 28;
    private static final int USE_BIBTEX = 29;
    private static final int BIBTEX_STYLE = 30;
    private static final int FORMATTING = 31;
    private static final int PAGE_FORMATTING = 32;
    private static final int OTHER_STYLES = 33;
    private static final int IMAGE_CONTENT = 34;
	private static final int TABLE_CONTENT = 35;
    private static final int IGNORE_HARD_PAGE_BREAKS = 36;
    private static final int IGNORE_HARD_LINE_BREAKS = 37;
    private static final int IGNORE_EMPTY_PARAGRAPHS = 38;
    private static final int IGNORE_DOUBLE_SPACES = 39;
    private static final int ALIGN_FRAMES = 40;
    private static final int FLOAT_FIGURES = 41; 
    private static final int FLOAT_TABLES = 42; 
    private static final int FLOAT_OPTIONS = 43;
    private static final int FIGURE_SEQUENCE_NAME = 44; 
    private static final int TABLE_SEQUENCE_NAME = 45; 
    private static final int IMAGE_OPTIONS = 46;
    private static final int REMOVE_GRAPHICS_EXTENSION = 47;
    private static final int KEEP_IMAGE_SIZE = 48;
    private static final int SIMPLE_TABLE_LIMIT = 49;
    private static final int NOTES = 50;
    private static final int TABSTOP = 51;
    private static final int WRAP_LINES_AFTER = 52;
    private static final int SPLIT_LINKED_SECTIONS = 53;
    private static final int SPLIT_TOPLEVEL_SECTIONS =54;
    private static final int XHTML_NO_DOCTYPE = 55;
    private static final int XHTML_ENCODING = 56;
    private static final int XHTML_CUSTOM_STYLESHEET = 57;
    private static final int XHTML_FORMATTING = 58;
    private static final int XHTML_FRAME_FORMATTING = 59;
    private static final int XHTML_SECTION_FORMATTING = 60;
    private static final int XHTML_TABLE_FORMATTING = 61;
    private static final int XHTML_IGNORE_TABLE_DIMENSIONS = 62;
    private static final int XHTML_USE_DUBLIN_CORE = 63;
    private static final int XHTML_CONVERT_TO_PX = 64;
    private static final int XHTML_SCALING = 65;
    private static final int XHTML_COLUMN_SCALING = 66;
    private static final int XHTML_FLOAT_OBJECTS = 67;
    private static final int XHTML_TABSTOP_STYLE = 68;
    private static final int XHTML_USE_LIST_HACK = 69;
    private static final int XHTML_SPLIT_LEVEL = 70;
    private static final int XHTML_CALC_SPLIT = 71;
    private static final int XHTML_UPLINK = 72;
    private static final int XHTML_DIRECTORY_ICON = 73;
    private static final int XHTML_DOCUMENT_ICON = 74;
    private static final int DEBUG = 75;

    private static final int OPTION_COUNT = 76;
	
    Option[] options = new Option[OPTION_COUNT];

    protected boolean bDebug = false;

    protected LinkedList<String> customPreamble = new LinkedList<String>();
    protected StyleMap par = new StyleMap();
    protected StyleMap parBlock = new StyleMap();
    protected StyleMap text = new StyleMap();
    protected StyleMap list = new StyleMap();
    protected StyleMap listItem = new StyleMap();
    protected HeadingMap headingMap = new HeadingMap(5);
    protected XhtmlStyleMap xpar = new XhtmlStyleMap();
    protected XhtmlStyleMap xtext = new XhtmlStyleMap();
    protected XhtmlStyleMap xframe = new XhtmlStyleMap();
    protected XhtmlStyleMap xlist = new XhtmlStyleMap();
    protected XhtmlStyleMap xattr = new XhtmlStyleMap();
    protected Hashtable<String, String> mathSymbols = new Hashtable<String, String>();
    protected ReplacementTrie stringReplace = new ReplacementTrie();
	
    public Config() {
        // create options with default values
        options[NO_PREAMBLE] = new BooleanOption("no_preamble","false");
        options[NO_INDEX] = new BooleanOption("no_index","false");
        options[DOCUMENTCLASS] = new Option("documentclass","article");
        options[GLOBAL_OPTIONS] = new Option("global_options","");
        options[BACKEND] = new IntegerOption("backend","pdftex") {
            void setString(String sValue) {
                super.setString(sValue);
                if ("generic".equals(sValue)) nValue = GENERIC;
                else if ("dvips".equals(sValue)) nValue = DVIPS;
                else if ("pdftex".equals(sValue)) nValue = PDFTEX;
                else if ("unspecified".equals(sValue)) nValue = UNSPECIFIED;
            }
        };
        options[INPUTENCODING] = new IntegerOption("inputencoding",I18n.writeInputenc(I18n.ASCII)) {
            void setString(String sValue) {
                super.setString(sValue);
                nValue = I18n.readInputenc(sValue);
            }
        };
        options[MULTILINGUAL] = new BooleanOption("multilingual","true");
        options[GREEK_MATH] = new BooleanOption("greek_math","true");
        options[USE_OOOMATH] = new BooleanOption("use_ooomath","false");
        options[USE_PIFONT] = new BooleanOption("use_pifont","false");
        options[USE_IFSYM] = new BooleanOption("use_ifsym","false");
        options[USE_WASYSYM] = new BooleanOption("use_wasysym","false");
        options[USE_BBDING] = new BooleanOption("use_bbding","false");
        options[USE_EUROSYM] = new BooleanOption("use_eurosym","false");
        options[USE_TIPA] = new BooleanOption("use_tipa","false");
        options[USE_COLOR] = new BooleanOption("use_color","true");
        options[USE_COLORTBL] = new BooleanOption("use_colortbl","false");
        options[USE_GEOMETRY] = new BooleanOption("use_geometry","false");
        options[USE_FANCYHDR] = new BooleanOption("use_fancyhdr","false");
        options[USE_HYPERREF] = new BooleanOption("use_hyperref","true");
        options[USE_CAPTION] = new BooleanOption("use_caption","false");
        options[USE_LONGTABLE] = new BooleanOption("use_longtable","false");
        options[USE_SUPERTABULAR] = new BooleanOption("use_supertabular","true");
        options[USE_TABULARY] = new BooleanOption("use_tabulary","false");
        options[USE_ENDNOTES] = new BooleanOption("use_endnotes","false");
        options[USE_ULEM] = new BooleanOption("use_ulem","false");
        options[USE_LASTPAGE] = new BooleanOption("use_lastpage","false");
        options[USE_TITLEREF] = new BooleanOption("use_titleref","false");
        options[USE_OOOREF] = new BooleanOption("use_oooref","false");
        options[USE_BIBTEX] = new BooleanOption("use_bibtex","false");
        options[BIBTEX_STYLE] = new Option("bibtex_style","plain");
        options[FORMATTING] = new IntegerOption("formatting","convert_basic") {
            void setString(String sValue) {
                super.setString(sValue);
                if ("convert_all".equals(sValue)) nValue = CONVERT_ALL;
                else if ("convert_most".equals(sValue)) nValue = CONVERT_MOST;
                else if ("convert_basic".equals(sValue)) nValue = CONVERT_BASIC;
                else if ("ignore_most".equals(sValue)) nValue = IGNORE_MOST;
                else if ("ignore_all".equals(sValue)) nValue = IGNORE_ALL;
            }
        };
        options[PAGE_FORMATTING] = new IntegerOption("page_formatting","convert_all") {
            void setString(String sValue) {
                super.setString(sValue);
                if ("convert_all".equals(sValue)) nValue = CONVERT_ALL;
                else if ("convert_header_footer".equals(sValue)) nValue = CONVERT_HEADER_FOOTER;
                else if ("ignore_all".equals(sValue)) nValue = IGNORE_ALL;
            }
        };
        options[OTHER_STYLES] = new ContentHandlingOption("other_styles","accept");
        options[IMAGE_CONTENT] = new ContentHandlingOption("image_content","accept");
        options[TABLE_CONTENT] = new ContentHandlingOption("table_content","accept");
        options[IGNORE_HARD_PAGE_BREAKS] = new BooleanOption("ignore_hard_page_breaks","false");
        options[IGNORE_HARD_LINE_BREAKS] = new BooleanOption("ignore_hard_line_breaks","false");
        options[IGNORE_EMPTY_PARAGRAPHS] = new BooleanOption("ignore_empty_paragraphs","false");
        options[IGNORE_DOUBLE_SPACES] = new BooleanOption("ignore_double_spaces","false");
        options[ALIGN_FRAMES] = new BooleanOption("align_frames","true");
        options[FLOAT_FIGURES] = new BooleanOption("float_figures","false");
        options[FLOAT_TABLES] = new BooleanOption("float_tables","false");
        options[FLOAT_OPTIONS] = new Option("float_options","h");
        options[FIGURE_SEQUENCE_NAME] = new BooleanOption("figure_sequence_name","");
        options[TABLE_SEQUENCE_NAME] = new BooleanOption("table_sequence_name","");
        options[IMAGE_OPTIONS] = new Option("image_options","");
        options[REMOVE_GRAPHICS_EXTENSION] = new BooleanOption("remove_graphics_extension","false");
        options[KEEP_IMAGE_SIZE] = new BooleanOption("keep_image_size","false");
        options[SIMPLE_TABLE_LIMIT] = new IntegerOption("simple_table_limit","0") {
           void setString(String sValue) {
               super.setString(sValue);
               nValue = Misc.getPosInteger(sValue,0);
           }
        };
        options[NOTES] = new IntegerOption("notes","comment") {
            void setString(String sValue) {
                super.setString(sValue);
                if ("ignore".equals(sValue)) nValue = IGNORE;
                else if ("comment".equals(sValue)) nValue = COMMENT;
                else if ("pdfannotation".equals(sValue)) nValue = PDFANNOTATION;
                else if ("marginpar".equals(sValue)) nValue = MARGINPAR;
                else nValue = CUSTOM;
            }
        };
        options[TABSTOP] = new Option("tabstop","");
        options[WRAP_LINES_AFTER] = new IntegerOption("wrap_lines_after","72") {
            void setString(String sValue) {
                super.setString(sValue);
                nValue = Misc.getPosInteger(sValue,0);
            }
        };
        options[SPLIT_LINKED_SECTIONS] = new BooleanOption("split_linked_sections","false");
        options[SPLIT_TOPLEVEL_SECTIONS] = new BooleanOption("split_toplevel_sections","false");
        options[XHTML_NO_DOCTYPE] = new BooleanOption("xhtml_no_doctype","false");
        options[XHTML_ENCODING] = new Option("xhtml_encoding","UTF-8");
        options[XHTML_CUSTOM_STYLESHEET] = new Option("xhtml_custom_stylesheet","");
        options[XHTML_FORMATTING] = new XhtmlFormatOption("xhtml_formatting","convert_all");
        options[XHTML_FRAME_FORMATTING] = new XhtmlFormatOption("xhtml_frame_formatting","convert_all");
        options[XHTML_SECTION_FORMATTING] = new XhtmlFormatOption("xhtml_section_formatting","convert_all");
        options[XHTML_TABLE_FORMATTING] = new XhtmlFormatOption("xhtml_table_formatting","convert_all");
        options[XHTML_IGNORE_TABLE_DIMENSIONS] = new BooleanOption("xhtml_ignore_table_dimensions","false");
        options[XHTML_USE_DUBLIN_CORE] = new BooleanOption("xhtml_use_dublin_core","true");
        options[XHTML_CONVERT_TO_PX] = new BooleanOption("xhtml_convert_to_px","true");
        options[XHTML_SCALING] = new Option("xhtml_scaling","100%");
        options[XHTML_COLUMN_SCALING] = new Option("xhtml_column_scaling","100%");
        options[XHTML_FLOAT_OBJECTS] = new BooleanOption("xhtml_float_objects","true");
        options[XHTML_TABSTOP_STYLE] = new Option("xhtml_tabstop_style","");
        options[XHTML_USE_LIST_HACK] = new BooleanOption("xhtml_use_list_hack","false");
        options[XHTML_SPLIT_LEVEL] = new IntegerOption("xhtml_split_level","0") {
            void setString(String sValue) {
                super.setString(sValue);
                nValue = Misc.getPosInteger(sValue,0);
            }
        };
        options[XHTML_CALC_SPLIT] = new BooleanOption("xhtml_calc_split","false");
        options[XHTML_UPLINK] = new Option("xhtml_uplink","");
        options[XHTML_DIRECTORY_ICON] = new Option("xhtml_directory_icon","");
        options[XHTML_DOCUMENT_ICON] = new Option("xhtml_document_icon","");
        options[DEBUG] = new BooleanOption("debug","false");
        // Headings for article class:
        headingMap.setLevelData(1,"section",1);
        headingMap.setLevelData(2,"subsection",2);
        headingMap.setLevelData(3,"subsubsection",3);
        headingMap.setLevelData(4,"paragraph",4);
        headingMap.setLevelData(5,"subparagraph",5);
        // Standard string replace:
        // Fix french spacing; replace nonbreaking space 
        // right before em-dash, !, ?, : and ; (babel handles this)
        stringReplace.put("\u00A0\u2014"," \u2014",I18n.readFontencs("any"));
        stringReplace.put("\u00A0!"," !",I18n.readFontencs("any"));
        stringReplace.put("\u00A0?"," ?",I18n.readFontencs("any"));
        stringReplace.put("\u00A0:"," :",I18n.readFontencs("any"));
        stringReplace.put("\u00A0;"," ;",I18n.readFontencs("any"));
        // Right after opening guillemet and right before closing  guillemet:
        // Here we must *keep* the non-breaking space
        // TODO: Use \og and \fg if the document contains french...
        //stringReplace.put("\u00AB\u00A0","\u00AB ",I18n.readFontencs("any"));
        //stringReplace.put("\u00A0\u00BB"," \u00BB",I18n.readFontencs("any"));
    }
	
    public void setOption(String sName,String sValue) {
        for (int j=0; j<OPTION_COUNT; j++) {
            if (options[j].getName().equals(sName)) {
                options[j].setString(sValue);
                break;
            }
        }
    }
	
    /** <p>Read configuration from a specified input stream</p>
     *  @param is the input stream to read the configuration from
     */
    public void read(InputStream is) {
        NewDOMDocument doc = new NewDOMDocument("config",".xml");
        try {
            doc.read(is);
        } catch(IOException e) {
            System.err.println("Oops - I cannot read the configuration file");
            e.printStackTrace();
            return; // give up and continue without the configuration
        }
        Document dom = doc.getContentDOM();
        if (dom==null) {
            System.err.println("Oops - I cannot understand the contents of the configuration file");
            return; // give up and continue without the configuration
        }
        Node root = dom.getDocumentElement();
        if (!root.hasChildNodes()) { return; }
        NodeList nl = root.getChildNodes();
        int nLen = nl.getLength();
        for (int i=0; i<nLen; i++) {
            Node child = nl.item(i);
            if (child.getNodeType()==Node.ELEMENT_NODE) {
                String sChildName = child.getNodeName();
                if (sChildName.equals("option")) {
                    String sName = Misc.getAttribute(child,"name");
                    String sValue = Misc.getAttribute(child,"value");
                    setOption(sName,sValue);
                }
                else if (sChildName.equals("style-map")) {
                    String sName = Misc.getAttribute(child,"name");
                    String sClass = Misc.getAttribute(child,"class");
                    String sBefore = Misc.getAttribute(child,"before");
                    String sAfter = Misc.getAttribute(child,"after");
                    boolean bLineBreak = !"false".equals(Misc.getAttribute(child,"line-break"));
                    boolean bVerbatim = "true".equals(Misc.getAttribute(child,"verbatim"));
                    if ("paragraph".equals(sClass)) {
                        par.put(sName,sBefore,sAfter,bLineBreak,bVerbatim);
                    }
                    if ("paragraph-block".equals(sClass)) {
                        String sNext = Misc.getAttribute(child,"next");
                        parBlock.put(sName,sBefore,sAfter,sNext,bVerbatim);
                    }
                    else if ("text".equals(sClass)) {
                        text.put(sName,sBefore,sAfter,false,bVerbatim);
                    }
                    else if ("list".equals(sClass)) {
                        list.put(sName,sBefore,sAfter);
                    }
                    else if ("listitem".equals(sClass)) {
                        listItem.put(sName,sBefore,sAfter);
                    }
                }
                else if (sChildName.equals("heading-map")) {
                    readHeadingMap(child);
                }
                else if (sChildName.equals("string-replace")) {
                    String sInput = Misc.getAttribute(child,"input");
                    String sLaTeXCode = Misc.getAttribute(child,"latex-code");
                    String sFontencs = Misc.getAttribute(child,"fontencs");
                    int nFontencs = I18n.readFontencs(sFontencs!=null?sFontencs:"any");
                    stringReplace.put(sInput,sLaTeXCode,nFontencs);
                }
                else if (sChildName.equals("custom-preamble")) {
                    if (child.hasChildNodes()) {
                        NodeList subNl = child.getChildNodes();
                        int nSubLen = subNl.getLength();
                        for (int j=0; j<nSubLen; j++) {
                            Node subChild = subNl.item(j);
                            if (subChild.getNodeType()==Node.TEXT_NODE) {
                                customPreamble.add(subChild.getNodeValue());
                            }
                        }
                    }
                }
                else if (sChildName.equals("xhtml-style-map")) {
                    String sName = Misc.getAttribute(child,"name");
                    String sClass = Misc.getAttribute(child,"class");
                    String sBlockElement = Misc.getAttribute(child,"block-element");
                    if (sBlockElement==null) { sBlockElement=""; }
                    String sBlockCss = Misc.getAttribute(child,"block-css");
                    if (sBlockCss==null) { sBlockCss="(none)"; }
                    String sElement = Misc.getAttribute(child,"element");
                    if (sElement==null) { sElement=""; }
                    String sCss = Misc.getAttribute(child,"css");
                    if (sCss==null) { sCss="(none)"; }
                    if ("paragraph".equals(sClass)) {
                        xpar.put(sName,sBlockElement,sBlockCss,sElement,sCss);
                    }
                    else if ("text".equals(sClass)) {
                        xtext.put(sName,sBlockElement,sBlockCss,sElement,sCss);
                    }
                    else if ("frame".equals(sClass)) {
                        xframe.put(sName,sBlockElement,sBlockCss,sElement,sCss);
                    }
                    else if ("list".equals(sClass)) {
                        xlist.put(sName,sBlockElement,sBlockCss,sElement,sCss);
                    }
                    else if ("attribute".equals(sClass)) {
                        xattr.put(sName,sBlockElement,sBlockCss,sElement,sCss);
                    }
                }
                else if (sChildName.equals("math-symbol-map")) {
                    String sName = Misc.getAttribute(child,"name");
                    String sLatex = Misc.getAttribute(child,"latex");
                    mathSymbols.put(sName,sLatex);
                }
            }
        }
    }

    public void readHeadingMap(Node node) {
        int nMaxLevel = Misc.getPosInteger(Misc.getAttribute(node,"max-level"),0);
        headingMap.reset(nMaxLevel);
        NodeList nl = node.getChildNodes();
        int nLen = nl.getLength();
        for (int i=0; i<nLen; i++) {
            Node child = nl.item(i);
            if (child.getNodeType()==Node.ELEMENT_NODE) {
                String sChildName = child.getNodeName();
                if (sChildName.equals("heading-level-map")) {
                    int nWriterLevel = Misc.getPosInteger(Misc.getAttribute(child,"writer-level"),1);
                    String sName = Misc.getAttribute(child,"name");
                    int nLevel = Misc.getPosInteger(Misc.getAttribute(child,"level"),0);
                    headingMap.setLevelData(nWriterLevel,sName,nLevel);
                }
            }
        }
    }
    
    public void write(OutputStream os) {
        NewDOMDocument doc = new NewDOMDocument("config-file",".xml");
        Document dom = null;
        try {
            DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = builderFactory.newDocumentBuilder();
            DOMImplementation domImpl = builder.getDOMImplementation();
            dom = domImpl.createDocument("","config",null);
        }
        catch (Throwable t) {
            t.printStackTrace();
        }
        Element rootElement = dom.getDocumentElement();

        for (int i=0; i<OPTION_COUNT; i++) {
            Element optionNode = dom.createElement("option");
            optionNode.setAttribute("name",options[i].getName());
            optionNode.setAttribute("value",options[i].getString());
            rootElement.appendChild(optionNode);
        }

        // Write math symbol map
        Enumeration msEnum = mathSymbols.keys();
        while (msEnum.hasMoreElements()) {
            String sName = (String) msEnum.nextElement();
            String sLatex = (String) mathSymbols.get(sName);
            Element msNode = dom.createElement("math-symbol-map");
            msNode.setAttribute("name",sName);
	        msNode.setAttribute("latex",sLatex);
            rootElement.appendChild(msNode);
        }

        writeStyleMap(dom,par,"paragraph");
        writeStyleMap(dom,parBlock,"paragraph-block");
        writeStyleMap(dom,text,"text");
        writeStyleMap(dom,list,"list");
        writeStyleMap(dom,listItem,"listitem");

        Element hmNode = dom.createElement("heading-map");
        hmNode.setAttribute("max-level",Integer.toString(headingMap.getMaxLevel()));
        rootElement.appendChild(hmNode);
        for (int i=1; i<=headingMap.getMaxLevel(); i++) {
            Element hlmNode = dom.createElement("heading-level-map");
            hlmNode.setAttribute("writer-level",Integer.toString(i));
            hlmNode.setAttribute("name",headingMap.getName(i));
            hlmNode.setAttribute("level",Integer.toString(headingMap.getLevel(i)));
            hmNode.appendChild(hlmNode);
        }
		
        // TODO
        @SuppressWarnings("unused")
		String[] sInputStrings = stringReplace.getInputStrings();
        /*
        int nSize = sInputStrings.size();
        for (int i=0; i<nSize; i++) {
            String sInput = sInputStrings[i];
            ReplacementTrieNode node = stringReplace.get(sInput);
            Element srNode = dom.createElement("string-replace");
            srNode.setAttribute("input",sInput);
            srNode.setAttribute("latex-code",node.getLaTeXCode());
            srNode.setAttribute("fontenc",I18n.writeFontencs(node.getFontencs()));
            hmNode.appendChild(srNode);
        }
        */
		
        writeXStyleMap(dom,xpar,"paragraph");
        writeXStyleMap(dom,xtext,"text");
        writeXStyleMap(dom,xlist,"list");
        writeXStyleMap(dom,xframe,"frame");
        writeXStyleMap(dom,xframe,"attribute");

        writeContent(dom,customPreamble,"custom-preamble");

        doc.setContentDOM(dom);
        try {
            doc.write(os);
        } catch(IOException e) {
            System.err.println("Oops - problem writing the configuration");
            e.printStackTrace();
        }
    }
	
    // String replace
    public ReplacementTrie getStringReplace() { return stringReplace; }

    // Common options
    public boolean debug() { return ((BooleanOption) options[DEBUG]).getValue(); }

    // General options
    public String getDocumentclass() { return options[DOCUMENTCLASS].getString(); }
    public String getGlobalOptions() { return options[GLOBAL_OPTIONS].getString(); }
    public int getBackend() { return ((IntegerOption) options[BACKEND]).getValue(); }
    public int getInputencoding() { return ((IntegerOption) options[INPUTENCODING]).getValue(); }
    public boolean multilingual() { return ((BooleanOption) options[MULTILINGUAL]).getValue(); }
    public boolean greekMath() { return ((BooleanOption) options[GREEK_MATH]).getValue(); }
    public boolean noPreamble() { return ((BooleanOption) options[NO_PREAMBLE]).getValue(); }
    public boolean noIndex() { return ((BooleanOption) options[NO_INDEX]).getValue(); }
	
    // Package options
    public boolean useOoomath() { return ((BooleanOption) options[USE_OOOMATH]).getValue(); }
    public boolean usePifont() { return ((BooleanOption) options[USE_PIFONT]).getValue(); }
    public boolean useIfsym() { return ((BooleanOption) options[USE_IFSYM]).getValue(); }
    public boolean useWasysym() { return ((BooleanOption) options[USE_WASYSYM]).getValue(); }
    public boolean useBbding() { return ((BooleanOption) options[USE_BBDING]).getValue(); }
    public boolean useEurosym() { return ((BooleanOption) options[USE_EUROSYM]).getValue(); }
    public boolean useTipa() { return ((BooleanOption) options[USE_TIPA]).getValue(); }
    public boolean useColor() { return ((BooleanOption) options[USE_COLOR]).getValue(); }
    public boolean useColortbl() { return ((BooleanOption) options[USE_COLORTBL]).getValue(); }
    public boolean useGeometry() { return ((BooleanOption) options[USE_GEOMETRY]).getValue(); }
    public boolean useFancyhdr() { return ((BooleanOption) options[USE_FANCYHDR]).getValue(); }
    public boolean useHyperref() { return ((BooleanOption) options[USE_HYPERREF]).getValue(); }
    public boolean useCaption() { return ((BooleanOption) options[USE_CAPTION]).getValue(); }
    public boolean useLongtable() { return ((BooleanOption) options[USE_LONGTABLE]).getValue(); }
    public boolean useSupertabular() { return ((BooleanOption) options[USE_SUPERTABULAR]).getValue(); }
    public boolean useTabulary() { return ((BooleanOption) options[USE_TABULARY]).getValue(); }
    public boolean useEndnotes() { return ((BooleanOption) options[USE_ENDNOTES]).getValue(); }
    public boolean useUlem() { return ((BooleanOption) options[USE_ULEM]).getValue(); }
    public boolean useLastpage() { return ((BooleanOption) options[USE_LASTPAGE]).getValue(); }
    public boolean useTitleref() { return ((BooleanOption) options[USE_TITLEREF]).getValue(); }
    public boolean useOooref() { return ((BooleanOption) options[USE_OOOREF]).getValue(); }
    public boolean useBibtex() { return ((BooleanOption) options[USE_BIBTEX]).getValue(); }
    public String bibtexStyle() { return options[BIBTEX_STYLE].getString(); }
	
    // Formatting options
    public int formatting() { return ((IntegerOption) options[FORMATTING]).getValue(); }
    public int pageFormatting() { return ((IntegerOption) options[PAGE_FORMATTING]).getValue(); }
    public int otherStyles() { return ((IntegerOption) options[OTHER_STYLES]).getValue(); }
    public int imageContent() { return ((IntegerOption) options[IMAGE_CONTENT]).getValue(); }
    public int tableContent() { return ((IntegerOption) options[TABLE_CONTENT]).getValue(); }
    public boolean ignoreHardPageBreaks() { return ((BooleanOption) options[IGNORE_HARD_PAGE_BREAKS]).getValue(); }
    public boolean ignoreHardLineBreaks() { return ((BooleanOption) options[IGNORE_HARD_LINE_BREAKS]).getValue(); }
    public boolean ignoreEmptyParagraphs() { return ((BooleanOption) options[IGNORE_EMPTY_PARAGRAPHS]).getValue(); }
    public boolean ignoreDoubleSpaces() { return ((BooleanOption) options[IGNORE_DOUBLE_SPACES]).getValue(); }

    // Graphics options
    public boolean alignFrames() { return ((BooleanOption) options[ALIGN_FRAMES]).getValue(); }
    public boolean floatFigures() { return ((BooleanOption) options[FLOAT_FIGURES]).getValue(); }
    public boolean floatTables() { return ((BooleanOption) options[FLOAT_TABLES]).getValue(); }
    public String getFloatOptions() { return options[FLOAT_OPTIONS].getString(); }
    public String getFigureSequenceName() { return options[FIGURE_SEQUENCE_NAME].getString(); }
    public String getTableSequenceName() { return options[TABLE_SEQUENCE_NAME].getString(); }
    public String getImageOptions() { return options[IMAGE_OPTIONS].getString(); }
    public boolean removeGraphicsExtension() { return ((BooleanOption) options[REMOVE_GRAPHICS_EXTENSION]).getValue(); }
    public boolean keepImageSize() { return ((BooleanOption) options[KEEP_IMAGE_SIZE]).getValue(); }
	
    // Tables
    public int getSimpleTableLimit() { return ((IntegerOption) options[SIMPLE_TABLE_LIMIT]).getValue(); }
	
    // Notes
    public int notes() { return ((IntegerOption) options[NOTES]).getValue(); }
    public String getNotesCommand() { return options[NOTES].getString(); }
	
    // Tab stops
    public String getTabstop() { return options[TABSTOP].getString(); }
	
    // Files
    public int getWrapLinesAfter() { return ((IntegerOption) options[WRAP_LINES_AFTER]).getValue(); }
    public boolean splitLinkedSections() { return ((BooleanOption) options[SPLIT_LINKED_SECTIONS]).getValue(); }
    public boolean splitToplevelSections() { return ((BooleanOption) options[SPLIT_TOPLEVEL_SECTIONS]).getValue(); }

    // XHTML options
    public boolean xhtmlNoDoctype() { return ((BooleanOption) options[XHTML_NO_DOCTYPE]).getValue(); }
    public String xhtmlEncoding() { return options[XHTML_ENCODING].getString(); }
    public String xhtmlCustomStylesheet() { return options[XHTML_CUSTOM_STYLESHEET].getString(); }
    public int xhtmlFormatting() { return ((XhtmlFormatOption) options[XHTML_FORMATTING]).getValue(); }
    public int xhtmlFrameFormatting() { return ((XhtmlFormatOption) options[XHTML_FRAME_FORMATTING]).getValue(); }
    public int xhtmlSectionFormatting() { return ((XhtmlFormatOption) options[XHTML_SECTION_FORMATTING]).getValue(); }
    public int xhtmlTableFormatting() { return ((XhtmlFormatOption) options[XHTML_TABLE_FORMATTING]).getValue(); }
    public boolean xhtmlIgnoreTableDimensions() { return ((BooleanOption) options[XHTML_IGNORE_TABLE_DIMENSIONS]).getValue(); }
    public boolean xhtmlUseDublinCore() { return ((BooleanOption) options[XHTML_USE_DUBLIN_CORE]).getValue(); }
    public boolean xhtmlConvertToPx() { return ((BooleanOption) options[XHTML_CONVERT_TO_PX]).getValue(); }
    public String getXhtmlScaling() { return options[XHTML_SCALING].getString(); }
    public String getXhtmlColumnScaling() { return options[XHTML_COLUMN_SCALING].getString(); }
    public boolean xhtmlFloatObjects() { return ((BooleanOption) options[XHTML_FLOAT_OBJECTS]).getValue(); }
    public String getXhtmlTabstopStyle() { return options[XHTML_TABSTOP_STYLE].getString(); }
    public boolean xhtmlUseListHack() { return ((BooleanOption) options[XHTML_USE_LIST_HACK]).getValue(); }
    public int getXhtmlSplitLevel() { return ((IntegerOption) options[XHTML_SPLIT_LEVEL]).getValue(); }
    public boolean xhtmlCalcSplit() { return ((BooleanOption) options[XHTML_CALC_SPLIT]).getValue(); }
    public String getXhtmlUplink() { return options[XHTML_UPLINK].getString(); }
    public String getXhtmlDirectoryIcon() { return options[XHTML_DIRECTORY_ICON].getString(); }
    public String getXhtmlDocumentIcon() { return options[XHTML_DOCUMENT_ICON].getString(); }
	
    public Hashtable getMathSymbols() { return mathSymbols; }

    public StyleMap getParStyleMap() { return par; }
    public StyleMap getParBlockStyleMap() { return parBlock; }
    public StyleMap getTextStyleMap() { return text; }
    public StyleMap getListStyleMap() { return list; }
    public StyleMap getListItemStyleMap() { return listItem; }
    public HeadingMap getHeadingMap() { return headingMap; }
    public LinkedList getCustomPreamble() { return customPreamble; }
	
    public XhtmlStyleMap getXParStyleMap() { return xpar; }
    public XhtmlStyleMap getXTextStyleMap() { return xtext; }
    public XhtmlStyleMap getXFrameStyleMap() { return xframe; }
    public XhtmlStyleMap getXListStyleMap() { return xlist; }
    public XhtmlStyleMap getXAttrStyleMap() { return xattr; }
	
    private void writeStyleMap(Document dom, StyleMap sm, String sClass) {
        Enumeration smEnum = sm.getNames();
        while (smEnum.hasMoreElements()) {
            String sName = (String) smEnum.nextElement();
            Element smNode = dom.createElement("style-map");
            smNode.setAttribute("name",sName);
	        smNode.setAttribute("class",sClass);
            smNode.setAttribute("before",sm.getBefore(sName));
            smNode.setAttribute("after",sm.getAfter(sName));
            if (sm.getNext(sName)!=null) {
                smNode.setAttribute("next",sm.getNext(sName));
            }
            if (!sm.getLineBreak(sName)) {
                smNode.setAttribute("line-break","false");
            }
            if (sm.getVerbatim(sName)) {
                smNode.setAttribute("verbatim","true");
            }
            dom.getDocumentElement().appendChild(smNode);
        }
    }

    private void writeXStyleMap(Document dom, XhtmlStyleMap sm, String sClass) {
        Enumeration smEnum = sm.getNames();
        while (smEnum.hasMoreElements()) {
            String sName = (String) smEnum.nextElement();
            Element smNode = dom.createElement("xhtml-style-map");
            smNode.setAttribute("name",sName);
	        smNode.setAttribute("class",sClass);
            smNode.setAttribute("element",sm.getElement(sName));
            smNode.setAttribute("css",sm.getCss(sName));
            String sBlockElement = sm.getBlockElement(sName);
            if (sBlockElement!=null) { smNode.setAttribute("block-element",sm.getCss(sBlockElement)); }
            String sBlockCss = sm.getBlockCss(sName);
            if (sBlockCss!=null) { smNode.setAttribute("block-css",sm.getCss(sBlockCss)); }
            dom.getDocumentElement().appendChild(smNode);
        }
    }
    private void writeContent(Document dom, LinkedList list, String sElement) {
        Element node = dom.createElement(sElement);
        int nLen = list.size();
        for (int i=0; i<nLen; i++) {
            node.appendChild( dom.createTextNode( (String) list.get(i) ) );
        }
        dom.getDocumentElement().appendChild(node);
    }

}

