/* OdLib_Io - Copyright (c) 2020-2021 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package net.mapeadores.opendocument.io;

import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import net.mapeadores.opendocument.elements.Constants;
import net.mapeadores.opendocument.elements.ElementMaps;
import net.mapeadores.opendocument.elements.ListLevelElement;
import net.mapeadores.opendocument.elements.ListStyleElement;
import net.mapeadores.opendocument.elements.OdAttribute;
import net.mapeadores.opendocument.elements.OdAttributeMap;
import net.mapeadores.opendocument.elements.OdElement;
import net.mapeadores.opendocument.elements.PropertiesElementUtils;
import net.mapeadores.opendocument.elements.StyleElement;
import net.mapeadores.util.xml.XMLPart;
import net.mapeadores.util.xml.XMLWriter;


/**
 * Adaptation de net.mapeadores.opendocument.css.output.XMLWriter à une classe
 * implémentant XMLPart
 *
 * @author Vincent Calame
 */
public class StylesXMLPart extends XMLPart {

    public StylesXMLPart(XMLWriter xmlWriter) {
        super(xmlWriter);
    }

    public void openStyleDocument() throws IOException {
        startOpenTag("office:document-styles");
        appendNameSpaceAttributes();
        addAttribute("office:version", "1.0");
        endOpenTag();
    }

    public void appendNameSpaceAttributes() throws IOException {
        addAttribute("xmlns:office", "urn:oasis:names:tc:opendocument:xmlns:office:1.0");
        addAttribute("xmlns:style", "urn:oasis:names:tc:opendocument:xmlns:style:1.0");
        addAttribute("xmlns:text", "urn:oasis:names:tc:opendocument:xmlns:text:1.0");
        addAttribute("xmlns:table", "urn:oasis:names:tc:opendocument:xmlns:table:1.0");
        addAttribute("xmlns:draw", "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0");
        addAttribute("xmlns:fo", "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0");
        addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
        /*addAttribute("xmlns:dc","http://purl.org/dc/elements/1.1");*/
        addAttribute("xmlns:meta", "urn:oasis:names:tc:opendocument:xmlns:meta:1.0");
        addAttribute("xmlns:number", "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0");
        addAttribute("xmlns:svg", "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0");
        addAttribute("xmlns:chart", "urn:oasis:names:tc:opendocument:xmlns:chart:1.0");
        addAttribute("xmlns:dr3d", "urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0");
        addAttribute("xmlns:math", "http://www.w3.org/1998/Math/MathML");
        addAttribute("xmlns:form", "urn:oasis:names:tc:opendocument:xmlns:form:1.0");
        addAttribute("xmlns:script", "urn:oasis:names:tc:opendocument:xmlns:script:1.0");
        addAttribute("xmlns:ooo", "http://openoffice.org/2004/office");
        addAttribute("xmlns:ooow", "http://openoffice.org/2004/writer");
        addAttribute("xmlns:oooc", "http://openoffice.org/2004/calc");
        addAttribute("xmlns:dom", "http://www.w3.org/2001/xml-events");
    }

    public void closeStyleDocument() throws IOException {
        addEmptyElement("office:master-styles");
        closeTag("office:document-styles");
    }

    public void openStyles() throws IOException {
        openTag("office:styles");
    }

    public void closeStyles() throws IOException {
        closeTag("office:styles");
    }

    public void addStyleElement(StyleElement styleElement) throws IOException {
        startOpenTag("style:style");
        addAttribute("style:name", styleElement.getStyleName());
        addAttribute("style:family", styleElement.getStyleFamily());
        Map<OdAttribute, String> map = styleElement.getAttributeMap(Constants.STYLE_ATTRIBUTES);
        if (map.size() > 0) {
            addAttributeMap(map);
        }
        endOpenTag();
        addPropertiesElementList(styleElement);
        closeTag("style:style");
    }

    public void addListStyleElement(ListStyleElement listStyleElement) throws IOException {
        startOpenTag("text:list-style");
        addAttribute("style:name", listStyleElement.getStyleName());
        Map<OdAttribute, String> map = listStyleElement.getAttributeMap(Constants.LISTSTYLE_ATTRIBUTES);
        if (map.size() > 0) {
            addAttributeMap(map);
        }
        endOpenTag();
        for (int i = 1; i <= 10; i++) {
            ListLevelElement listLevelElement = listStyleElement.getListLevelElement(i);
            if (listLevelElement != null) {
                addListLevelElement(listLevelElement);
            }
        }
        closeTag("text:list-style");
    }

    public void addListLevelElement(ListLevelElement listLevelElement) throws IOException {
        String tagName = "text:list-level-style-";
        switch (listLevelElement.getType()) {
            case ListLevelElement.BULLET_TYPE:
                tagName = tagName + "bullet";
                break;
            case ListLevelElement.NUMBER_TYPE:
                tagName = tagName + "number";
                break;
        }
        startOpenTag(tagName);
        addAttribute("text:level", String.valueOf(listLevelElement.getLevel()));
        Map<OdAttribute, String> map = listLevelElement.getAttributeMap(Constants.LISTLEVEL_ATTRIBUTES);
        if (map.size() > 0) {
            addAttributeMap(map);
        }
        endOpenTag();
        addPropertiesElementList(listLevelElement);
        closeTag(tagName);
    }

    public void addPropertiesElementList(OdElement abstractElement) throws IOException {
        for (int i = 0; i < PropertiesElementUtils.count(); i++) {
            int type = PropertiesElementUtils.getAttributesType(i);
            String tagName = PropertiesElementUtils.getTagName(i);
            Map<OdAttribute, String> map = abstractElement.getAttributeMap(type);
            if (map.isEmpty()) {
                continue;
            }
            startOpenTag(tagName);
            addAttributeMap(map);
            closeEmptyTag();
        }
    }

    public void addAttributeMap(Map<OdAttribute, String> map) throws IOException {
        for (Map.Entry<OdAttribute, String> mapEntry : map.entrySet()) {
            OdAttribute propertiesAttribute = mapEntry.getKey();
            String value = mapEntry.getValue();
            addAttribute(propertiesAttribute, value);
        }
    }

    public void addAttribute(OdAttribute propertiesAttribute, String value) throws IOException {
        short nameSpace = propertiesAttribute.getNameSpace();
        String name = propertiesAttribute.getName();
        addAttribute(OdAttributeMap.nameSpaceToString(nameSpace) + ":" + name, value);
    }

    public void insertAutomaticStyles(ElementMaps elementMaps, boolean isSpreadSheet) throws IOException {
        addStyleElements(elementMaps, StyleElement.TABLE_FAMILY);
        addStyleElements(elementMaps, StyleElement.TABLECOLUMN_FAMILY);
        addStyleElements(elementMaps, StyleElement.TABLEROW_FAMILY);
        if (!isSpreadSheet) {
            addStyleElements(elementMaps, StyleElement.TABLECELL_FAMILY);
        }
    }

    public void insertStyles(ElementMaps elementMaps, boolean isSpreadSheet) throws IOException {
        addStyleElements(elementMaps.getHMap().values());
        addStyleElements(elementMaps, StyleElement.PARAGRAPH_FAMILY);
        addStyleElements(elementMaps, StyleElement.TEXT_FAMILY);
        addStyleElements(elementMaps, StyleElement.GRAPHIC_FAMILY);
        if (isSpreadSheet) {
            addStyleElements(elementMaps, StyleElement.TABLECELL_FAMILY);
        }
        Map<String, ListStyleElement> map = elementMaps.getListStyleMap();
        for (ListStyleElement listStyleElement : map.values()) {
            addListStyleElement(listStyleElement);
        }
    }

    private void addStyleElements(ElementMaps elementMaps, String family) throws IOException {
        Map<String, StyleElement> map = elementMaps.getFamilyMap(family);
        if ((map == null) || (map.isEmpty())) {
            return;
        }
        StylesXMLPart.this.addStyleElements(map.values());
    }

    private void addStyleElements(Collection<StyleElement> collection) throws IOException {
        for (StyleElement styleElement : collection) {
            addStyleElement(styleElement);
        }
    }

}
