/* BdfServer - Copyright (c) 2023-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.overview;

import fr.exemole.bdfserver.api.BdfServer;
import fr.exemole.bdfserver.api.BdfServerConstants;
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.SpecialIncludeUi;
import fr.exemole.bdfserver.api.ui.SubsetIncludeUi;
import fr.exemole.bdfserver.api.ui.UiComponent;
import fr.exemole.bdfserver.api.ui.UiComponents;
import fr.exemole.bdfserver.tools.ui.UiUtils;
import java.util.HashSet;
import java.util.Set;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.corpus.metadata.CorpusField;
import net.fichotheque.corpus.metadata.CorpusMetadata;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.externalsource.ExternalSourceDef;
import net.fichotheque.include.ExtendedIncludeKey;
import net.mapeadores.util.attr.AttributeParser;
import net.mapeadores.util.attr.Attributes;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.text.Label;
import net.mapeadores.util.text.Labels;
import net.mapeadores.util.text.MultiStringable;
import net.mapeadores.util.text.Phrase;


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

    private final BdfServer bdfServer;
    private final Corpus corpus;
    private final CorpusMetadata corpusMetadata;
    private final StringBuilder buf;
    private final Set<String> alreadySet = new HashSet<String>();

    private CorpusOverview(BdfServer bdfServer, Corpus corpus) {
        this.bdfServer = bdfServer;
        this.corpus = corpus;
        this.corpusMetadata = corpus.getCorpusMetadata();
        this.buf = new StringBuilder();
    }

    private String run() {
        UiComponents uiComponents = bdfServer.getUiManager().getMainUiComponents(corpus);
        metadataBlock(uiComponents);
        idBlock();
        for (UiComponent uiComponent : uiComponents.getUiComponentList()) {
            if (uiComponent instanceof CommentUi) {
                CommentUi commentUi = (CommentUi) uiComponent;
                commentBlock(commentUi);
            } else if (uiComponent instanceof FieldUi) {
                fieldBlock((FieldUi) uiComponent);
            } else if (uiComponent instanceof SpecialIncludeUi) {
                specialIncludeBlock((SpecialIncludeUi) uiComponent);
            } else if (uiComponent instanceof SubsetIncludeUi) {
                subsetIncludeBlock((SubsetIncludeUi) uiComponent);
            } else if (uiComponent instanceof DataUi) {
                dataBlock((DataUi) uiComponent);
            }
        }
        return buf.toString();
    }

    private void endLine() {
        buf.append("\n");
    }

    private void newBlock(String name) {
        buf.append("[");
        buf.append(name);
        buf.append("]");
        endLine();
    }

    private void metadataBlock(UiComponents uiComponents) {
        newBlock("metadata");
        addLabels("title", corpusMetadata.getTitleLabels());
        for (Phrase phrase : corpusMetadata.getPhrases()) {
            addLabels("phrase_" + phrase.getName(), phrase);
        }
        CorpusField geolocalisationField = corpusMetadata.getGeolocalisationField();
        if (geolocalisationField != null) {
            append("geolocalisation", geolocalisationField.getFieldString());
        }
        String rawString = corpusMetadata.getFieldGeneration().getRawString();
        if (!rawString.isEmpty()) {
            append("fieldgeneration", rawString);
        }
        StringBuilder orderBuf = new StringBuilder();
        for (UiComponent uiComponent : uiComponents.getUiComponentList()) {
            if (orderBuf.length() > 0) {
                orderBuf.append(", ");
            }
            orderBuf.append(uiComponent.getName());
        }
        append("order", orderBuf.toString());
        addAttributes(corpusMetadata.getAttributes());
        endLine();
    }

    private void idBlock() {
        newBlock("id");
        CorpusField corpusField = corpusMetadata.getCorpusField(FieldKey.ID);
        addLabels("label", corpusField.getLabels());
        endLine();
    }

    private void fieldBlock(FieldUi fieldUi) {
        FieldKey fieldKey = fieldUi.getFieldKey();
        CorpusField corpusField = corpusMetadata.getCorpusField(fieldKey);
        newBlock(fieldKey.getKeyString());
        if ((corpusField.isProp()) || (corpusField.isInfo())) {
            append("type", corpusField.getFicheItemType());
        }
        addLabels("label", corpusField.getLabels());
        for (String optionName : corpusField.getOptionNameSet()) {
            Object optionValue = corpusField.getOption(optionName);
            if (optionValue instanceof String) {
                append("field." + optionName, (String) optionValue);
            } else if (optionValue instanceof MultiStringable) {
                append("field." + optionName, ((MultiStringable) optionValue).toString(", "));
            }

        }
        addStatus(fieldUi);
        addUiOption(fieldUi);
        addAttributes(fieldUi.getAttributes());
        endLine();
    }

    private void specialIncludeBlock(SpecialIncludeUi includeUi) {
        newBlock(includeUi.getName());
        addStatus(includeUi);
        addCustomLabels(includeUi);
        addUiOption(includeUi);
        addAttributes(includeUi.getAttributes());
        endLine();
    }

    private void subsetIncludeBlock(SubsetIncludeUi includeUi) {
        ExtendedIncludeKey includeKey = includeUi.getExtendedIncludeKey();
        newBlock(includeKey.getKeyString());
        addStatus(includeUi);
        addCustomLabels(includeUi);
        addUiOption(includeUi);
        addAttributes(includeUi.getAttributes());
        endLine();
    }

    private void dataBlock(DataUi dataUi) {
        ExternalSourceDef externalSourceDef = dataUi.getExternalSourceDef();
        newBlock(dataUi.getName());
        append("type", externalSourceDef.getType());
        for (String paramName : externalSourceDef.getParamNameSet()) {
            append("param." + paramName, externalSourceDef.getParam(paramName));
        }
        addLabels("label", dataUi.getLabels());
        addUiOption(dataUi);
        addAttributes(dataUi.getAttributes());
        endLine();
    }

    private void commentBlock(CommentUi commentUi) {
        String name = commentUi.getName();
        newBlock(name);
        if (!alreadySet.contains(name)) {
            alreadySet.add(name);
            append("location", UiUtils.maskToString(commentUi.getLocation()));
            for (Lang lang : commentUi.getLangs()) {
                append("html", lang, commentUi.getHtmlByLang(lang));
            }
            addUiOption(commentUi);
        }
        endLine();
    }

    private void addLabels(String name, Labels labels) {
        if (labels != null) {
            for (Label label : labels) {
                append(name, label.getLang(), label.getLabelString());
            }
        }
    }

    private void addCustomLabels(IncludeUi includeUi) {
        Labels labels = includeUi.getCustomLabels();
        if (labels == null) {
            return;
        }
        addLabels("custom", labels);
    }

    private void addAttributes(Attributes attributes) {
        String attributesString = AttributeParser.toString(attributes, "= ", "; ");
        if (attributesString.length() > 0) {
            buf.append(attributesString);
            endLine();
        }
    }


    private void addStatus(UiComponent uiComponent) {
        if (uiComponent.isModifiableStatus()) {
            String status = uiComponent.getStatus();
            if (!status.equals(BdfServerConstants.DEFAULT_STATUS)) {
                append("status", status);
            }
        }
    }

    private void addUiOption(UiComponent uiComponent) {
        for (String optionName : uiComponent.getOptionNameSet()) {
            append("ui." + optionName, uiComponent.getOptionValue(optionName));
        }
    }

    private void append(String name, CharSequence value) {
        buf.append(name);
        buf.append("= ");
        escape(value);
        endLine();
    }

    private void append(String name, Lang lang, CharSequence value) {
        buf.append(name);
        buf.append('|');
        buf.append(lang.toString());
        buf.append("= ");
        escape(value);
        endLine();
    }

    private void append(String name, int value) {
        buf.append(name);
        buf.append("= ");
        buf.append(value);
        endLine();
    }

    public static String run(BdfServer bdfServer, Corpus corpus) {
        CorpusOverview corpusOverview = new CorpusOverview(bdfServer, corpus);
        return corpusOverview.run();
    }

    private void escape(CharSequence text) {
        int length = text.length();
        for (int i = 0; i < length; i++) {
            char carac = text.charAt(i);
            switch (carac) {
                case '\\':
                    buf.append("\\\\");
                    break;
                case '\n':
                    buf.append("¶");
                    break;
                case '\t':
                    buf.append("\\t");
                    break;
                case '\r':
                    buf.append("\\r");
                    break;
                case '¶':
                    buf.append("\\¶");
                    break;
                default:
                    buf.append(carac);
            }
        }
    }


}
