/* OdLib_Elements - Copyright (c) 2007-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.elements;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;


/**
 *
 * @author Vincent Calame
 */
public class ElementMaps {

    private final Map<String, Map<String, StyleElement>> mapOfMaps = new HashMap<String, Map<String, StyleElement>>();
    private final Map<Integer, StyleElement> hMap = new TreeMap<Integer, StyleElement>();
    private final Map<String, ListStyleElement> listStyleMap = new HashMap<String, ListStyleElement>();

    public ElementMaps() {
    }

    public Map<Integer, StyleElement> getHMap() {
        return hMap;
    }

    public Map<String, StyleElement> getFamilyMap(String family) {
        return mapOfMaps.get(family);
    }

    public OdElement getElement(String elementName, String styleName, boolean createIfMissing) {
        int level = OdElementUtils.getHLevel(elementName);
        if (level == 0) {
            return getFromStyleFamily(StyleElement.PARAGRAPH_FAMILY, "Heading", createIfMissing);
        } else if (level > 0) {
            return getHStyle(level, createIfMissing);
        }
        String styleFamily = StyleElement.getMatchingStyleFamily(elementName);
        if (styleFamily == null) {
            return null;
        }
        return getFromStyleFamily(styleFamily, styleName, createIfMissing);
    }

    public OdElement getElement(String elementName, String styleName, String attributeName, String attributeValue, boolean createIfMissing) {
        if ((elementName.equals("ul")) || (elementName.equals("ol"))) {
            if (attributeName.equals("level")) {
                try {
                    int level = Integer.parseInt(attributeValue);
                    if ((level < 1) || (level > 10)) {
                        return null;
                    }
                    short type = ListLevelElement.BULLET_TYPE;
                    if (elementName.equals("ol")) {
                        type = ListLevelElement.NUMBER_TYPE;
                    }
                    ListStyleElement listStyleElement = getListStyleElement(styleName, type, createIfMissing);
                    if (listStyleElement == null) {
                        return null;
                    }
                    return listStyleElement.getOrCreateListLevelElement(level);
                } catch (NumberFormatException nfe) {
                    return null;
                }
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    public Map<String, ListStyleElement> getListStyleMap() {
        return listStyleMap;
    }

    public void append(ElementMaps importElementMaps) {
        importH(importElementMaps);
        for (Map.Entry<String, Map<String, StyleElement>> entry : importElementMaps.mapOfMaps.entrySet()) {
            String family = (String) entry.getKey();
            Map<String, StyleElement> map = entry.getValue();
            if (!mapOfMaps.containsKey(family)) {
                Map<String, StyleElement> newMap = new HashMap<String, StyleElement>(map);
                mapOfMaps.put(family, newMap);
            } else {
                append(map, (Map<String, StyleElement>) mapOfMaps.get(family));
            }
        }
        importListStyle(importElementMaps);
    }

    private ListStyleElement getListStyleElement(String styleName, short listLevelType, boolean createIfMissing) {
        ListStyleElement listStyleElement = (ListStyleElement) listStyleMap.get(styleName);
        if ((listStyleElement == null) && (createIfMissing)) {
            listStyleElement = new ListStyleElement(styleName, listLevelType);
            listStyleMap.put(styleName, listStyleElement);
        }
        return listStyleElement;
    }

    private StyleElement getFromStyleFamily(String styleFamily, String styleName, boolean createIfMissing) {
        if (styleName == null) {
            return null;
        }
        Map<String, StyleElement> map = mapOfMaps.get(styleFamily);
        if (map == null) {
            if (!createIfMissing) {
                return null;
            }
            map = new LinkedHashMap<String, StyleElement>();
            mapOfMaps.put(styleFamily, map);
        }
        StyleElement styleElement = (StyleElement) map.get(styleName);
        if ((styleElement == null) && (createIfMissing)) {
            styleElement = StyleElement.newInstance(styleFamily, styleName);
            map.put(styleName, styleElement);
        }
        return styleElement;
    }

    private StyleElement getHStyle(int level, boolean createIfMissing) {
        StyleElement hStyle = (StyleElement) hMap.get(level);
        if ((hStyle == null) && (createIfMissing)) {
            hStyle = StyleElement.newHInstance(level);
            hMap.put(level, hStyle);
        }
        return hStyle;
    }

    private void importListStyle(ElementMaps importElementMaps) {
        for (Map.Entry<String, ListStyleElement> entry : importElementMaps.listStyleMap.entrySet()) {
            String styleName = entry.getKey();
            ListStyleElement importStyle = entry.getValue();
            ListStyleElement current = listStyleMap.get(styleName);
            if (current == null) {
                listStyleMap.put(styleName, importStyle);
            } else {
                current.importAttributes(importStyle);
            }
        }
    }

    private void importH(ElementMaps importElementMaps) {
        for (Map.Entry<Integer, StyleElement> entry : importElementMaps.hMap.entrySet()) {
            Integer level = entry.getKey();
            StyleElement importStyle = entry.getValue();
            StyleElement hStyle = hMap.get(level);
            if (hStyle == null) {
                hMap.put(level, importStyle);
            } else {
                hStyle.importAttributes(importStyle);
            }
        }
    }

    private void append(Map<String, StyleElement> importMap, Map<String, StyleElement> destinationMap) {
        for (Map.Entry<String, StyleElement> entry : importMap.entrySet()) {
            String styleName = entry.getKey();
            StyleElement importStyle = entry.getValue();
            StyleElement destinationStyle = destinationMap.get(styleName);
            if (destinationStyle == null) {
                destinationMap.put(styleName, importStyle);
            } else {
                destinationStyle.importAttributes(importStyle);
            }
        }
    }

}
