/* FichothequeLib_API - Copyright (c) 2019-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.util.AbstractList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.RandomAccess;
import net.fichotheque.FichothequeConstants;
import net.fichotheque.SubsetItem;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.FicheMeta;
import net.fichotheque.exportation.table.Cell;
import net.fichotheque.exportation.table.CellConverter;
import net.fichotheque.exportation.table.CellConverterProvider;
import net.fichotheque.exportation.table.Col;
import net.fichotheque.exportation.table.ColDef;
import net.fichotheque.exportation.table.JunctionTable;
import net.fichotheque.exportation.table.SubsetTable;
import net.fichotheque.exportation.table.SumCellConverter;
import net.fichotheque.exportation.table.SumResult;
import net.fichotheque.exportation.table.TableExportContentDescription;
import net.fichotheque.exportation.table.TableExportDescription;
import net.fichotheque.format.formatters.SourceFormatter;
import net.fichotheque.thesaurus.Motcle;
import net.mapeadores.util.annotation.Nullable;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.localisation.LangContext;
import net.mapeadores.util.localisation.UserLangContext;


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

    public final static List<TableExportContentDescription> EMPTY_CONTENTDESCRIPTIONLIST = Collections.emptyList();
    public final static List<JunctionTable> EMPTY_JUNCTIONTABLE_LIST = Collections.emptyList();
    public final static SumResult EMPTY_SUMRESULT = new EmptySumResult();
    public final static CellConverter EMPTY_CELLCONVERTER = new EmptyCellConverter();
    public final static SumCellConverter EMPTY_SUMCELLCONVERTER = new EmptyCellConverter();
    public final static Cell[] EMPTY_CELLARRAY = new Cell[0];
    public final static CellConverterProvider EMPTY_CELLCONVERTERPROVIDER = new EmptyCellConverterProvider();
    public final static List<ColDef> EMPTY_COLDEFLIST = Collections.emptyList();
    public final static Col[] EMPTY_COLARRAY = new Col[0];

    private TableExportUtils() {

    }

    public static TableExportContentDescription getTableExportContentDescription(TableExportDescription tableExportDescription, String contentPath) {
        for (TableExportContentDescription description : tableExportDescription.getTableExportContentDescriptionList()) {
            if (description.getPath().equals(contentPath)) {
                return description;
            }
        }
        return null;
    }

    public static boolean isContentEditable(TableExportDescription tableExportDescription, String contentPath) {
        if (!tableExportDescription.isEditable()) {
            return false;
        }
        for (TableExportContentDescription tecd : tableExportDescription.getTableExportContentDescriptionList()) {
            if (tecd.getPath().equals(contentPath)) {
                return tecd.isEditable();
            }
        }
        return true;
    }

    public static Cell toCell(String formatCast, Object value, ColDef colDef, LangContext langContext) {
        return new InternalCell(formatCast, value, colDef, langContext);
    }

    public static Col toCol(ColDef colDef, SourceFormatter sourceFormatter) {
        return new InternalCol(colDef, sourceFormatter);
    }

    public static SumResult.Entry toSumResultEntry(ColDef colDef, @Nullable SumResult.ColumnSum columnSum) {
        return new InternalSumResultEntry(colDef, columnSum);
    }

    public static SumResult toSumResult(ColDef[] colDefArray, SumResult.ColumnSum[] colSumArray) {
        boolean hasColumnSum;
        int length = colDefArray.length;
        SumResult.Entry[] array = new SumResult.Entry[length];
        if (colSumArray == null) {
            hasColumnSum = false;
            for (int i = 0; i < length; i++) {
                array[i] = toSumResultEntry(colDefArray[i], null);
            }
        } else {
            hasColumnSum = true;
            for (int i = 0; i < length; i++) {
                array[i] = toSumResultEntry(colDefArray[i], colSumArray[i]);
            }
        }
        return new InternalSumResult(wrap(array), hasColumnSum);
    }

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

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


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

    public static List<SumResult.Entry> wrap(SumResult.Entry[] array) {
        return new SumResultEntryList(array);
    }

    public static CellConverter getPhraseConverter(UserLangContext userLangContext) {
        return new PhraseCellConverter(userLangContext);
    }

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

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


    private static class PhraseCellConverter implements CellConverter {

        private final UserLangContext userLangContext;
        private final Lang lang;
        private final Locale formatLocale;
        private final ColDef numberColDef;


        private PhraseCellConverter(UserLangContext userLangContext) {
            this.userLangContext = userLangContext;
            this.lang = userLangContext.getWorkingLang();
            this.formatLocale = userLangContext.getFormatLocale();
            this.numberColDef = TableDefUtils.toColDef("number", null);
        }

        @Override
        public Cell[] toCellArray(SubsetItem subsetItem) {
            if (subsetItem instanceof FicheMeta) {
                FicheMeta ficheMeta = (FicheMeta) subsetItem;
                String numberPhrase = FichothequeUtils.getNumberPhrase(ficheMeta, FichothequeConstants.FICHE_PHRASE, lang, formatLocale);
                Cell[] result = new Cell[1];
                result[0] = toCell("", numberPhrase, numberColDef, userLangContext);
                return result;
            } else if (subsetItem instanceof Motcle) {
                Motcle motcle = (Motcle) subsetItem;
                String numberPhrase = FichothequeUtils.getNumberPhrase(motcle, FichothequeConstants.FICHESTYLE_PHRASE, lang, formatLocale, "");
                if (numberPhrase.isEmpty()) {
                    return EMPTY_CELLARRAY;
                } else {
                    Cell[] result = new Cell[1];
                    result[0] = toCell("", numberPhrase, numberColDef, userLangContext);
                    return result;
                }
            } else {
                return null;
            }
        }

    }


    private static class TableExportDescriptionList extends AbstractList<TableExportDescription> implements RandomAccess {

        private final TableExportDescription[] array;

        private TableExportDescriptionList(TableExportDescription[] array) {
            this.array = array;
        }

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

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

    }


    private static class TableExportContentDescriptionList extends AbstractList<TableExportContentDescription> implements RandomAccess {

        private final TableExportContentDescription[] array;

        private TableExportContentDescriptionList(TableExportContentDescription[] array) {
            this.array = array;
        }

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

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

    }


    private static class InternalCell implements Cell {

        private final String formatCast;
        private final Object value;
        private final ColDef colDef;
        private final LangContext langContext;

        private InternalCell(String formatCast, Object value, ColDef colDef, LangContext langContext) {
            this.formatCast = formatCast;
            this.value = value;
            this.colDef = colDef;
            this.langContext = langContext;
        }

        @Override
        public String getFormatCast() {
            return formatCast;
        }

        @Override
        public Object getValue() {
            return value;
        }

        @Override
        public ColDef getColDef() {
            return colDef;
        }

        @Override
        public LangContext getLangContext() {
            return langContext;
        }

    }


    private static class EmptyCellConverter implements SumCellConverter {

        private EmptyCellConverter() {

        }

        @Override
        public Cell[] toCellArray(SubsetItem subsetItem) {
            return null;
        }

        @Override
        public SumResult getSumResult(SubsetKey subsetKey) {
            return EMPTY_SUMRESULT;
        }

    }


    private static class EmptyCellConverterProvider implements CellConverterProvider {

        private EmptyCellConverterProvider() {

        }

        @Override
        public SumCellConverter getCellConverter(String name) {
            return null;
        }

    }


    private static class EmptySumResult implements SumResult {

        private final List<SumResult.Entry> emptyList = Collections.emptyList();

        private EmptySumResult() {

        }

        @Override
        public boolean hasColumnSum() {
            return false;
        }

        @Override
        public List<Entry> getEntryList() {
            return emptyList;
        }

    }


    private static class InternalSumResult implements SumResult {

        private final List<SumResult.Entry> entryList;
        private final boolean hasColumnSum;

        private InternalSumResult(List<SumResult.Entry> entryList, boolean hasColumnSum) {
            this.entryList = entryList;
            this.hasColumnSum = hasColumnSum;
        }

        @Override
        public boolean hasColumnSum() {
            return hasColumnSum;
        }

        @Override
        public List<Entry> getEntryList() {
            return entryList;
        }

    }


    private static class InternalCol implements Col {

        private final ColDef colDef;
        private final SourceFormatter sourceFormatter;

        private InternalCol(ColDef colDef, SourceFormatter sourceFormatter) {
            this.colDef = colDef;
            this.sourceFormatter = sourceFormatter;
        }

        @Override
        public ColDef getColDef() {
            return colDef;
        }

        @Override
        public SourceFormatter getSourceFormatter() {
            return sourceFormatter;
        }

    }


    private static class ColList extends AbstractList<Col> implements RandomAccess {

        private final Col[] array;

        private ColList(Col[] colDefArray) {
            this.array = colDefArray;
        }

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

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

    }


    private static class InternalSumResultEntry implements SumResult.Entry {

        private final ColDef colDef;
        private final SumResult.ColumnSum columnSum;

        private InternalSumResultEntry(ColDef colDef, SumResult.ColumnSum columnSum) {
            this.colDef = colDef;
            this.columnSum = columnSum;
        }

        @Override
        public ColDef getColDef() {
            return colDef;
        }

        @Override
        public SumResult.ColumnSum getColumnSum() {
            return columnSum;
        }

    }


    private static class SumResultEntryList extends AbstractList<SumResult.Entry> implements RandomAccess {

        private final SumResult.Entry[] array;

        private SumResultEntryList(SumResult.Entry[] colDefArray) {
            this.array = colDefArray;
        }

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

        @Override
        public SumResult.Entry get(int i) {
            return array[i];
        }

    }


    private static class SubsetTableList extends AbstractList<SubsetTable> implements RandomAccess {

        private final SubsetTable[] array;

        private SubsetTableList(SubsetTable[] array) {
            this.array = array;
        }

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

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

    }


    private static class JunctionTableList extends AbstractList<JunctionTable> implements RandomAccess {

        private final JunctionTable[] array;

        private JunctionTableList(JunctionTable[] array) {
            this.array = array;
        }

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

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

    }

}
