/* BdfServer - Copyright (c) 2015-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 fr.exemole.bdfserver.tools.ui.components;

import fr.exemole.bdfserver.api.BdfServerConstants;
import fr.exemole.bdfserver.api.ui.IncludeUi;
import fr.exemole.bdfserver.api.ui.SpecialIncludeUi;
import fr.exemole.bdfserver.api.ui.SubsetIncludeUi;
import java.util.Collections;
import java.util.Set;
import java.util.SortedMap;
import net.fichotheque.include.ExtendedIncludeKey;
import net.mapeadores.util.annotation.Nullable;
import net.mapeadores.util.attr.AttributeChangeBuilder;
import net.mapeadores.util.attr.Attributes;
import net.mapeadores.util.text.Label;
import net.mapeadores.util.text.LabelChangeBuilder;
import net.mapeadores.util.text.Labels;


/**
 *
 * @author Vincent Calame
 */
public class IncludeUiBuilder extends UiComponentBuilder {

    private final String name;
    private final ExtendedIncludeKey extendedIncludeKey;
    private final AttributeChangeBuilder attributeChangeBuilder = new AttributeChangeBuilder();
    private final LabelChangeBuilder labelChangeBuilder = new LabelChangeBuilder();
    private final Attributes previousAttributes;
    private String status = BdfServerConstants.DEFAULT_STATUS;

    public IncludeUiBuilder(String name) {
        this(name, null);
    }

    public IncludeUiBuilder(String name, @Nullable Attributes previousAttributes) {
        if (name == null) {
            throw new IllegalArgumentException("name is null");
        }
        this.name = name;
        this.extendedIncludeKey = null;
        this.previousAttributes = previousAttributes;
    }

    public IncludeUiBuilder(SpecialIncludeUi specialIncludeUi) {
        this.name = specialIncludeUi.getName();
        this.status = specialIncludeUi.getStatus();
        this.extendedIncludeKey = null;
        this.previousAttributes = specialIncludeUi.getAttributes();
        for (String optionName : specialIncludeUi.getOptionNameSet()) {
            optionMap.put(optionName, specialIncludeUi.getOption(optionName));
        }
        Labels customLabels = specialIncludeUi.getCustomLabels();
        if (customLabels != null) {
            labelChangeBuilder.putLabels(customLabels);
        }
    }

    public IncludeUiBuilder(ExtendedIncludeKey extendedIncludeKey) {
        this(extendedIncludeKey, null);
    }

    public IncludeUiBuilder(ExtendedIncludeKey extendedIncludeKey, @Nullable Attributes previousAttributes) {
        if (extendedIncludeKey == null) {
            throw new IllegalArgumentException("extendedIncludeKey is null");
        }
        this.name = extendedIncludeKey.getKeyString();
        this.extendedIncludeKey = extendedIncludeKey;
        this.previousAttributes = previousAttributes;
    }

    public IncludeUiBuilder(SubsetIncludeUi subsetIncludeUi) {
        this.name = subsetIncludeUi.getName();
        this.extendedIncludeKey = subsetIncludeUi.getExtendedIncludeKey();
        this.status = subsetIncludeUi.getStatus();
        for (String optionName : subsetIncludeUi.getOptionNameSet()) {
            optionMap.put(optionName, subsetIncludeUi.getOption(optionName));
        }
        this.previousAttributes = subsetIncludeUi.getAttributes();
        Labels customLabels = subsetIncludeUi.getCustomLabels();
        if (customLabels != null) {
            for (Label label : customLabels) {
                labelChangeBuilder.putLabel(label);
            }
        }
    }

    @Override
    public IncludeUiBuilder setStatus(String status) {
        if (status != null) {
            this.status = BdfServerConstants.checkValidStatus(status);
        }
        return this;
    }

    public AttributeChangeBuilder getAttributeChangeBuilder() {
        return attributeChangeBuilder;
    }

    public LabelChangeBuilder getLabelChangeBuilder() {
        return labelChangeBuilder;
    }

    @Override
    public IncludeUiBuilder putOption(String name, Object obj) {
        return (IncludeUiBuilder) super.putOption(name, obj);
    }

    public IncludeUi toIncludeUi() {
        Labels customLabels = labelChangeBuilder.toLabels();
        if (customLabels.isEmpty()) {
            customLabels = null;
        }
        if (extendedIncludeKey == null) {
            return new InternalSpecialIncludeUi(name, status, sortOptions(), customLabels, attributeChangeBuilder.toAttributes(previousAttributes));
        } else {
            return new InternalSubsetIncludeUi(name, status, extendedIncludeKey, sortOptions(), customLabels, attributeChangeBuilder.toAttributes(previousAttributes));
        }
    }

    public static IncludeUiBuilder initSubset(ExtendedIncludeKey includeKey) {
        return new IncludeUiBuilder(includeKey, null);
    }

    public static IncludeUiBuilder initSubset(ExtendedIncludeKey includeKey, @Nullable Attributes attributes) {
        return new IncludeUiBuilder(includeKey, attributes);
    }

    public static IncludeUiBuilder initSpecial(String specialIncludeName) {
        return new IncludeUiBuilder(specialIncludeName, null);
    }

    public static IncludeUiBuilder initSpecial(String specialIncludeName, @Nullable Attributes attributes) {
        return new IncludeUiBuilder(specialIncludeName, attributes);
    }

    public static IncludeUiBuilder init(IncludeUi includeUi) {
        if (includeUi instanceof SubsetIncludeUi) {
            return new IncludeUiBuilder((SubsetIncludeUi) includeUi);
        }
        if (includeUi instanceof SpecialIncludeUi) {
            return new IncludeUiBuilder((SpecialIncludeUi) includeUi);
        }
        throw new ClassCastException("unknown implementation: " + includeUi.getClass().getCanonicalName());
    }


    private static class InternalSpecialIncludeUi implements SpecialIncludeUi {

        private final String name;
        private final String status;
        private final SortedMap<String, Object> optionMap;
        private final Labels customLabels;
        private final Attributes attributes;

        private InternalSpecialIncludeUi(String name, String status, SortedMap<String, Object> optionMap, Labels customLabels, Attributes attributes) {
            this.name = name;
            this.status = status;
            this.optionMap = optionMap;
            this.customLabels = customLabels;
            this.attributes = attributes;
        }

        @Override
        public String getName() {
            return name;
        }

        @Override
        public String getStatus() {
            return status;
        }

        @Override
        public Set<String> getOptionNameSet() {
            return Collections.unmodifiableSet(optionMap.keySet());
        }

        @Override
        public Object getOption(String optionName) {
            return optionMap.get(optionName);
        }

        @Override
        public Labels getCustomLabels() {
            return customLabels;
        }

        @Override
        public Attributes getAttributes() {
            return attributes;
        }

    }


    private static class InternalSubsetIncludeUi implements SubsetIncludeUi {

        private final String name;
        private final ExtendedIncludeKey extendedIncludeKey;
        private final String status;
        private final SortedMap<String, Object> optionMap;
        private final Labels customLabels;
        private final Attributes attributes;

        private InternalSubsetIncludeUi(String name, String status, ExtendedIncludeKey extendedIncludeKey, SortedMap<String, Object> optionMap, Labels customLabels, Attributes attributes) {
            this.name = name;
            this.status = status;
            this.extendedIncludeKey = extendedIncludeKey;
            this.optionMap = optionMap;
            this.customLabels = customLabels;
            this.attributes = attributes;
        }

        @Override
        public String getName() {
            return name;
        }

        @Override
        public String getStatus() {
            return status;
        }

        @Override
        public ExtendedIncludeKey getExtendedIncludeKey() {
            return extendedIncludeKey;
        }

        @Override
        public Set<String> getOptionNameSet() {
            return Collections.unmodifiableSet(optionMap.keySet());
        }

        @Override
        public Object getOption(String optionName) {
            return optionMap.get(optionName);
        }

        @Override
        public Labels getCustomLabels() {
            return customLabels;
        }

        @Override
        public Attributes getAttributes() {
            return attributes;
        }

    }

}
