/* FichothequeLib_Tools - Copyright (c) 2021-2024 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.fichotheque.tools.selection;

import java.text.ParseException;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.fichotheque.Fichotheque;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.selection.CroisementCondition;
import net.fichotheque.selection.DocumentCondition;
import net.fichotheque.selection.FicheCondition;
import net.fichotheque.selection.FieldContentCondition;
import net.fichotheque.selection.IllustrationCondition;
import net.fichotheque.selection.MotcleCondition;
import net.fichotheque.selection.MotcleQuery;
import net.fichotheque.selection.PeriodCondition;
import net.fichotheque.selection.RangeCondition;
import net.fichotheque.selection.UserCondition;
import net.fichotheque.sphere.LoginKey;
import net.fichotheque.utils.FichothequeUtils;
import net.fichotheque.utils.SelectionUtils;
import net.fichotheque.utils.selection.RangeConditionBuilder;
import net.mapeadores.util.conditions.ConditionsConstants;
import net.mapeadores.util.conditions.ConditionsUtils;
import net.mapeadores.util.conditions.ConditionsXMLStorage;
import net.mapeadores.util.conditions.TextCondition;
import net.mapeadores.util.exceptions.ShouldNotOccurException;
import net.mapeadores.util.primitives.FuzzyDate;
import net.mapeadores.util.primitives.RangeUtils;
import net.mapeadores.util.primitives.Ranges;
import net.mapeadores.util.xml.DOMUtils;
import net.mapeadores.util.xml.XMLUtils;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;


/**
 *
 * @author Vincent Calame
 */
public final class SelectionDOMUtils {

    private final static Pattern GLOBALID_REGEX = Pattern.compile("^([a-z]+)/([0-9]+)$");
    private final static Pattern BRACKETS_REGEX = Pattern.compile("^([a-zA-Z][a-zA-Z0-9]*)\\[([a-z]+)\\]$");
    private final static Pattern UNDERSCORE_REGEX = Pattern.compile("^([a-z]+)_([a-zA-Z][a-zA-Z0-9]*)$");

    private SelectionDOMUtils() {

    }

    public static Consumer<Element> getQueryElementConsumer(Fichotheque fichotheque, FichothequeQueriesBuilder fichothequeQueriesBuilder) {
        return new QueryConsumer(fichotheque, fichothequeQueriesBuilder);
    }

    public static boolean readSelectionOptions(SelectionOptionsBuilder selectionOptionsBuilder, Fichotheque fichotheque, Element element) {
        switch (element.getTagName()) {
            case "selectiondef-name":
                selectionOptionsBuilder.setSelectionDefName(DOMUtils.readSimpleElement(element));
                return true;
            case "fiche-query":
            case "fiche-select":
                FicheCondition.Entry ficheEntry = getFicheConditionEntry(fichotheque, element);
                selectionOptionsBuilder.getCustomFichothequeQueriesBuilder().addFicheQuery(ficheEntry.getFicheQuery());
                return true;
            case "motcle-query":
            case "motcle-select":
                MotcleCondition.Entry motcleEntry = getMotcleConditionEntry(fichotheque, element);
                selectionOptionsBuilder.getCustomFichothequeQueriesBuilder().addMotcleQuery(motcleEntry.getMotcleQuery());
                return true;
            default:
                return false;
        }
    }

    public static boolean readQueryElement(FichothequeQueriesBuilder fichothequeQueriesBuilder, Fichotheque fichotheque, Element element) {
        switch (element.getTagName()) {
            case "fiche-query":
            case "fiche-select":
                FicheCondition.Entry ficheEntry = getFicheConditionEntry(fichotheque, element);
                fichothequeQueriesBuilder.addFicheQuery(ficheEntry.getFicheQuery());
                return true;
            case "motcle-query":
            case "motcle-select":
                MotcleCondition.Entry motcleEntry = getMotcleConditionEntry(fichotheque, element);
                fichothequeQueriesBuilder.addMotcleQuery(motcleEntry.getMotcleQuery());
                return true;
            default:
                return false;
        }
    }

    public static FicheCondition.Entry getFicheConditionEntry(Fichotheque fichotheque, Element element) {
        if (isEmpty(element)) {
            return SelectionUtils.EMPTY_FICHECONDITIONENTRY;
        }
        FicheQueryBuilder ficheQueryBuilder = new FicheQueryBuilder();
        StringBuilder corpusNameBuffer = new StringBuilder();
        CroisementConditionBuilder croisementConditionBuilder = new CroisementConditionBuilder();
        BooleanBuilder satelliteBuilder = new BooleanBuilder();
        FicheQueryConsumer ficheQueryConsumer = new FicheQueryConsumer(fichotheque, ficheQueryBuilder, croisementConditionBuilder, corpusNameBuffer, satelliteBuilder);
        DOMUtils.readChildren(element, ficheQueryConsumer);
        if (corpusNameBuffer.length() > 0) {
            Set<SubsetKey> corpusKeySet = FichothequeUtils.toSubsetKeySet(SubsetKey.CATEGORY_CORPUS, corpusNameBuffer.toString());
            if (!corpusKeySet.isEmpty()) {
                ficheQueryBuilder.addCorpus(corpusKeySet);
            }
        }
        CroisementCondition croisementCondition;
        if (!croisementConditionBuilder.isEmpty()) {
            croisementCondition = croisementConditionBuilder.toCroisementCondition();
        } else {
            croisementCondition = null;
        }
        return SelectionUtils.toFicheConditionEntry(ficheQueryBuilder.toFicheQuery(), croisementCondition, satelliteBuilder.getValue());
    }

    private static Set<FieldKey> getFieldKeySet(Element element_xml) {
        Set<FieldKey> fieldKeySet = new LinkedHashSet<FieldKey>();
        NodeList liste = element_xml.getChildNodes();
        for (int i = 0; i < liste.getLength(); i++) {
            if (liste.item(i).getNodeType() == Node.ELEMENT_NODE) {
                Element element = (Element) liste.item(i);
                String tagname = element.getTagName();
                if (tagname.equals("field")) {
                    try {
                        FieldKey fieldKey = FieldKey.parse(XMLUtils.getData(element));
                        fieldKeySet.add(fieldKey);
                    } catch (ParseException pe) {
                    }
                }
            }
        }
        return fieldKeySet;
    }

    private static PeriodCondition getPeriodCondition(Element element) {
        String startDateString = element.getAttribute("start");
        if (startDateString.length() == 0) {
            return null;
        }
        PeriodConditionBuilder builder = new PeriodConditionBuilder();
        if (startDateString.equals(PeriodCondition.ANY_CHAR)) {
            builder.setAnyStartDate();
        } else {
            try {
                FuzzyDate startDate = FuzzyDate.parse(startDateString);
                builder.setStartDate(startDate);
            } catch (ParseException pe) {
                return null;
            }
        }
        String endDateString = element.getAttribute("end");
        if (endDateString.length() > 0) {
            if (endDateString.equals(PeriodCondition.ANY_CHAR)) {
                builder.setAnyEndDate();
            } else {
                try {
                    FuzzyDate endDate = FuzzyDate.parse(endDateString);
                    builder.setEndDate(endDate);
                } catch (ParseException pe) {
                }
            }
        }
        DOMUtils.readChildren(element, new FieldConsumer(builder));
        return builder.toPeriodCondition();
    }

    public static IllustrationCondition.Entry getIllustrationConditionEntry(Element element_xml) {
        if (isEmpty(element_xml)) {
            return SelectionUtils.EMPTY_ILLUSTRATIONCONDITIONENTRY;
        }
        IllustrationQueryBuilder illustrationQueryBuilder = new IllustrationQueryBuilder();
        CroisementConditionBuilder croisementConditionBuilder = new CroisementConditionBuilder();
        StringBuilder albumNameBuffer = new StringBuilder();
        NodeList nodeList = element_xml.getChildNodes();
        for (int i = 0; i < nodeList.getLength(); i++) {
            if (nodeList.item(i).getNodeType() == Node.ELEMENT_NODE) {
                Element element = (Element) nodeList.item(i);
                String tagname = element.getTagName();
                if (tagname.equals("album")) {
                    String albumName = XMLUtils.getData(element);
                    if (albumName.length() > 0) {
                        if (albumNameBuffer.length() > 0) {
                            albumNameBuffer.append(';');
                        }
                        albumNameBuffer.append(albumName);
                    }
                } else if (tagname.equals("albumdim")) {
                    String albumDimName = XMLUtils.getData(element);
                    if (albumDimName.length() > 0) {
                        illustrationQueryBuilder.addAlbumDimName(albumDimName);
                    }
                } else if (testConditionCroisement(element, croisementConditionBuilder)) {

                }
            }
        }
        if (albumNameBuffer.length() > 0) {
            illustrationQueryBuilder.addAlbum(FichothequeUtils.toSubsetKeySet(SubsetKey.CATEGORY_ALBUM, albumNameBuffer.toString()));
        }
        CroisementCondition croisementCondition;
        if (!croisementConditionBuilder.isEmpty()) {
            croisementCondition = croisementConditionBuilder.toCroisementCondition();
        } else {
            croisementCondition = null;
        }
        return SelectionUtils.toIllustrationConditionEntry(illustrationQueryBuilder.toIllustrationQuery(), croisementCondition);
    }

    public static MotcleCondition.Entry getMotcleConditionEntry(Fichotheque fichotheque, Element element) {
        if (isEmpty(element)) {
            return SelectionUtils.EMPTY_MOTCLECONDITIONENTRY;
        }
        MotcleQueryBuilder motcleQueryBuilder = new MotcleQueryBuilder();
        CroisementConditionBuilder croisementConditionBuilder = new CroisementConditionBuilder();
        StringBuilder thesaurusNameBuffer = new StringBuilder();
        BooleanBuilder masterBuilder = new BooleanBuilder();
        MotcleQueryConsumer handler = new MotcleQueryConsumer(fichotheque, motcleQueryBuilder, croisementConditionBuilder, thesaurusNameBuffer, masterBuilder);
        DOMUtils.readChildren(element, handler);
        CroisementCondition croisementCondition;
        if (!croisementConditionBuilder.isEmpty()) {
            croisementCondition = croisementConditionBuilder.toCroisementCondition();
        } else {
            croisementCondition = null;
        }
        if (thesaurusNameBuffer.length() > 0) {
            Set<SubsetKey> subsetKeySet = FichothequeUtils.toSubsetKeySet(SubsetKey.CATEGORY_THESAURUS, thesaurusNameBuffer.toString());
            if (!subsetKeySet.isEmpty()) {
                motcleQueryBuilder.addThesaurus(subsetKeySet);
            }
        }
        return SelectionUtils.toMotcleConditionEntry(motcleQueryBuilder.toMotcleQuery(), croisementCondition, masterBuilder.getValue());
    }

    public static DocumentCondition.Entry getDocumentConditionEntry(Element element_xml) {
        if (isEmpty(element_xml)) {
            return SelectionUtils.EMPTY_DOCUMENTCONDITIONENTRY;
        }
        DocumentQueryBuilder documentQueryBuilder = new DocumentQueryBuilder();
        CroisementConditionBuilder croisementConditionBuilder = new CroisementConditionBuilder();
        StringBuilder addendaNameBuffer = new StringBuilder();
        NodeList nodeList = element_xml.getChildNodes();
        for (int i = 0; i < nodeList.getLength(); i++) {
            if (nodeList.item(i).getNodeType() == Node.ELEMENT_NODE) {
                Element element = (Element) nodeList.item(i);
                String tagname = element.getTagName();
                if (tagname.equals("addenda")) {
                    String addendaName = XMLUtils.getData(element);
                    if (addendaName.length() > 0) {
                        if (addendaNameBuffer.length() > 0) {
                            addendaNameBuffer.append(';');
                        }
                        addendaNameBuffer.append(addendaName);
                    }
                } else if (testConditionCroisement(element, croisementConditionBuilder)) {
                }
            }
        }
        if (addendaNameBuffer.length() > 0) {
            documentQueryBuilder.addAddenda(FichothequeUtils.toSubsetKeySet(SubsetKey.CATEGORY_ADDENDA, addendaNameBuffer.toString()));
        }
        CroisementCondition croisementCondition;
        if (!croisementConditionBuilder.isEmpty()) {
            croisementCondition = croisementConditionBuilder.toCroisementCondition();
        } else {
            croisementCondition = null;
        }
        return SelectionUtils.toDocumentConditionEntry(documentQueryBuilder.toDocumentQuery(), croisementCondition);
    }

    private static boolean testConditionCroisement(Element element, CroisementConditionBuilder builder) {
        String tagName = element.getTagName();
        switch (tagName) {
            case "weight":
            case "lien-poids":
            case "poids":
                RangeCondition weightRangeCondition = readRangeCondition(element);
                if (weightRangeCondition != null) {
                    builder.setWeightRangeCondition(weightRangeCondition);
                }
                return true;
            case "mode":
            case "lien-mode":
                builder.addLienMode(XMLUtils.getData(element));
                return true;
            default:
                return false;
        }
    }

    private static RangeCondition readRangeCondition(Element element) {
        String range = XMLUtils.getData(element);
        if (range.length() > 0) {
            Ranges ranges = RangeUtils.positiveRangeParse(range);
            if (ranges != null) {
                boolean exclude = testExclude(element);
                return RangeConditionBuilder.init(ranges)
                        .setExclude(exclude)
                        .toRangeCondition();
            }
        }
        return null;
    }

    private static boolean testExclude(Element element) {
        String exclude = element.getAttribute("exclude");
        if (exclude.isEmpty()) {
            exclude = element.getAttribute("mode");
        }
        switch (exclude) {
            case "true":
            case "1":
            case "exclude":
                return true;
            default:
                return false;
        }
    }

    private static String getLogicalOperator(Element element) {
        String operator = element.getAttribute("operator");
        if (operator.isEmpty()) {
            operator = element.getAttribute("mode");
        }
        switch (operator) {
            case ConditionsConstants.LOGICALOPERATOR_AND:
            case ConditionsConstants.LOGICALOPERATOR_OR:
                return operator;
            default:
                return null;
        }
    }

    private static TextCondition checkConditionElement(Element element) {
        NodeList liste = element.getChildNodes();
        for (int i = 0; i < liste.getLength(); i++) {
            if (liste.item(i).getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) liste.item(i);
                String tagname = child.getTagName();
                switch (tagname) {
                    case "condition":
                    case "condition-group":
                        return ConditionsXMLStorage.readTextCondition(child);
                }
            }
        }
        return null;
    }

    public static boolean isEmpty(Element element) {
        int size = element.getChildNodes().getLength();
        if (size == 0) {
            return true;
        }
        if (size > 1) {
            return false;
        }
        Node node = element.getFirstChild();
        if (node.getNodeType() != Node.TEXT_NODE) {
            return false;
        }
        String data = ((Text) node).getData().trim();
        return data.isEmpty();
    }


    private static class QueryConsumer implements Consumer<Element> {

        private final Fichotheque fichotheque;
        private final FichothequeQueriesBuilder fichothequeQueriesBuilder;

        private QueryConsumer(Fichotheque fichotheque, FichothequeQueriesBuilder fichothequeQueriesBuilder) {
            this.fichotheque = fichotheque;
            this.fichothequeQueriesBuilder = fichothequeQueriesBuilder;
        }

        @Override
        public void accept(Element element) {
            readQueryElement(fichothequeQueriesBuilder, fichotheque, element);
        }

    }


    private static class FicheQueryConsumer implements Consumer<Element> {

        private final Fichotheque fichotheque;
        private final FicheQueryBuilder ficheQueryBuilder;
        private final CroisementConditionBuilder croisementConditionBuilder;
        private final StringBuilder corpusNameBuilder;
        private final BooleanBuilder satelliteBuilder;

        private FicheQueryConsumer(Fichotheque fichotheque, FicheQueryBuilder ficheQueryBuilder, CroisementConditionBuilder croisementConditionBuilder, StringBuilder corpusNameBuilder, BooleanBuilder satelliteBuilder) {
            this.fichotheque = fichotheque;
            this.ficheQueryBuilder = ficheQueryBuilder;
            this.croisementConditionBuilder = croisementConditionBuilder;
            this.corpusNameBuilder = corpusNameBuilder;
            this.satelliteBuilder = satelliteBuilder;
        }

        @Override
        public void accept(Element element) {
            String tagName = element.getTagName();
            switch (tagName) {
                case "corpus": {
                    String corpusName = XMLUtils.getData(element);
                    if (corpusName.length() > 0) {
                        if (corpusNameBuilder.length() > 0) {
                            corpusNameBuilder.append(';');
                        }
                        corpusNameBuilder.append(corpusName);
                    }
                    break;
                }
                case "range": {
                    RangeCondition rangeCondition = readRangeCondition(element);
                    if (rangeCondition != null) {
                        ficheQueryBuilder.setIdRangeCondition(rangeCondition);
                    }
                    break;
                }
                case "discard": {
                    try {
                        ficheQueryBuilder.setDiscardFilter(element.getAttribute("filter"));
                    } catch (IllegalArgumentException iae) {

                    }
                    break;
                }
                case "content": {
                    TextCondition textCondition = ConditionsXMLStorage.readTextCondition(element);
                    initFieldContent(textCondition, element);
                    break;
                }
                case "field-query":
                case "field-select": {
                    TextCondition textCondition = checkConditionElement(element);
                    initFieldContent(textCondition, element);
                    break;
                }
                case "motcle-query":
                case "motcle-select": {
                    MotcleCondition.Entry motcleEntry = getMotcleConditionEntry(fichotheque, element);
                    ficheQueryBuilder.addMotcleConditionEntry(motcleEntry);
                    break;
                }
                case "motcle-query-logic":
                case "motcle-query-mode":
                case "motcle-select-mode": {
                    String logicalOperator = getLogicalOperator(element);
                    if (logicalOperator != null) {
                        ficheQueryBuilder.setMotcleLogicalOperator(logicalOperator);
                    }
                    break;
                }
                case "fiche-query":
                case "fiche-select": {
                    FicheCondition.Entry ficheEntry = getFicheConditionEntry(fichotheque, element);
                    ficheQueryBuilder.addFicheConditionQuery(ficheEntry);
                    break;
                }
                case "fiche-query-logic":
                case "fiche-query-mode":
                case "fiche-select-mode": {
                    String logicalOperator = getLogicalOperator(element);
                    if (logicalOperator != null) {
                        ficheQueryBuilder.setFicheLogicalOperator(logicalOperator);
                    }
                    break;
                }
                case "users":
                case "redacteurs": {
                    switch (element.getAttribute("filter")) {
                        case UserCondition.FILTER_ANY:
                            ficheQueryBuilder.setUserCondition(SelectionUtils.ANY_USERCONDITION);
                            break;
                        case UserCondition.FILTER_NONE:
                            ficheQueryBuilder.setUserCondition(SelectionUtils.NONE_USERCONDITION);
                            break;
                        default: {
                            UserConditionBuilder userConditionBuilder = new UserConditionBuilder();
                            DOMUtils.readChildren(element, new UserConsumer(fichotheque, userConditionBuilder));
                            if (!userConditionBuilder.isEmpty()) {
                                ficheQueryBuilder.setUserCondition(userConditionBuilder.toUserCondition());
                            }
                        }
                    }
                    break;
                }
                case "period": {
                    ficheQueryBuilder.setPeriodCondition(getPeriodCondition(element));
                    break;
                }
                case "satellite":
                case "satellites":
                case "parentage":
                case "rattachement": {
                    satelliteBuilder.setValue(true);
                    break;
                }
                case "current": {
                    ficheQueryBuilder.setWithCurrentCorpus(true);
                    break;
                }
                case "exclude": {
                    ficheQueryBuilder.setExclude(true);
                    break;
                }
                case "geoloc": {
                    ficheQueryBuilder.setWithGeoloc(true);
                    break;
                }
                default: {
                    testConditionCroisement(element, croisementConditionBuilder);
                }
            }
        }

        private void initFieldContent(TextCondition textCondition, Element element) {
            if (textCondition == null) {
                return;
            }
            if (ConditionsUtils.isNeutral(textCondition)) {
                return;
            }
            String scope = FieldContentCondition.TITLE_SCOPE;
            String scopeString = element.getAttribute("scope");
            try {
                scope = FieldContentCondition.checkScope(scopeString);
            } catch (IllegalArgumentException iae) {
            }
            Set<FieldKey> fieldKeySet = null;
            if (scope.equals(FieldContentCondition.SELECTION_SCOPE)) {
                fieldKeySet = getFieldKeySet(element);
                if (fieldKeySet.isEmpty()) {
                    scope = FieldContentCondition.TITLE_SCOPE;
                }
            }
            ficheQueryBuilder.setFieldContentCondition(textCondition, scope, fieldKeySet);
        }

    }


    private static class MotcleQueryConsumer implements Consumer<Element> {

        private final Fichotheque fichotheque;
        private final MotcleQueryBuilder motcleQueryBuilder;
        private final CroisementConditionBuilder croisementConditionBuilder;
        private final StringBuilder thesaurusNameBuffer;
        private final BooleanBuilder masterBuilder;

        private MotcleQueryConsumer(Fichotheque fichotheque, MotcleQueryBuilder motcleQueryBuilder, CroisementConditionBuilder croisementConditionBuilder, StringBuilder thesaurusNameBuffer, BooleanBuilder masterBuilder) {
            this.fichotheque = fichotheque;
            this.motcleQueryBuilder = motcleQueryBuilder;
            this.croisementConditionBuilder = croisementConditionBuilder;
            this.thesaurusNameBuffer = thesaurusNameBuffer;
            this.masterBuilder = masterBuilder;
        }

        @Override
        public void accept(Element element) {
            String tagName = element.getTagName();
            switch (tagName) {
                case "thesaurus": {
                    String thesaurusName = XMLUtils.getData(element);
                    if (thesaurusName.length() > 0) {
                        if (thesaurusNameBuffer.length() > 0) {
                            thesaurusNameBuffer.append(';');
                        }
                        thesaurusNameBuffer.append(thesaurusName);
                    }
                    break;
                }
                case "range": {
                    RangeCondition idRangeCondition = readRangeCondition(element);
                    if (idRangeCondition != null) {
                        motcleQueryBuilder.setIdRangeCondition(idRangeCondition);
                    }
                    break;
                }
                case "level": {
                    RangeCondition levelRangeCondition = readRangeCondition(element);
                    if (levelRangeCondition != null) {
                        motcleQueryBuilder.setLevelRangeCondition(levelRangeCondition);
                    }
                    break;
                }
                case "master":
                case "satellites":
                case "parentage":
                case "rattachement": {
                    masterBuilder.setValue(true);
                    break;
                }
                case "fiche-query":
                case "fiche-select": {
                    FicheCondition.Entry ficheEntry = getFicheConditionEntry(fichotheque, element);
                    motcleQueryBuilder.addFicheConditionQuery(ficheEntry);
                    break;
                }
                case "fiche-query-logic":
                case "fiche-query-mode":
                case "fiche-select-mode": {
                    String logicalOperator = getLogicalOperator(element);
                    if (logicalOperator != null) {
                        motcleQueryBuilder.setFicheLogicalOperator(logicalOperator);
                    }
                    break;
                }
                case "content":
                case "condition":
                case "condition-group": {
                    String scope = MotcleQuery.SCOPE_IDALPHA_ONLY;
                    try {
                        scope = MotcleQuery.checkScope(element.getAttribute("scope"));
                    } catch (IllegalArgumentException iae) {

                    }
                    TextCondition condition = ConditionsXMLStorage.readTextCondition(element);
                    if (!ConditionsUtils.isNeutral(condition)) {
                        motcleQueryBuilder.setContentCondition(condition, scope);
                    }
                    break;
                }
                case "status": {
                    motcleQueryBuilder.addStatus(XMLUtils.getData(element));
                    break;
                }
                default: {
                    testConditionCroisement(element, croisementConditionBuilder);
                }
            }
        }

    }


    private static class BooleanBuilder {

        private boolean value = false;

        private BooleanBuilder() {

        }

        private void setValue(boolean includeSatellites) {
            this.value = includeSatellites;
        }

        private boolean getValue() {
            return value;
        }

    }


    private static class UserConsumer implements Consumer<Element> {

        private final Fichotheque fichotheque;
        private final UserConditionBuilder userConditionBuilder;


        private UserConsumer(Fichotheque fichotheque, UserConditionBuilder userConditionBuilder) {
            this.fichotheque = fichotheque;
            this.userConditionBuilder = userConditionBuilder;
        }

        @Override
        public void accept(Element element) {
            String tagName = element.getTagName();
            switch (tagName) {
                case "user":
                case "redacteur":
                    addUser(element);
                    break;
                case "sphere":
                    addSphere(element);
                    break;
            }
        }

        private void addUser(Element element) {
            String sphereName = element.getAttribute("sphere");
            if (sphereName.length() > 0) {
                try {
                    SubsetKey sphereKey = SubsetKey.parse(SubsetKey.CATEGORY_SPHERE, sphereName);
                    String login = element.getAttribute("login");
                    if (login.length() == 0) {
                        login = element.getAttribute("idsphere");
                    }
                    if (login.length() > 0) {
                        LoginKey.checkLogin(login);
                        userConditionBuilder.addLogin(sphereKey, login);
                    } else {
                        String idString = element.getAttribute("id");
                        if (idString.length() == 0) {
                            idString = element.getAttribute("idsph");
                        }
                        if (idString.length() > 0) {
                            try {
                                int id = Integer.parseInt(idString);
                                if (id > 0) {
                                    userConditionBuilder.addId(sphereKey, id);
                                }
                            } catch (NumberFormatException nfe) {
                            }
                        } else {
                            userConditionBuilder.addSphere(sphereKey);
                        }
                    }
                } catch (ParseException pe) {
                }
            } else {
                String data = XMLUtils.getData(element);
                if (data.length() > 0) {
                    try {
                        parse(userConditionBuilder, data);
                    } catch (ParseException pe) {

                    }
                }
            }
        }

        private void addSphere(Element element) {
            String sphereName = XMLUtils.getData(element);
            if (sphereName.length() > 0) {
                try {
                    SubsetKey sphereKey = SubsetKey.parse(SubsetKey.CATEGORY_SPHERE, sphereName);
                    userConditionBuilder.addSphere(sphereKey);
                } catch (ParseException pe) {
                }
            }
        }

    }


    private static class FieldConsumer implements Consumer<Element> {

        private final PeriodConditionBuilder builder;

        private FieldConsumer(PeriodConditionBuilder builder) {
            this.builder = builder;
        }

        @Override
        public void accept(Element element) {
            String tagName = element.getTagName();
            switch (tagName) {
                case "field": {
                    try {
                        FieldKey fieldKey = FieldKey.parse(XMLUtils.getData(element));
                        builder.addFieldKey(fieldKey);
                    } catch (ParseException pe) {
                    }
                    break;
                }
                case "chrono": {
                    String chrono = XMLUtils.getData(element);
                    if (chrono.equals("creation")) {
                        builder.setOnCreationDate(true);
                    } else if (chrono.equals("modification")) {
                        builder.setOnModificationDate(true);
                    }
                    break;
                }
            }
        }

    }

    public static void parse(UserConditionBuilder userConditionBuilder, String key) throws ParseException {
        Matcher matcher = GLOBALID_REGEX.matcher(key);
        if (matcher.matches()) {
            try {
                SubsetKey sphereKey = SubsetKey.parse(SubsetKey.CATEGORY_SPHERE, matcher.group(1));
                int id = Integer.parseInt(matcher.group(2));
                if (id < 1) {
                    throw new ParseException("id = 0", key.indexOf('/'));
                }
                userConditionBuilder.addId(sphereKey, id);
            } catch (ParseException | NumberFormatException e) {
                throw new ShouldNotOccurException(e);
            }
        }
        matcher = BRACKETS_REGEX.matcher(key);
        if (matcher.matches()) {
            try {
                SubsetKey sphereKey = SubsetKey.parse(SubsetKey.CATEGORY_SPHERE, matcher.group(1));
                String login = matcher.group(2);
                userConditionBuilder.addLogin(sphereKey, login);
            } catch (ParseException e) {
                throw new ShouldNotOccurException(e);
            }
        }
        matcher = UNDERSCORE_REGEX.matcher(key);
        if (matcher.matches()) {
            try {
                SubsetKey sphereKey = SubsetKey.parse(SubsetKey.CATEGORY_SPHERE, matcher.group(2));
                String login = matcher.group(1);
                userConditionBuilder.addLogin(sphereKey, login);
            } catch (ParseException e) {
                throw new ShouldNotOccurException(e);
            }
        }
        throw new ParseException("Matching none pattern", 0);
    }


}
