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

import fr.exemole.bdfserver.api.BdfServer;
import fr.exemole.bdfserver.api.BdfServerEditor;
import fr.exemole.bdfserver.api.namespaces.CellSpace;
import fr.exemole.bdfserver.api.ui.CommentUi;
import fr.exemole.bdfserver.api.ui.DataUi;
import fr.exemole.bdfserver.api.ui.FieldUi;
import fr.exemole.bdfserver.api.ui.IncludeUi;
import fr.exemole.bdfserver.api.ui.MetadataPhraseDef;
import fr.exemole.bdfserver.api.ui.SubsetIncludeUi;
import fr.exemole.bdfserver.api.ui.UiComponent;
import fr.exemole.bdfserver.api.ui.UiComponentFilter;
import fr.exemole.bdfserver.api.ui.UiComponents;
import fr.exemole.bdfserver.tools.ui.components.CommentUiBuilder;
import fr.exemole.bdfserver.tools.ui.components.FieldUiBuilder;
import fr.exemole.bdfserver.tools.ui.components.IncludeUiBuilder;
import fr.exemole.bdfserver.tools.ui.components.UiSummary;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.RandomAccess;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.function.Predicate;
import net.fichotheque.FichothequeConstants;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.include.LiageTest;
import net.fichotheque.tools.include.LiageTestBuilder;
import net.mapeadores.util.attr.Attribute;
import net.mapeadores.util.attr.Attributes;
import net.mapeadores.util.html.HtmlCleaner;
import net.mapeadores.util.html.TrustedHtml;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.localisation.Langs;
import net.mapeadores.util.text.StringUtils;


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

    public final static UiComponents EMPTY_UICOMPONENTS = new EmptyUiComponents();
    public final static List<MetadataPhraseDef> EMPTY_METADATAPHRASEDEFLIST = Collections.emptyList();
    public final static List<IncludeUi> EMPTY_INCLUDEUILIST = Collections.emptyList();
    public final static List<SubsetIncludeUi> EMPTY_SUBSETINCLUDEUILIST = Collections.emptyList();
    public final static List<CommentUi> EMPTY_COMMENTUILIST = Collections.emptyList();
    public final static List<DataUi> EMPTY_DATAUILIST = Collections.emptyList();
    public final static Predicate<UiComponent> ALL_COMPONENTPREDICATE = new AllComponentPredicate();
    public final static Predicate<UiComponent> MAIN_COMPONENTPREDICATE = new MainComponentPredicate();
    public final static Predicate<UiComponent> SATELLITE_COMPONENTPREDICATE = new SatelliteComponentPredicate();

    private UiUtils() {

    }

    public static String maskToString(int position) {
        MaskBuffer maskBuffer = new MaskBuffer(position);
        maskBuffer.append(CommentUi.FORM_BITVALUE, "form");
        maskBuffer.append(CommentUi.TEMPLATE_BITVALUE, "template");
        return maskBuffer.toString();
    }

    public static int maskStringToInt(String s) {
        int location = 0;
        String[] array = StringUtils.getTokens(s, '+', StringUtils.EMPTY_EXCLUDE);
        for (String token : array) {
            if (token.equals("form")) {
                location = location | CommentUi.FORM_BITVALUE;
            } else if ((token.equals("affichage")) || (token.equals("template"))) {
                location = location | CommentUi.TEMPLATE_BITVALUE;
            }
        }
        if (location == 0) {
            location = CommentUi.ALL_MASK;
        }
        return location;
    }

    public static List<SubsetIncludeUi> getSubsetIncludeUiList(UiComponents uiComponents, short subsetType) {
        List<UiComponent> uiComponentList = uiComponents.getUiComponentList();
        if (uiComponentList.isEmpty()) {
            return EMPTY_SUBSETINCLUDEUILIST;
        }
        List<SubsetIncludeUi> includeUiList = new ArrayList<SubsetIncludeUi>();
        for (UiComponent uiComponent : uiComponentList) {
            if (uiComponent instanceof SubsetIncludeUi) {
                SubsetIncludeUi includeUi = (SubsetIncludeUi) uiComponent;
                if (includeUi.getCategory() == subsetType) {
                    includeUiList.add(includeUi);
                }
            }
        }
        return includeUiList;
    }

    public static List<DataUi> getDataUiList(UiComponents uiComponents) {
        List<UiComponent> uiComponentList = uiComponents.getUiComponentList();
        if (uiComponentList.isEmpty()) {
            return EMPTY_DATAUILIST;
        }
        List<DataUi> dataUiList = new ArrayList<DataUi>();
        for (UiComponent uiComponent : uiComponentList) {
            if (uiComponent instanceof DataUi) {
                dataUiList.add((DataUi) uiComponent);
            }
        }
        return dataUiList;
    }

    public static List<CommentUi> getCommentUiList(UiComponents uiComponents) {
        List<UiComponent> uiComponentList = uiComponents.getUiComponentList();
        if (uiComponentList.isEmpty()) {
            return EMPTY_COMMENTUILIST;
        }
        List<CommentUi> commentUiList = new ArrayList<CommentUi>();
        Set<String> doneSet = new HashSet<String>();
        for (UiComponent uiComponent : uiComponentList) {
            if (uiComponent instanceof CommentUi) {
                CommentUi commentUi = (CommentUi) uiComponent;
                if (!doneSet.contains(commentUi.getName())) {
                    doneSet.add(commentUi.getName());
                    commentUiList.add(commentUi);
                }
            }
        }
        return commentUiList;
    }

    public static UiSummary summarize(UiComponents uiComponents) {
        boolean addendaInclude = false;
        boolean albumInclude = false;
        for (UiComponent uiComponent : uiComponents.getUiComponentList()) {
            if (uiComponent instanceof SubsetIncludeUi) {
                SubsetIncludeUi includeUi = (SubsetIncludeUi) uiComponent;
                switch (includeUi.getCategory()) {
                    case SubsetKey.CATEGORY_ADDENDA:
                        addendaInclude = true;
                        break;
                    case SubsetKey.CATEGORY_ALBUM:
                        albumInclude = true;
                        break;
                }
            }
        }
        return new UiSummary(addendaInclude, albumInclude);
    }


    public static String[] toStringArray(CommentUi commentUi, Lang[] langArray) {
        int length = langArray.length;
        String[] result = new String[length];
        for (int i = 0; i < length; i++) {
            String html;
            TrustedHtml trustedHtml = commentUi.getHtmlByLang(langArray[i]);
            if (trustedHtml != null) {
                html = trustedHtml.toString();
            } else {
                html = "";
            }
            result[i] = html;
        }
        return result;
    }

    public static String[] toPositionArray(UiComponents uiComponents) {
        List<UiComponent> uiComponentList = uiComponents.getUiComponentList();
        int size = uiComponentList.size();
        String[] result = new String[size];
        for (int i = 0; i < size; i++) {
            result[i] = uiComponentList.get(i).getName();
        }
        return result;
    }


    public static UiComponents filter(UiComponents uiComponents, UiComponentFilter uiComponentFilter) {
        if (uiComponentFilter == null) {
            return uiComponents;
        }
        List<UiComponent> result = new ArrayList<UiComponent>();
        for (UiComponent componentUi : uiComponents.getUiComponentList()) {
            componentUi = uiComponentFilter.filter(componentUi);
            if (componentUi != null) {
                result.add(componentUi);
            }
        }
        int size = result.size();
        if (size == 0) {
            return EMPTY_UICOMPONENTS;
        }
        return new ArrayUiComponents(result.toArray(new UiComponent[size]));
    }

    public static LiageTest checkLiageTest(UiComponents uiComponents) {
        LiageTestBuilder builder = new LiageTestBuilder();
        for (UiComponent componentUi : uiComponents.getUiComponentList()) {
            if (componentUi instanceof SubsetIncludeUi) {
                builder.checkIncludeKey(((SubsetIncludeUi) componentUi).getExtendedIncludeKey());
            }
        }
        return builder.toLiageTest();
    }

    public static List<UiComponent> wrap(UiComponent[] array) {
        return new UiComponentList(array);
    }

    public static void copyUi(BdfServerEditor bdfServerEditor, Corpus origin, Corpus destination) {
        BdfServer bdfServer = bdfServerEditor.getBdfServer();
        UiComponents originUiComponents = bdfServer.getUiManager().getMainUiComponents(origin);
        UiComponents newUiComponents = bdfServer.getUiManager().getMainUiComponents(destination);
        HtmlCleaner htmlCleaner = bdfServer.getUiManager().getHtmlCleaner();
        for (UiComponent uiComponent : originUiComponents.getUiComponentList()) {
            if (uiComponent instanceof FieldUi) {
                FieldUi fieldUi = (FieldUi) uiComponent;
                FieldUiBuilder fieldUiBuilder = FieldUiBuilder.derive(fieldUi);
                bdfServerEditor.putComponentUi(newUiComponents, fieldUiBuilder.toFieldUi());
            } else if (uiComponent instanceof IncludeUi) {
                IncludeUiBuilder includeUiBuilder = IncludeUiBuilder.init((IncludeUi) uiComponent);
                bdfServerEditor.putComponentUi(newUiComponents, includeUiBuilder.toIncludeUi());
            } else if (uiComponent instanceof CommentUi) {
                CommentUiBuilder builder = CommentUiBuilder.init(htmlCleaner, (CommentUi) uiComponent);
                bdfServerEditor.putComponentUi(newUiComponents, builder.toCommentUi());
            }
        }
        bdfServerEditor.setPositionArray(newUiComponents, toPositionArray(originUiComponents));
    }

    public static void updateUi(BdfServerEditor bdfServerEditor, Corpus corpus, UiComponents newUiComponents) {
        UiComponents currentUiComponents = bdfServerEditor.getBdfServer().getUiManager().getMainUiComponents(corpus);
        List<String> nameList = new ArrayList<String>();
        for (UiComponent uiComponent : newUiComponents.getUiComponentList()) {
            bdfServerEditor.putComponentUi(currentUiComponents, uiComponent);
            nameList.add(uiComponent.getName());
        }
        bdfServerEditor.setPositionArray(currentUiComponents, nameList.toArray(new String[nameList.size()]));
    }

    public static List<UiComponent> filter(List<UiComponent> uiComponentList, Predicate<UiComponent> predicate) {
        List<UiComponent> temp = new ArrayList<UiComponent>();
        for (UiComponent uiComponent : uiComponentList) {
            if (predicate.test(uiComponent)) {
                temp.add(uiComponent);
            }
        }
        return temp;
    }

    public static Predicate<UiComponent> getFicheTableUiComponentPredicate(SubsetKey parentKey) {
        return new FicheTableComponentPredicate(parentKey);
    }

    public static List<UiComponent> filterFicheTableUiComponents(UiComponents uiComponents, SubsetKey parentKey) {
        Predicate<UiComponent> predicate = getFicheTableUiComponentPredicate(parentKey);
        SortedMap<Integer, List<UiComponent>> orderMap = new TreeMap<Integer, List<UiComponent>>();
        for (UiComponent uiComponent : uiComponents.getUiComponentList()) {
            if (!predicate.test(uiComponent)) {
                continue;
            }
            Attribute orderAttribute = uiComponent.getAttributes().getAttribute(CellSpace.ORDER_KEY);
            if (orderAttribute != null) {
                try {
                    int value = Integer.parseInt(orderAttribute.getFirstValue());
                    if (value > 0) {
                        if (orderMap.containsKey(value)) {
                            orderMap.get(value).add(uiComponent);
                        } else {
                            List<UiComponent> list = new ArrayList<UiComponent>();
                            list.add(uiComponent);
                            orderMap.put(value, list);
                        }
                    }
                } catch (NumberFormatException nfe) {

                }
            }
        }
        List<UiComponent> uiComponentList = new ArrayList<UiComponent>();
        if (orderMap.isEmpty()) {
            for (UiComponent uiComponent : uiComponents.getUiComponentList()) {
                if (!predicate.test(uiComponent)) {
                    continue;
                }
                uiComponentList.add(uiComponent);
            }
        } else {
            for (List<UiComponent> subList : orderMap.values()) {
                uiComponentList.addAll(subList);
            }
        }
        return uiComponentList;
    }

    public static CommentUi cloneCommentUi(CommentUi originalCommentUi, int cloneNumber) {
        return new CloneCommentUi(originalCommentUi, cloneNumber);
    }


    private static class EmptyUiComponents implements UiComponents {

        private final List<UiComponent> list = Collections.emptyList();

        private EmptyUiComponents() {
        }

        @Override
        public List<UiComponent> getUiComponentList() {
            return list;
        }

        @Override
        public UiComponent getUiComponent(String keyString) {
            return null;
        }

    }


    private static class ArrayUiComponents implements UiComponents {

        private final UiComponent[] uiComponentArray;
        private final List<UiComponent> list;

        private ArrayUiComponents(UiComponent[] uiComponentArray) {
            this.uiComponentArray = uiComponentArray;
            this.list = wrap(uiComponentArray);
        }

        @Override
        public List<UiComponent> getUiComponentList() {
            return list;
        }

        @Override
        public UiComponent getUiComponent(String keyString) {
            int length = uiComponentArray.length;
            for (int i = 0; i < length; i++) {
                UiComponent componentUi = uiComponentArray[i];
                if (componentUi.getName().equals(keyString)) {
                    return componentUi;
                }
            }
            return null;
        }

    }


    private static class UiComponentList extends AbstractList<UiComponent> implements RandomAccess {

        private final UiComponent[] array;

        private UiComponentList(UiComponent[] array) {
            this.array = array;
        }

        @Override
        public int size() {
            return array.length;
        }

        @Override
        public UiComponent get(int index) {
            return array[index];
        }

    }


    private static class MaskBuffer {

        private final StringBuilder buf = new StringBuilder();
        private final int location;
        private boolean premier = true;

        private MaskBuffer(int location) {
            this.location = location;
        }

        private void append(int mask, String maskString) {
            if ((location & mask) != 0) {
                if (premier) {
                    premier = false;
                } else {
                    buf.append("+");
                }
                buf.append(maskString);
            }
        }

        @Override
        public String toString() {
            return buf.toString();
        }

    }


    private static class AllComponentPredicate implements Predicate<UiComponent> {

        private AllComponentPredicate() {

        }

        @Override
        public boolean test(UiComponent iComponent) {
            return true;
        }

    }


    private static class MainComponentPredicate implements Predicate<UiComponent> {

        private MainComponentPredicate() {

        }

        @Override
        public boolean test(UiComponent uiComponent) {
            switch (uiComponent.getName()) {
                case FichothequeConstants.PARENTAGE_NAME:
                    return false;
                default:
                    return true;
            }
        }

    }


    private static class SatelliteComponentPredicate implements Predicate<UiComponent> {

        private SatelliteComponentPredicate() {

        }

        @Override
        public boolean test(UiComponent uiComponent) {
            switch (uiComponent.getName()) {
                case FieldKey.SPECIAL_TITLE:
                case FieldKey.SPECIAL_OWNERS:
                case FieldKey.SPECIAL_LANG:
                case FichothequeConstants.PARENTAGE_NAME:
                case FichothequeConstants.LIAGE_NAME:
                    return false;
                default:
                    return true;
            }
        }

    }


    private static class FicheTableComponentPredicate implements Predicate<UiComponent> {

        private final SubsetKey parentKey;

        private FicheTableComponentPredicate(SubsetKey parentKey) {
            this.parentKey = parentKey;
        }

        @Override
        public boolean test(UiComponent uiComponent) {
            if (uiComponent instanceof SubsetIncludeUi) {
                if ((parentKey != null) && ((SubsetIncludeUi) uiComponent).getSubsetKey().equals(parentKey)) {
                    return false;
                } else {
                    return true;
                }
            }
            if (uiComponent instanceof FieldUi) {
                FieldKey fieldKey = ((FieldUi) uiComponent).getFieldKey();
                switch (fieldKey.getCategory()) {
                    case FieldKey.SECTION_CATEGORY:
                        return false;
                    case FieldKey.SPECIAL_CATEGORY: {
                        switch (fieldKey.getKeyString()) {
                            case FieldKey.SPECIAL_OWNERS:
                            case FieldKey.SPECIAL_LANG:
                                return false;
                            default:
                                return true;
                        }
                    }
                    default:
                        return true;

                }
            }
            return false;
        }

    }


    private static class CloneCommentUi implements CommentUi {

        private final CommentUi originalCommentUi;
        private final String cloneName;

        private CloneCommentUi(CommentUi originalCommentUi, int cloneNumber) {
            this.originalCommentUi = originalCommentUi;
            this.cloneName = originalCommentUi.getName() + "-" + cloneNumber;
        }

        @Override
        public String getName() {
            return originalCommentUi.getName();
        }

        @Override
        public int getLocation() {
            return originalCommentUi.getLocation();
        }

        @Override
        public String getCommentName() {
            return originalCommentUi.getCommentName();
        }

        @Override
        public Langs getLangs() {
            return originalCommentUi.getLangs();
        }

        @Override
        public TrustedHtml getHtmlByLang(Lang lang) {
            return originalCommentUi.getHtmlByLang(lang);
        }

        @Override
        public Attributes getAttributes() {
            return originalCommentUi.getAttributes();

        }

        @Override
        public boolean isClone() {
            return true;
        }

        @Override
        public String getCloneName() {
            return cloneName;
        }

    }

}
