/* BdfServer_Html - Copyright (c) 2010-2023 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 fr.exemole.bdfserver.html.consumers;

import fr.exemole.bdfserver.api.BdfServer;
import fr.exemole.bdfserver.api.subsettree.GroupNode;
import fr.exemole.bdfserver.api.subsettree.SubsetNode;
import fr.exemole.bdfserver.api.subsettree.SubsetTree;
import fr.exemole.bdfserver.tools.subsettree.TreeUtils;
import java.text.ParseException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import net.fichotheque.Fichotheque;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.utils.FichothequeUtils;
import net.mapeadores.util.html.HA;
import net.mapeadores.util.html.HtmlAttributes;
import net.mapeadores.util.html.HtmlPrinter;
import net.mapeadores.util.localisation.Lang;


/**
 *
 * @author Vincent Calame
 */
public class SubsetTreeOptions implements Consumer<HtmlPrinter> {

    private final static Set<SubsetKey> EMPTY_SET = Collections.emptySet();
    private final SubsetTree subsetTree;
    private final BdfServer bdfServer;
    private final Fichotheque fichotheque;
    private final Lang workingLang;
    private final Set<SubsetKey> disabledSet = new HashSet<SubsetKey>();
    private boolean onlyNames = false;
    private boolean displayKeys = false;
    private boolean checkboxMode = false;
    private boolean isMultiple = false;
    private Set<SubsetKey> selectedSet = EMPTY_SET;
    private HtmlAttributes checkAttr;
    private HtmlAttributes labelAttributes;
    private HtmlAttributes groupTitleAttributes;
    private HtmlAttributes groupDivAttributes;


    private SubsetTreeOptions(SubsetTree subsetTree, BdfServer bdfServer, Lang workingLang) {
        this.subsetTree = subsetTree;
        this.bdfServer = bdfServer;
        this.fichotheque = bdfServer.getFichotheque();
        this.workingLang = workingLang;
    }

    public SubsetTreeOptions onlyNames(boolean onlyNames) {
        this.onlyNames = onlyNames;
        return this;
    }

    public SubsetTreeOptions withKeys(boolean withKeys) {
        this.displayKeys = withKeys;
        return this;
    }

    public SubsetTreeOptions selectedSubsetKey(SubsetKey subsetKey) {
        if (subsetKey == null) {
            selectedSet = EMPTY_SET;
        } else {
            selectedSet = Collections.singleton(subsetKey);
        }
        return this;
    }

    public SubsetTreeOptions selectedSubsetKey(short subsetCategory, Map<String, String> valueMap) {
        String value = valueMap.get(SubsetKey.categoryToString(subsetCategory));
        if (value == null) {
            selectedSet = EMPTY_SET;
        } else {
            try {
                SubsetKey subsetKey = SubsetKey.parse(subsetCategory, value);
                selectedSet = Collections.singleton(subsetKey);
            } catch (ParseException pe) {
                selectedSet = EMPTY_SET;
            }
        }
        return this;
    }

    public SubsetTreeOptions disabledSet(Set<SubsetKey> disabledSet) {
        if (disabledSet == null) {
            this.disabledSet.clear();
        } else {
            this.disabledSet.addAll(disabledSet);
        }
        return this;
    }

    public SubsetTreeOptions disableSatellites() {
        for (Corpus corpus : fichotheque.getCorpusList()) {
            if (corpus.getMasterSubset() != null) {
                this.disabledSet.add(corpus.getSubsetKey());
            }
        }
        return this;
    }

    public SubsetTreeOptions selectedSet(Set<SubsetKey> selectedSet) {
        if (selectedSet == null) {
            this.selectedSet = EMPTY_SET;
        } else {
            this.selectedSet = selectedSet;
        }
        return this;
    }

    public SubsetTreeOptions multiple(boolean isMultiple) {
        this.isMultiple = isMultiple;
        return this;
    }

    public SubsetTreeOptions checkboxMode(HtmlAttributes checkAttr, HtmlAttributes labelAttributes, HtmlAttributes groupTitleAttributes, HtmlAttributes groupDivAttributes) {
        checkboxMode = true;
        this.checkAttr = checkAttr;
        this.labelAttributes = labelAttributes;
        this.groupTitleAttributes = groupTitleAttributes;
        this.groupDivAttributes = groupDivAttributes;
        return this;
    }

    @Override
    public void accept(HtmlPrinter hp) {
        if (checkboxMode) {
            printCheckBoxList(hp);
        } else {
            printOptions(hp);
        }
    }

    private boolean printOptions(HtmlPrinter hp) {
        Set<SubsetKey> selection = selectedSet;
        if (!isMultiple) {
            if (selectedSet.isEmpty()) {
                SubsetKey firstSubsetKey = TreeUtils.getFirstSubsetKey(subsetTree);
                if (firstSubsetKey != null) {
                    selection = Collections.singleton(firstSubsetKey);
                }
            }
        }
        for (SubsetTree.Node node : subsetTree.getNodeList()) {
            if (node instanceof GroupNode) {
                hp
                        .__(printGroupNode(hp, (GroupNode) node, 0, selection));
            } else if (node instanceof SubsetNode) {
                hp
                        .__(printSubsetNodeOption(hp, (SubsetNode) node, 0, selection));
            }
        }
        return true;
    }

    private boolean printCheckBoxList(HtmlPrinter hp) {
        for (SubsetTree.Node node : subsetTree.getNodeList()) {
            if (node instanceof GroupNode) {
                hp
                        .__(printGroupNodeDiv(hp, (GroupNode) node, checkAttr, labelAttributes, groupTitleAttributes, groupDivAttributes));
            } else if (node instanceof SubsetNode) {
                hp
                        .__(printSubsetNodeCheckbox(hp, (SubsetNode) node, checkAttr, labelAttributes));
            }

        }
        return true;
    }

    public static SubsetTreeOptions init(SubsetTree subsetTree, BdfServer bdfServer, Lang lang) {
        return (new SubsetTreeOptions(subsetTree, bdfServer, lang));
    }

    private boolean printGroupNode(HtmlPrinter hp, GroupNode groupNode, int level, Set<SubsetKey> selection) {
        StringBuilder labelBuf = new StringBuilder();
        for (int i = 0; i < level; i++) {
            labelBuf.append("\u00A0 \u00A0 ");

        }
        labelBuf.append(TreeUtils.getTitle(bdfServer, groupNode, workingLang));
        hp
                .OPTGROUP(HA.label(labelBuf.toString()))
                ._OPTGROUP();
        for (SubsetTree.Node subnode : groupNode.getSubnodeList()) {
            if (subnode instanceof GroupNode) {
                hp
                        .__(printGroupNode(hp, (GroupNode) subnode, level + 1, selection));
            } else if (subnode instanceof SubsetNode) {
                hp
                        .__(printSubsetNodeOption(hp, (SubsetNode) subnode, level + 1, selection));
            }
        }
        return true;
    }

    private boolean printSubsetNodeOption(HtmlPrinter hp, SubsetNode subsetNode, int level, Set<SubsetKey> selection) {
        SubsetKey subsetKey = subsetNode.getSubsetKey();
        boolean selected = selection.contains(subsetKey);
        String value = (onlyNames) ? subsetKey.getSubsetName() : subsetKey.getKeyString();
        hp
                .OPTION(HA.value(value).disabled(disabledSet.contains(subsetKey)).selected(selected))
                .__(indent(hp, level))
                .__escape(FichothequeUtils.getTitle(fichotheque, subsetKey, workingLang, displayKeys))
                ._OPTION();
        return true;
    }

    private boolean printGroupNodeDiv(HtmlPrinter hp, GroupNode groupNode, HtmlAttributes checkAttr, HtmlAttributes labelAttributes, HtmlAttributes groupTitleAttributes, HtmlAttributes groupDivAttributes) {
        hp
                .SPAN(groupTitleAttributes)
                .__escape(TreeUtils.getTitle(bdfServer, groupNode, workingLang))
                ._SPAN();
        hp
                .DIV(groupDivAttributes);
        for (SubsetTree.Node node : groupNode.getSubnodeList()) {
            if (node instanceof GroupNode) {
                hp
                        .__(printGroupNodeDiv(hp, (GroupNode) node, checkAttr, labelAttributes, groupTitleAttributes, groupDivAttributes));
            } else if (node instanceof SubsetNode) {
                hp
                        .__(printSubsetNodeCheckbox(hp, (SubsetNode) node, checkAttr, labelAttributes));
            }

        }
        hp
                ._DIV();
        return true;
    }

    private boolean printSubsetNodeCheckbox(HtmlPrinter hp, SubsetNode subsetNode, HtmlAttributes checkAttr, HtmlAttributes labelAttributes) {
        SubsetKey subsetKey = subsetNode.getSubsetKey();
        String value = (onlyNames) ? subsetKey.getSubsetName() : subsetKey.getKeyString();
        hp
                .LABEL(labelAttributes)
                .INPUT_checkbox(checkAttr.value(value).checked(selectedSet.contains(subsetKey)).disabled(disabledSet.contains(subsetKey)))
                .__escape(FichothequeUtils.getTitle(fichotheque, subsetKey, workingLang, displayKeys))
                ._LABEL();
        return true;
    }

    private boolean indent(HtmlPrinter hp, int level) {
        if (level > 0) {
            for (int i = 0; i < level; i++) {
                hp
                        .__doublespace()
                        .__doublespace();
            }
        }
        return true;
    }

}
