/* FichothequeLib_API - Copyright (c) 2010-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.utils;

import java.io.IOException;
import java.util.AbstractList;
import java.util.List;
import java.util.RandomAccess;
import net.fichotheque.Subset;
import net.fichotheque.exportation.table.ColDef;
import net.fichotheque.exportation.table.FormatColDef;
import net.fichotheque.exportation.table.TableDef;
import net.fichotheque.exportation.table.TableDefItem;
import net.fichotheque.exportation.table.TableInclusionDef;
import net.fichotheque.exportation.table.TableParameterDef;
import net.fichotheque.format.FichothequeFormatDef;
import net.fichotheque.format.SourceLabelProvider;
import net.mapeadores.util.annotation.Nullable;
import net.mapeadores.util.format.FormatConstants;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.localisation.Message;
import net.mapeadores.util.logging.LineMessageHandler;
import net.mapeadores.util.logging.MessageHandler;
import net.mapeadores.util.text.Label;
import net.mapeadores.util.text.Labels;
import net.mapeadores.util.text.StringUtils;


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

    private TableDefUtils() {
    }

    public static Labels getLabels(ColDef colDef, SourceLabelProvider sourceLabelProvider, Subset subset) {
        Labels labels = colDef.getCustomLabels();
        if (labels != null) {
            return labels;
        }
        if (sourceLabelProvider == null) {
            return null;
        }
        if (colDef instanceof FormatColDef) {
            return sourceLabelProvider.getLabels(subset, ((FormatColDef) colDef).getFichothequeFormatDef().getFormatSourceKeyList().get(0));
        } else {
            return null;
        }
    }

    public static String getColTitle(ColDef colDef, Lang lang, SourceLabelProvider sourceLabelProvider, Subset subset) {
        Labels labels = getLabels(colDef, sourceLabelProvider, subset);
        if (labels != null) {
            return labels.seekLabelString(lang, colDef.getColName());
        } else {
            return colDef.getColName();
        }
    }

    public static void writeTableDef(Appendable appendable, TableDef tableDef) throws IOException {
        boolean next = false;
        for (TableDefItem defItem : tableDef.getDefItemList()) {
            if (next) {
                appendable.append("\n");
            } else {
                next = true;
            }
            if (defItem instanceof FormatColDef) {
                FormatColDef formatColDef = (FormatColDef) defItem;
                writeNameLine(appendable, formatColDef.getColName(), formatColDef.getCustomLabels());
                FichothequeFormatDef ficheFormatDef = formatColDef.getFichothequeFormatDef();
                FichothequeFormatUtils.appendLines(ficheFormatDef, appendable);
            } else if (defItem instanceof TableInclusionDef) {
                TableInclusionDef tableInclusionDef = (TableInclusionDef) defItem;
                appendable.append("%");
                writeNameLine(appendable, getCompleteName(tableInclusionDef), tableInclusionDef.getLabels());
                for (String configLine : tableInclusionDef.getConfigLineList()) {
                    appendable.append(configLine);
                    appendable.append('\n');
                }
            } else if (defItem instanceof TableParameterDef) {
                TableParameterDef tableParameterDef = (TableParameterDef) defItem;
                String name = tableParameterDef.getParameterName();
                String value = tableParameterDef.getParameterValue();
                if (value.indexOf('\n') != -1) {
                    appendable.append("!!start_");
                    appendable.append(name);
                    appendable.append('\n');
                    appendable.append(value);
                    if (value.charAt(value.length() - 1) != '\n') {
                        appendable.append('\n');
                    }
                    appendable.append("!!end_");
                    appendable.append(name);
                    appendable.append('\n');
                } else {
                    appendable.append('!');
                    appendable.append(name);
                    appendable.append('=');
                    appendable.append(tableParameterDef.getParameterValue());
                    appendable.append('\n');
                }
            }
        }
    }

    private static void writeNameLine(Appendable appendable, String colName, Labels customLabels) throws IOException {
        appendable.append(colName);
        if (customLabels != null) {
            appendable.append(" > ");
            boolean next = false;
            for (Label label : customLabels) {
                if (next) {
                    appendable.append(',');
                } else {
                    next = true;
                }
                appendable.append(label.getLang().toString());
                appendable.append("=\"");
                StringUtils.escapeDoubleQuote(label.getLabelString(), appendable);
                appendable.append("\"");
            }
        }
        appendable.append('\n');
    }

    public static MessageHandler toFormatDefMessageHandler(LineMessageHandler lineMessageHandler, int coldDefStartLineNumber) {
        return new FormatDefMessageHandler(lineMessageHandler, coldDefStartLineNumber);
    }

    public static String getCompleteName(TableInclusionDef tableInclusionDef) {
        String nameSpace = tableInclusionDef.getNameSpace();
        String localName = tableInclusionDef.getLocalName();
        if (localName.length() == 0) {
            return nameSpace;
        } else {
            return nameSpace + ":" + localName;
        }
    }


    private static class FormatDefMessageHandler implements MessageHandler {

        private final LineMessageHandler lineMessageHandler;
        private final int coldDefStartLineNumber;

        private FormatDefMessageHandler(LineMessageHandler lineMessageHandler, int coldDefStartLineNumber) {
            this.lineMessageHandler = lineMessageHandler;
            this.coldDefStartLineNumber = coldDefStartLineNumber;
        }

        @Override
        public void addMessage(String category, Message message) {
            int lineDiff = getLineDiff(category);
            int lineNumber = coldDefStartLineNumber + lineDiff;
            lineMessageHandler.addMessage(category, lineNumber, message);
        }

        private int getLineDiff(String formatErrorCategory) {
            switch (formatErrorCategory) {
                case FormatConstants.SEVERE_INSTRUCTION:
                case FormatConstants.WARNING_INSTRUCTION:
                    return 3;
                case FormatConstants.SEVERE_PATTERN:
                    return 2;
                case FormatConstants.SEVERE_SOURCE:
                    return 1;
                default:
                    return 0;
            }
        }

    }

    public static ColDef toColDef(String colName, @Nullable Labels customLabels) {
        return new SimpleColDef(colName, customLabels);
    }

    public static FormatColDef toFormatColDef(String colName, FichothequeFormatDef ficheFormatDef) {
        return toFormatColDef(colName, ficheFormatDef, null);
    }

    public static FormatColDef toFormatColDef(String colName, FichothequeFormatDef ficheFormatDef, @Nullable Labels customLabels) {
        return new InternalFormatColDef(colName, ficheFormatDef, customLabels);
    }

    public static TableInclusionDef newTableInclusionDef(String completeName, Labels customLabels, String[] lines) {
        String nameSpace = completeName;
        String localName = "";
        int idx = completeName.indexOf(":");
        if (idx != -1) {
            nameSpace = completeName.substring(0, idx);
            localName = completeName.substring(idx + 1);
        }
        return new InternalTableInclusionDef(nameSpace, localName, customLabels, StringUtils.wrap(lines));
    }

    public static TableDef toTableDef(List<TableDefItem> defItemList) {
        return new InternalTableDef(wrap(defItemList.toArray(new TableDefItem[defItemList.size()])));
    }

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

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


    private static class SimpleColDef implements ColDef {

        private final String colName;
        private final Labels customLabels;

        private SimpleColDef(String colName, Labels customLabels) {
            this.colName = colName;
            this.customLabels = customLabels;
        }

        @Override
        public String getColName() {
            return colName;
        }

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

        @Override
        public Object getParameterValue(String paramKey) {
            return null;
        }

    }


    private static class InternalFormatColDef implements FormatColDef {

        private final String colName;
        private final FichothequeFormatDef ficheFormatDef;
        private final Labels customLabels;

        private InternalFormatColDef(String colName, FichothequeFormatDef ficheFormatDef, Labels customLabels) {
            this.colName = colName;
            this.ficheFormatDef = ficheFormatDef;
            this.customLabels = customLabels;
        }

        @Override
        public String getColName() {
            return colName;
        }

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

        @Override
        public FichothequeFormatDef getFichothequeFormatDef() {
            return ficheFormatDef;
        }

    }


    private static class InternalTableInclusionDef implements TableInclusionDef {

        private final String nameSpace;
        private final String localName;
        private final Labels labels;
        private final List<String> configLineList;

        private InternalTableInclusionDef(String nameSpace, String localName, Labels labels, List<String> configLineList) {
            this.nameSpace = nameSpace;
            this.localName = localName;
            this.labels = labels;
            this.configLineList = configLineList;
        }

        @Override
        public String getNameSpace() {
            return nameSpace;
        }

        @Override
        public String getLocalName() {
            return localName;
        }

        @Override
        public Labels getLabels() {
            return labels;
        }

        @Override
        public List<String> getConfigLineList() {
            return configLineList;
        }

    }


    private static class InternalTableDef implements TableDef {

        private final List<TableDefItem> defItemList;

        private InternalTableDef(List<TableDefItem> defItemList) {
            this.defItemList = defItemList;
        }

        @Override
        public List<TableDefItem> getDefItemList() {
            return defItemList;
        }


    }


    private static class TableDefItemList extends AbstractList<TableDefItem> implements RandomAccess {

        private final TableDefItem[] array;

        private TableDefItemList(TableDefItem[] array) {
            this.array = array;
        }

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

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

    }


    private static class ColDefList extends AbstractList<ColDef> implements RandomAccess {

        private final ColDef[] array;

        private ColDefList(ColDef[] colDefArray) {
            this.array = colDefArray;
        }

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

        @Override
        public ColDef get(int i) {
            return array[i];
        }

    }

}
