/* FichothequeLib_Tools - Copyright (c) 2013-2025 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.corpus;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.fichotheque.corpus.FicheChange;
import net.fichotheque.corpus.fiche.Fiche;
import net.fichotheque.corpus.fiche.FicheAPI;
import net.fichotheque.corpus.fiche.FicheBlocks;
import net.fichotheque.corpus.fiche.FicheItem;
import net.fichotheque.corpus.fiche.FicheItems;
import net.fichotheque.corpus.fiche.Info;
import net.fichotheque.corpus.fiche.ParaItem;
import net.fichotheque.corpus.fiche.Prop;
import net.fichotheque.corpus.fiche.Section;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.utils.CorpusMetadataUtils;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.text.CleanedString;


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

    private final Map<FieldKey, Boolean> fieldKeyMap = new HashMap<FieldKey, Boolean>();
    private final Fiche fiche;

    public FicheChangeBuilder() {
        this(new Fiche());
    }

    public FicheChangeBuilder(Fiche fiche) {
        this.fiche = fiche;
    }

    public FicheChangeBuilder setTitle(CleanedString cs) {
        if (cs == null) {
            fieldKeyMap.put(FieldKey.TITLE, Boolean.FALSE);
            fiche.setTitle("");
        } else {
            fieldKeyMap.put(FieldKey.TITLE, Boolean.TRUE);
            fiche.setTitle(cs.toString());
        }
        return this;
    }

    public FicheChangeBuilder setLang(Lang lang) {
        if (lang == null) {
            fieldKeyMap.put(FieldKey.LANG, Boolean.FALSE);
            fiche.setLang(null);
        } else {
            fieldKeyMap.put(FieldKey.LANG, Boolean.TRUE);
            fiche.setLang(lang);
        }
        return this;
    }

    public FicheChangeBuilder setSubtitle(ParaItem subtitlePara) {
        if ((subtitlePara == null) || (subtitlePara.isEmpty())) {
            fieldKeyMap.put(FieldKey.SUBTITLE, Boolean.FALSE);
            fiche.setSubtitle(null);
        } else {
            fieldKeyMap.put(FieldKey.SUBTITLE, Boolean.TRUE);
            fiche.setSubtitle(subtitlePara);
        }
        return this;
    }

    public FicheChangeBuilder appendOwners(FicheItems ficheItems) {
        if (ficheItems.isEmpty()) {
            setRemoveable(FieldKey.OWNERS);
        } else {
            fieldKeyMap.put(FieldKey.OWNERS, Boolean.TRUE);
            fiche.appendOwners(ficheItems);
        }
        return this;
    }

    public FicheChangeBuilder setProp(FieldKey fieldKey, FicheItem ficheItem) {
        fiche.setProp(fieldKey, ficheItem);
        if (ficheItem == null) {
            fieldKeyMap.put(fieldKey, Boolean.FALSE);
        } else {
            fieldKeyMap.put(fieldKey, Boolean.TRUE);
        }
        return this;
    }

    public FicheChangeBuilder appendInfo(FieldKey fieldKey, FicheItems ficheItems) {
        if (ficheItems.isEmpty()) {
            setRemoveable(fieldKey);
        } else {
            fieldKeyMap.put(fieldKey, Boolean.TRUE);
            fiche.appendInfo(fieldKey, ficheItems);
        }
        return this;
    }

    public FicheChangeBuilder appendSection(FieldKey fieldKey, FicheBlocks ficheBlocks) {
        if (ficheBlocks.isEmpty()) {
            setRemoveable(fieldKey);
        } else {
            fieldKeyMap.put(fieldKey, Boolean.TRUE);
            fiche.appendSection(fieldKey, ficheBlocks);
        }
        return this;
    }

    public FicheChangeBuilder removeField(FieldKey fieldKey) {
        switch (fieldKey.getCategory()) {
            case FieldKey.PROP_CATEGORY:
                setProp(fieldKey, null);
                break;
            case FieldKey.INFO_CATEGORY:
                setRemoveable(fieldKey);
                break;
            case FieldKey.SECTION_CATEGORY:
                setRemoveable(fieldKey);
                break;
            case FieldKey.SPECIAL_CATEGORY:
                switch (fieldKey.getKeyString()) {
                    case FieldKey.SPECIAL_TITLE:
                        setTitle(null);
                        break;
                    case FieldKey.SPECIAL_SUBTITLE:
                        setSubtitle(null);
                        break;
                    case FieldKey.SPECIAL_LANG:
                        setLang(null);
                        break;
                    case FieldKey.SPECIAL_OWNERS:
                        setRemoveable(fieldKey);
                        break;
                }
                break;
        }
        return this;
    }

    public FicheChangeBuilder appendFiche(FicheAPI fiche) {
        CleanedString title = CleanedString.newInstance(fiche.getTitle());
        if (title != null) {
            setTitle(title);
        }
        Lang lang = fiche.getLang();
        if (lang != null) {
            setLang(lang);
        }
        ParaItem subtitlePara = fiche.getSubtitle();
        if (subtitlePara != null) {
            setSubtitle(subtitlePara);
        }
        FicheItems owners = fiche.getOwners();
        if (owners != null) {
            appendOwners(owners);
        }
        for (Prop prop : fiche.getPropList()) {
            setProp(prop.getFieldKey(), prop.getFicheItem());
        }
        for (Info info : fiche.getInfoList()) {
            appendInfo(info.getFieldKey(), info);
        }
        for (Section section : fiche.getSectionList()) {
            appendSection(section.getFieldKey(), section);
        }
        return this;
    }

    public FicheChangeBuilder merge(FicheChangeBuilder otherFicheChangeBuilder) {
        Fiche otherFiche = otherFicheChangeBuilder.fiche;
        for (Info info : otherFiche.getInfoList()) {
            appendInfo(info.getFieldKey(), info);
        }
        for (Section section : otherFiche.getSectionList()) {
            appendSection(section.getFieldKey(), section);
        }
        FicheItems otherOwners = otherFiche.getOwners();
        if (otherOwners != null) {
            appendOwners(otherOwners);
        }
        for (Map.Entry<FieldKey, Boolean> entry : otherFicheChangeBuilder.fieldKeyMap.entrySet()) {
            boolean b = entry.getValue();
            if (!b) {
                FieldKey fieldKey = entry.getKey();
                if (!fieldKeyMap.containsKey(fieldKey)) {
                    fieldKeyMap.put(fieldKey, Boolean.FALSE);
                }
            }
        }
        return this;
    }

    public FicheChange toFicheChange() {
        boolean notEmpty = false;
        List<FieldKey> removedList = new ArrayList<FieldKey>();
        for (Map.Entry<FieldKey, Boolean> entry : fieldKeyMap.entrySet()) {
            Boolean b = entry.getValue();
            if (!b) {
                removedList.add(entry.getKey());
            } else {
                notEmpty = true;
            }
        }
        Fiche ficheAPI;
        if (notEmpty) {
            ficheAPI = fiche;
        } else {
            ficheAPI = null;
        }
        List<FieldKey> finalRemovedList = CorpusMetadataUtils.wrap(removedList.toArray(new FieldKey[removedList.size()]));
        return new InternalFicheChange(ficheAPI, finalRemovedList);
    }

    private void setRemoveable(FieldKey fieldKey) {
        if (!fieldKeyMap.containsKey(fieldKey)) {
            fieldKeyMap.put(fieldKey, Boolean.FALSE);
        }
    }

    public static FicheChangeBuilder init() {
        return new FicheChangeBuilder();
    }

    public static FicheChangeBuilder init(Fiche fiche) {
        return new FicheChangeBuilder(fiche);
    }


    private static class InternalFicheChange implements FicheChange {

        private final FicheAPI ficheAPI;
        private final List<FieldKey> removedList;

        private InternalFicheChange(FicheAPI ficheAPI, List<FieldKey> removedList) {
            this.ficheAPI = ficheAPI;
            this.removedList = removedList;
        }

        @Override
        public FicheAPI getFicheAPI() {
            return ficheAPI;
        }

        @Override
        public List<FieldKey> getRemovedList() {
            return removedList;
        }

    }

}
