/* BdfServer_JsonProducers - Copyright (c) 2013-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.jsonproducers.main;

import fr.exemole.bdfserver.api.BdfServer;
import fr.exemole.bdfserver.api.users.BdfUser;
import java.io.IOException;
import java.text.DecimalFormatSymbols;
import java.util.Collection;
import java.util.List;
import net.fichotheque.Subset;
import net.fichotheque.SubsetItem;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.corpus.Fiches;
import net.fichotheque.exportation.table.ColDef;
import net.fichotheque.exportation.table.SubsetTable;
import net.fichotheque.exportation.table.SumCellConverter;
import net.fichotheque.exportation.table.SumResult;
import net.fichotheque.exportation.table.TableExportContext;
import net.fichotheque.thesaurus.Thesaurus;
import net.fichotheque.tools.exportation.table.TableExportEngine;
import net.fichotheque.utils.CorpusUtils;
import net.fichotheque.utils.FichothequeUtils;
import net.fichotheque.utils.TableDefUtils;
import net.fichotheque.utils.ThesaurusUtils;
import net.mapeadores.util.exceptions.NestedIOException;
import net.mapeadores.util.exceptions.SwitchException;
import net.mapeadores.util.json.JSONWriter;
import net.mapeadores.util.json.JsonProperty;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.money.Amount;
import net.mapeadores.util.primitives.Decimal;
import net.mapeadores.util.primitives.FuzzyDate;
import net.mapeadores.util.table.TableWriter;


/**
 *
 * @author Vincent Calame
 */
public class TableJsonProperty implements JsonProperty {

    private final BdfServer bdfServer;
    private final BdfUser bdfUser;
    private final SubsetTable subsetTable;
    private final TableExportContext tableExportContext;
    private final SumCellConverter sumCellConverter;

    public TableJsonProperty(BdfServer bdfServer, BdfUser bdfUser, SubsetTable subsetTable, TableExportContext tableExportContext, SumCellConverter cellConverter) {
        this.bdfServer = bdfServer;
        this.bdfUser = bdfUser;
        this.subsetTable = subsetTable;
        this.tableExportContext = tableExportContext;
        this.sumCellConverter = cellConverter;
    }

    @Override
    public String getName() {
        return "table";
    }

    @Override
    public void writeValue(JSONWriter jw) throws IOException {
        DecimalFormatSymbols symbols = new DecimalFormatSymbols(bdfUser.getFormatLocale());
        Subset subset = subsetTable.getSubset();
        Collection<SubsetItem> subsetItems = getSubsetItems(subset);
        Lang workingLang = bdfUser.getWorkingLang();
        List<ColDef> colDefList = subsetTable.getColDefList();
        int colCount = colDefList.size();
        boolean[] hiddenArray = new boolean[colCount];
        jw.object();
        {
            jw.key("coldef");
            jw.array();
            for (int j = 0; j < colCount; j++) {
                ColDef colDef = colDefList.get(j);
                if (colDef.isFormula()) {
                    hiddenArray[j] = true;
                } else {
                    hiddenArray[j] = false;
                    jw.object();
                    {
                        jw.key("name")
                                .value(colDef.getColName());
                        jw.key("title")
                                .value(TableDefUtils.getColTitle(colDef, workingLang, tableExportContext.getSourceLabelProvider(), subset));
                    }
                    jw.endObject();
                }
            }
            jw.endArray();
            jw.key("data");
            jw.array();
            JsonTableWriter jsonTableWriter = new JsonTableWriter(jw, symbols, hiddenArray);
            TableExportEngine.exportSubset(subsetTable, jsonTableWriter, sumCellConverter, subsetItems);
            SumResult sumResult = sumCellConverter.getSumResult(subsetTable);
            jw.endArray();
            if (sumResult.hasColumnSum()) {
                List<SumResult.Entry> entryList = sumResult.getEntryList();
                jw.key("sum");
                jw.array();
                for (int j = 0; j < colCount; j++) {
                    if (hiddenArray[j]) {
                        continue;
                    }
                    SumResult.ColumnSum columnSum = entryList.get(j).getColumnSum();
                    if (columnSum != null) {
                        if (columnSum instanceof SumResult.IntegerColumnSum) {
                            writeIntegerObject(jw, ((SumResult.IntegerColumnSum) columnSum).getResult());
                        } else if (columnSum instanceof SumResult.DecimalColumnSum) {
                            writeDecimalObject(jw, ((SumResult.DecimalColumnSum) columnSum).getResult());
                        } else if (columnSum instanceof SumResult.PercentageColumnSum) {
                            writePercentageObject(jw, ((SumResult.PercentageColumnSum) columnSum).getResult());
                        } else if (columnSum instanceof SumResult.MoneyColumnSum) {
                            jw.object();
                            {
                                SumResult.MoneyColumnSum moneyColumnSum = (SumResult.MoneyColumnSum) columnSum;
                                int resultCount = moneyColumnSum.getResultCount();
                                jw.key("moneyArray");
                                jw.array();
                                for (int k = 0; k < resultCount; k++) {
                                    Amount resultAmount = moneyColumnSum.getResult(k);
                                    writeAmountObject(jw, resultAmount, symbols);
                                }
                                jw.endArray();
                            }
                            jw.endObject();
                        } else {
                            jw.value(false);
                        }
                    } else {
                        jw.value(false);
                    }
                }
                jw.endArray();
            }
        }
        jw.endObject();
    }

    private Collection<SubsetItem> getSubsetItems(Subset subset) {
        if (subset instanceof Corpus) {
            Fiches fiches = bdfUser.getSelectedFiches();
            return CorpusUtils.getFicheMetaListByCorpus(fiches, subset.getSubsetKey());
        } else if (subset instanceof Thesaurus) {
            Thesaurus thesaurus = (Thesaurus) subset;
            if ((thesaurus == null) || (thesaurus.size() == 0)) {
                return FichothequeUtils.EMPTY_SUBSETITEMLIST;
            } else {
                return ThesaurusUtils.toSubsetItemList(thesaurus, -1);
            }
        }
        throw new SwitchException("Unknown subset: " + subset.getSubsetKeyString());
    }

    private static void writeAmountObject(JSONWriter jw, Amount amount, DecimalFormatSymbols symbols) throws IOException {
        jw.object();
        {
            jw.key("currency")
                    .value(amount.getCurrencyCode());
            jw.key("value")
                    .value(amount.toDecimal(true));
            jw.key("text")
                    .value(amount.toLitteralString(symbols, false));
        }
        jw.endObject();
    }

    private static void writeIntegerObject(JSONWriter jw, long value) throws IOException {
        jw.object();
        jw.key("integer")
                .value(value);
        jw.endObject();
    }

    private static void writeDecimalObject(JSONWriter jw, Decimal decimal) throws IOException {
        jw.object();
        jw.key("decimal")
                .value(decimal);
        jw.endObject();
    }

    private static void writePercentageObject(JSONWriter jw, Decimal decimal) throws IOException {
        jw.object();
        jw.key("decimal")
                .value(decimal);
        jw.endObject();
    }


    private static class JsonTableWriter implements TableWriter {

        private final JSONWriter jw;
        private final DecimalFormatSymbols symbols;
        private final boolean[] hiddenArray;
        private int rowNumber;
        private int columnNumber;

        private JsonTableWriter(JSONWriter jw, DecimalFormatSymbols symbols, boolean[] hiddenArray) {
            this.jw = jw;
            this.symbols = symbols;
            this.rowNumber = 0;
            this.hiddenArray = hiddenArray;
        }

        @Override
        public int startRow() {
            rowNumber++;
            try {
                jw.array();
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }
            columnNumber = 0;
            return rowNumber;
        }

        @Override
        public int addIntegerCell(Long lg) {
            columnNumber++;
            if (isCurrentHidden()) {
                return columnNumber;
            }
            try {
                if (lg == null) {
                    jw.value("");
                } else {
                    writeIntegerObject(jw, lg);
                }
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }
            return columnNumber;
        }

        @Override
        public int addDecimalCell(Decimal decimal) {
            columnNumber++;
            if (isCurrentHidden()) {
                return columnNumber;
            }
            try {
                if (decimal == null) {
                    jw.value("");
                } else {
                    writeDecimalObject(jw, decimal);
                }
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }
            return columnNumber;
        }

        @Override
        public int addStringCell(String s) {
            columnNumber++;
            if (isCurrentHidden()) {
                return columnNumber;
            }
            try {
                if (s == null) {
                    jw.value("");
                } else {
                    jw.value(s);
                }
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }
            return columnNumber;
        }

        @Override
        public int addDateCell(FuzzyDate date) {
            columnNumber++;
            if (isCurrentHidden()) {
                return columnNumber;
            }
            try {
                if (date == null) {
                    jw.value("");
                } else {
                    jw.value(date.toISOString());
                }
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }
            return columnNumber;
        }

        @Override
        public int addMoneyCell(Amount amount) {
            columnNumber++;
            if (isCurrentHidden()) {
                return columnNumber;
            }
            try {
                if (amount == null) {
                    jw.value("");
                } else {
                    writeAmountObject(jw, amount, symbols);
                }
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }
            return columnNumber;
        }

        @Override
        public int addPercentageCell(Decimal decimal) {
            columnNumber++;
            if (isCurrentHidden()) {
                return columnNumber;
            }
            try {
                if (decimal == null) {
                    jw.value("");
                } else {
                    writePercentageObject(jw, decimal);
                }
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }
            return columnNumber;
        }

        @Override
        public int endRow() {
            try {
                jw.endArray();
            } catch (IOException ioe) {
                throw new NestedIOException(ioe);
            }
            return rowNumber;
        }

        private boolean isCurrentHidden() {
            return hiddenArray[columnNumber - 1];
        }

    }

}
